Role-Based Access Control, usually called RBAC, is one of the most important security mechanisms in Kubernetes. It allows you to define who can do what inside a cluster..
In this lab, we will build a practical RBAC scenario with two users:
Bob, who should only be able to read pods in thedevnamespace.Alice, who belongs to theadmingroup and should be able to manage pods across namespaces.
We will start with a namespace-scoped Role and RoleBinding, then move to a cluster-wide ClusterRole and ClusterRoleBinding. Finally, we will look at an important RBAC detail: Kubernetes subresources such as pods/log, pods/exec, and pods/attach.
What we are going to build
The scenario is simple but very useful:
| User or group | Scope | Allowed actions | Not allowed |
|---|---|---|---|
Bob | dev namespace only | get, list pods | Create, update, delete pods; access pods in prod; list pods across all namespaces |
admin group | Cluster-wide | Manage pods | Initially cannot read logs or exec into pods until subresources are added |
Alice | Member of admin group | Inherits permissions from the group | Cannot modify RBAC unless explicitly allowed or using an admin context |
This setup demonstrates the difference between authentication and authorization. Alice and Bob may both be authenticated users, but Kubernetes still checks whether they are authorized to perform each requested action.
RBAC objects in Kubernetes
Kubernetes RBAC is built around four main API objects:
| Object | Scope | Purpose |
|---|---|---|
Role | Namespace | Defines permissions inside one namespace |
ClusterRole | Cluster-wide | Defines permissions across namespaces or for cluster-scoped resources |
RoleBinding | Namespace | Connects a Role or ClusterRole to users, groups, or service accounts inside one namespace |
ClusterRoleBinding | Cluster-wide | Connects a ClusterRole to users, groups, or service accounts across the whole cluster |
A very important rule: RBAC permissions are additive. Kubernetes RBAC does not have explicit deny rules. If a user receives permission through any matching binding, that permission is granted.
Step 1: Create the namespaces
First, create two namespaces: dev and prod.
Create a file called namespaces.yaml:
apiVersion: v1
kind: Namespace
metadata:
name: dev
---
apiVersion: v1
kind: Namespace
metadata:
name: prod
Apply the file:
kubectl apply -f namespaces.yaml
If you try to run this command while using a limited user context such as alice or bob, it may fail. Creating namespaces is a cluster-scoped operation. A normal namespace-level user will usually not have this permission.
Use an administrative context, for example the default Minikube context:
kubectl config use-context minikube
kubectl apply -f namespaces.yaml
Step 2: Create test pods in both namespaces
Now create one nginx pod in dev and another one in prod.
Create pods.yaml:
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.27.0
ports:
- containerPort: 80
---
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: prod
spec:
containers:
- name: nginx
image: nginx:1.27.0
ports:
- containerPort: 80
Apply it:
kubectl apply -f pods.yaml
Check the pods:
kubectl get pods --all-namespaces | grep nginx
You should see one pod in dev and one pod in prod.
Step 3: Create a read-only Role for Bob
Now we want Bob to read pods in the dev namespace only.
Create a file called dev-pod-reader-role.yaml:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: dev
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
A few important details:
apiGroups: [""]means the core Kubernetes API group.- Pods belong to the core API group.
getallows reading a specific pod.listallows listing multiple pods.- This
Roleapplies only in thedevnamespace.
Apply the role:
kubectl apply -f dev-pod-reader-role.yaml
Check it:
kubectl get role pod-reader -n dev
kubectl describe role pod-reader -n dev
Step 4: Bind the Role to Bob
A Role only defines permissions. It does not grant them to anyone until it is connected to a subject with a RoleBinding.
Create dev-pod-reader-rolebinding.yaml:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-reader
namespace: dev
subjects:
- kind: User
name: Bob
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
The namespace of the RoleBinding matters. Here it is created in dev, so it grants the referenced role only in the dev namespace.
Apply it:
kubectl apply -f dev-pod-reader-rolebinding.yaml
Check it:
kubectl get rolebinding -n dev
kubectl describe rolebinding pod-reader -n dev
Step 5: Test Bob’s permissions
Switch to Bob’s context:
kubectl config use-context Bob
kubectl config current-context
Try to list pods in the default namespace:
kubectl get pods
Expected result: forbidden.
Bob has no permissions in the default namespace.
Now try the dev namespace:
kubectl get pods -n dev
Expected result: allowed.
Bob can also describe a pod:
kubectl describe pod nginx -n dev
This works because kubectl describe pod needs permission to get the pod.
Now try the prod namespace:
kubectl get pods -n prod
Expected result: forbidden.
Bob has permissions only in dev.
Now try all namespaces:
kubectl get pods --all-namespaces
Expected result: forbidden.
This is an important behavior. Kubernetes does not return only the objects Bob is allowed to see. Listing across all namespaces requires permission at the requested scope.
Step 6: Confirm Bob cannot create or delete pods
Bob only has get and list. He cannot create pods:
kubectl run busybox --image=busybox -n dev -- sleep 3600
Expected result: forbidden.
He also cannot delete pods:
kubectl delete pod nginx -n dev
Expected result: forbidden.
This is a clean example of least privilege: Bob can inspect pods in dev, but he cannot modify workloads.
Step 7: Create a ClusterRole for pod admins
Now let us create a different permission model for Alice.
Alice belongs to the admin group. We want everyone in this group to manage pods across namespaces.
Create pod-admin-clusterrole.yaml:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pod-admin
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["*"]
A ClusterRole is not namespaced, so there is no metadata.namespace.
The wildcard verb * means all verbs for the listed resources. In this case, members can create, get, list, watch, update, patch, and delete pods.
For a learning lab this is fine, but in production you should avoid wildcards where possible. They can grant more access than intended, especially as Kubernetes evolves or new resources are added.
Apply it using an administrative context:
kubectl config use-context minikube
kubectl apply -f pod-admin-clusterrole.yaml
Check it:
kubectl get clusterrole pod-admin
kubectl describe clusterrole pod-admin
Step 8: Bind the ClusterRole to the admin group
Now create the ClusterRoleBinding.
Create pod-admin-clusterrolebinding.yaml:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: pod-admin
subjects:
- kind: Group
name: admin
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: pod-admin
apiGroup: rbac.authorization.k8s.io
Kubernetes does not require you to create a separate group object. The group name comes from the authenticated identity, for example from a client certificate, OIDC token, or another authentication mechanism.
Apply it:
kubectl apply -f pod-admin-clusterrolebinding.yaml
Check it:
kubectl describe clusterrolebinding pod-admin
Anyone authenticated as a member of the admin group now receives the permissions from the pod-admin ClusterRole.
Step 9: Test Alice’s permissions
Switch to Alice:
kubectl config use-context Alice
kubectl config current-context
Try to apply the pod manifest:
kubectl apply -f pods.yaml
Expected result: allowed.
Try to delete pods:
kubectl delete pod nginx -n dev
kubectl delete pod nginx -n prod
Expected result: allowed.
Alice can manage pods across namespaces because the ClusterRole was granted through a ClusterRoleBinding.
Step 10: Understand pod subresources
Now comes a very important detail.
You might expect that if Alice can manage pods, she can also read logs or exec into a pod. But Kubernetes treats some operations as subresources.
Examples:
| Operation | RBAC resource |
|---|---|
| Read pod object | pods |
| Read pod logs | pods/log |
| Exec into a pod | pods/exec |
| Attach to a pod | pods/attach |
| Scale a deployment | deployments/scale |
Try to read logs as Alice with the first version of the ClusterRole:
kubectl logs nginx -n dev
You may get a forbidden error, because pods/log is a subresource and was not included.
Try exec:
kubectl exec -it nginx -n dev -- /bin/sh
You may also get a forbidden error because pods/exec requires explicit permission.
Step 11: Add pod subresources to the ClusterRole
Update pod-admin-clusterrole.yaml:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pod-admin
rules:
- apiGroups: [""]
resources:
- pods
- pods/log
- pods/exec
- pods/attach
verbs: ["*"]
Apply it again using an administrative context:
kubectl config use-context minikube
kubectl apply -f pod-admin-clusterrole.yaml
Switch back to Alice:
kubectl config use-context Alice
Now test logs:
kubectl logs nginx -n dev
And test exec:
kubectl exec -it nginx -n dev -- /bin/sh
Now these operations should work.
Step 12: Use kubectl auth can-i to validate access
A practical way to debug RBAC is kubectl auth can-i.
For Bob:
kubectl config use-context Bob
kubectl auth can-i list pods -n dev
kubectl auth can-i list pods -n prod
kubectl auth can-i create pods -n dev
kubectl auth can-i delete pods -n dev
Expected results:
yes
no
no
no
For Alice:
kubectl config use-context Alice
kubectl auth can-i create pods -n dev
kubectl auth can-i delete pods -n prod
kubectl auth can-i get pods --subresource=log -n dev
kubectl auth can-i create pods --subresource=exec -n dev
Expected results:
yes
yes
yes
yes
You can also list all allowed actions in a namespace:
kubectl auth can-i --list -n dev
This command is very useful when troubleshooting why a user can or cannot perform a certain operation.
Role vs ClusterRole: when to use which
Use a Role when permissions should be limited to one namespace.
Example use cases:
- A developer can read pods only in
dev. - A support engineer can view ConfigMaps in one application namespace.
- A CI/CD service account can deploy only into a specific namespace.
Use a ClusterRole when permissions need to be reusable or cluster-wide.
Example use cases:
- A monitoring tool needs to read pods in all namespaces.
- A platform admin needs to manage resources across namespaces.
- A user needs access to cluster-scoped resources such as nodes or namespaces.
There is also a useful hybrid pattern: create a ClusterRole, but bind it with a RoleBinding in a specific namespace. This allows you to define a reusable permission set once and grant it only inside selected namespaces.
Example:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: dev
subjects:
- kind: User
name: Bob
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: view
apiGroup: rbac.authorization.k8s.io
This binds the built-in view ClusterRole only inside the dev namespace.
Security notes and best practices
RBAC can become dangerous when permissions are too broad. Keep these principles in mind:
- Prefer namespace-level permissions with
RoleBindingwhere possible. - Avoid
cluster-adminfor daily work. - Avoid adding users to the
system:mastersgroup. - Avoid wildcard permissions in production unless there is a strong reason.
- Be careful with
create pods, because creating workloads can indirectly expose secrets, service account tokens, mounted volumes, and other namespace resources. - Be careful with
pods/exec, because it allows interactive access inside containers. - Periodically review roles and bindings to detect privilege creep.
- Use
kubectl auth can-iduring troubleshooting and access reviews.
Cleanup
Use an administrative context:
kubectl config use-context minikube
Delete the test pods and RBAC objects:
kubectl delete -f pods.yaml
kubectl delete -f dev-pod-reader-rolebinding.yaml
kubectl delete -f dev-pod-reader-role.yaml
kubectl delete -f pod-admin-clusterrolebinding.yaml
kubectl delete -f pod-admin-clusterrole.yaml
kubectl delete -f namespaces.yaml
Final thoughts
This lab shows the core RBAC flow in Kubernetes:
- Define permissions with a
RoleorClusterRole. - Bind those permissions to a user, group, or service account.
- Test access with real
kubectlcommands. - Validate the result with
kubectl auth can-i. - Add subresources explicitly when needed.
The most important lesson is that Kubernetes authorization is precise. Reading a pod, listing pods, reading pod logs, and executing into a pod are different authorization checks. This precision is what makes RBAC powerful, but it also means you need to design permissions carefully.
A good RBAC policy gives users exactly what they need, in the namespace where they need it, and nothing more.
Sources
- Kubernetes documentation: Using RBAC Authorization - https://kubernetes.io/docs/reference/access-authn-authz/rbac/
- Kubernetes documentation: Role Based Access Control Good Practices - https://kubernetes.io/docs/concepts/security/rbac-good-practices/
- Kubernetes documentation: kubectl auth can-i - https://kubernetes.io/docs/reference/kubectl/generated/kubectl_auth/kubectl_auth_can-i/