By KP | TZoneLabs | DevOps & Cloud Engineering
The fastest way to grant a pod too much power in Kubernetes is kubectl create clusterrolebinding temp --clusterrole=cluster-admin --serviceaccount=default:default. It works immediately, which is exactly the problem. Nobody circles back to fix it, and six months later a compromised pod can read every secret in the cluster.
We want to walk through how to scope RBAC so a workload gets exactly the permissions it needs, no more, and how to verify that before it ships.
Why RBAC Exists, In One Sentence
Every request that hits the Kubernetes API, whether it’s kubectl get pods, a controller listing ConfigMaps, or a CI pipeline deploying a Deployment, gets checked against RBAC first. If nothing explicitly allows it, it’s denied. RBAC is a pure allow-list; there’s no deny rule to write, only permissions to grant or withhold.
The Four Objects That Make Up RBAC
- Role — a set of permissions scoped to one namespace (e.g., “read ConfigMaps in
payments“). - ClusterRole — the same idea, but not tied to a namespace, either for cluster-scoped resources (nodes, PVs) or to be reused across many namespaces.
- RoleBinding — grants a Role (or a ClusterRole) to a user, group, or ServiceAccount, scoped to one namespace.
- ClusterRoleBinding — grants a ClusterRole cluster-wide, no namespace boundary.
The mistake we see most often: reaching for a ClusterRoleBinding because it’s one command, when a Role plus a RoleBinding in a single namespace would have covered it.
Start With What the Workload Actually Touches
Say we have a controller that watches Pods and reads ConfigMaps in its own namespace. Nothing else. The Role should reflect exactly that, not “just give it edit”:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: payments
name: pod-watcher
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list"]
No create, no delete, no secrets. If the controller never writes, the Role never grants write.
Bind It to a Dedicated ServiceAccount, Skip default
Every pod that doesn’t specify a serviceAccountName runs as the namespace’s default ServiceAccount. If anything ever binds a permission to default, even temporarily, even for debugging, every unlabeled pod in that namespace inherits it. Always create a named ServiceAccount per workload:
apiVersion: v1
kind: ServiceAccount
metadata:
name: pod-watcher-sa
namespace: payments
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-watcher-binding
namespace: payments
subjects:
- kind: ServiceAccount
name: pod-watcher-sa
namespace: payments
roleRef:
kind: Role
name: pod-watcher
apiGroup: rbac.authorization.k8s.io
Then reference it explicitly in the pod spec: serviceAccountName: pod-watcher-sa.
When You Actually Need Cluster Scope
Cluster scope is correct for things like a monitoring agent that needs to list nodes, or a cert-manager-style controller managing CRDs across every namespace. In that case, be explicit about the ClusterRole’s contents rather than reusing view/edit/admin (Kubernetes’ built-in aggregated roles), which are broader than most single controllers need:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: node-reader
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
Test Permissions Before You Ship, Not After an Incident
kubectl auth can-i impersonates a ServiceAccount and tells you exactly what it can and can’t do. No need to trigger the real workload to find out:
kubectl auth can-i delete secrets \
--as=system:serviceaccount:payments:pod-watcher-sa \
-n payments
# no
kubectl auth can-i --list \
--as=system:serviceaccount:payments:pod-watcher-sa \
-n payments
We run the --list version in CI against every changed Role/RoleBinding pair. A diff in the permission list is a required reviewer callout, the same way we’d flag a diff in a security group.
A Quick Audit for Existing Clusters
If RBAC in your cluster grew organically, this finds every binding to cluster-admin, almost always worth a second look:
kubectl get clusterrolebindings -o json \
| jq -r '.items[] | select(.roleRef.name=="cluster-admin") | .metadata.name'
And this finds every RoleBinding still pointed at a namespace’s default ServiceAccount:
kubectl get rolebindings --all-namespaces -o json \
| jq -r '.items[] | select(.subjects[]?.name=="default") | "\(.metadata.namespace)/\(.metadata.name)"'
Both commands tend to turn up more than expected the first time they’re run.
Key Lessons
-
RBAC is allow-only.
There’s no explicit deny rule. If nothing grants a permission, it’s already denied, so scope by only granting what’s actually used. -
defaultServiceAccount bindings are the most common accidental over-grant.
Every unlabeled pod in the namespace inherits them. -
ClusterRoleBinding should be rare.
Most workloads only ever touch resources in their own namespace. -
Run
kubectl auth can-iin CI, not just during incident response.
Verifying scope before merge is cheaper than discovering it during a breach postmortem. -
Audit existing clusters periodically.
RBAC sprawl happens gradually, and a scheduledcluster-adminbinding audit catches what code review missed.
Summary
| Object | Scope | Use When |
|---|---|---|
Role |
Single namespace | Workload only touches resources in its own namespace |
ClusterRole |
Cluster-wide (or reusable) | Cluster-scoped resources (nodes, PVs) or shared across namespaces |
RoleBinding |
Single namespace | Grants a Role, or a ClusterRole, within one namespace |
ClusterRoleBinding |
Cluster-wide | Grants a ClusterRole with no namespace boundary, used sparingly |
Most RBAC incidents don’t come from missing permissions. They come from convenience: cluster-admin because it was faster, default service accounts because nobody created a new one. Scope it right the first time and there’s nothing for the audit to find later.
Read Next
- I Stopped SSH-ing Into Kubernetes Nodes. Here’s What I Use Instead.
- Kubernetes Silently Evicted 60% of Our Production Pods — Here Is Why and How to Fix It
If you’re running Kubernetes in production, follow along on LinkedIn for more guides like this one as they’re published.
Tags:
#Kubernetes #RBAC #DevOps #Security
#K8sSecurity #CloudNative #AWS #EKS