What RBAC Is — Who, What, and Where
Kubernetes RBAC (Role-Based Access Control) is a permission model that defines which subject can perform which verb on which resource. When a cluster is shared by multiple people, teams, and CI pipelines, it is the core mechanism that enforces least privilege.
RBAC works through a combination of four resources.
| Resource | Scope | Role |
|---|---|---|
Role | Namespace | A bundle of permissions (policy) |
ClusterRole | Cluster-wide | A bundle of permissions (global / nodes / PVs, etc.) |
RoleBinding | Namespace | Connects a subject ↔ Role |
ClusterRoleBinding | Cluster-wide | Connects a subject ↔ ClusterRole |
The key rule: Role/ClusterRole only define "what can be done". Binding decides who actually receives those permissions.
RBAC is purely additive — there is addition, but no subtraction. There are no deny rules that say "this is forbidden"; only what is explicitly allowed is possible. Permissions from multiple bindings accumulate as a union.
Three Kinds of Subject
Bindings grant permissions to three kinds of target.
- User: A human user (external auth such as certificates or OIDC. Kubernetes has no User object)
- Group: A user group
- ServiceAccount: The identity of a workload running inside a Pod. Used when CI deployments, operators, or apps call the API.
Creating a Role — Namespace Permissions
This Role can only list Pods and read logs in the dev namespace.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: dev
name: pod-reader
rules:
- apiGroups: [""] # "" = core API
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]Choose verbs from get list watch create update patch delete deletecollection. You can also specify subresources such as pods/log and pods/exec separately in resources.
RoleBinding — Connecting to a Subject
Grant the Role above to a specific ServiceAccount.
apiVersion: v1
kind: ServiceAccount
metadata:
name: ci-deployer
namespace: dev
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: ci-can-read-pods
namespace: dev
subjects:
- kind: ServiceAccount
name: ci-deployer
namespace: dev
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.iokubectl apply -f rbac.yamlroleRef cannot be modified once created. To change the Role it references, you must delete the RoleBinding and recreate it.
ClusterRole and Reuse
Use a ClusterRole for cluster-wide resources (nodes, PersistentVolumes, namespaces, and so on), or when you want common permissions that can be reused across multiple namespaces.
The key pattern: binding a ClusterRole with a RoleBinding applies that ClusterRole's permissions only in that namespace.
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: deploy-in-dev
namespace: dev
subjects:
- kind: ServiceAccount
name: ci-deployer
namespace: dev
roleRef:
kind: ClusterRole # ...
name: edit #
apiGroup: rbac.authorization.k8s.ioview / edit / admin / cluster-admin are ClusterRoles that Kubernetes provides by default. Most real-world needs are solved by binding these to a specific namespace with a RoleBinding.
Attaching a ServiceAccount to a Pod
For a workload to call the API server, specify an SA on the Pod. The token is mounted automatically.
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployer
namespace: dev
spec:
template:
spec:
serviceAccountName: ci-deployer
# API call Pod :
automountServiceAccountToken: false
containers:
- name: app
image: myapp:1.0For ordinary app Pods that do not call the API, turning off token mounting with automountServiceAccountToken: false is safer. Even if the container is compromised, an API access token is not exposed.
Checking Permissions — kubectl auth can-i
This is the core command for RBAC debugging. Ask directly whether a given subject can perform a given action.
# dev pod ?
kubectl auth can-i create pods -n dev
# ServiceAccount check (--as)
kubectl auth can-i list pods -n dev \
--as=system:serviceaccount:dev:ci-deployer
# SA
kubectl auth can-i --list -n dev \
--as=system:serviceaccount:dev:ci-deployerThe ServiceAccount username format is always system:serviceaccount:<>:<>.
Least-Privilege Example — CI Deployment SA
Giving cluster-admin when CI only needs to manage Deployments in dev is a common mistake. Define only the permissions that are actually needed.```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: dev
name: deployment-manager
rules:
- apiGroups: ["apps"] resources: ["deployments", "replicasets"] verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: [""] resources: ["pods", "services"] verbs: ["get", "list", "watch"]
- Omitting delete permission (`delete`) prevents accidents where CI accidentally deletes resources.
- There is no access across namespaces, so `prod` is unaffected.
---
## Common Problems
- **`Forbidden` errors**: The message lists the required apiGroup/resource/verb as-is. Add those to the Role's `rules`.
- **Mixing up apiGroup**: Pod, Service, and ConfigMap are core (`""`); Deployment is `apps`; Ingress is `networking.k8s.io`. Check with `kubectl api-resources`.
- **Bound but still no permission**: Usually a typo in the `roleRef` name or a ServiceAccount namespace mismatch. Check the permissions actually granted with `kubectl auth can-i --list --as=...`.
- **Overly broad ClusterRoleBinding**: Binding `cluster-admin` to an SA with a ClusterRoleBinding means full cluster takeover. Reconsider whether you really need it.
RBAC looks complicated at first because there are four resources, but it boils down to one sentence: **"a permission bundle (Role) + a connection (Binding)"**. Keep `kubectl auth can-i` nearby and ask it as you go — tightening down to least privilege gets much easier.AI tools may assist drafting. Check the evidence in each guide for whether commands were run and which environment was used. Confirm the current docs for your version before applying instructions, and email us about errors. email us
Questions & answers
Ask a question about this guide. We'll review and reply.