Kubernetes RBAC Lab: Setting Up Permissions to Read and Manage Pods

0


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 the dev namespace.
  • Alice, who belongs to the admin group 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/logpods/exec, and pods/attach.

What we are going to build

The scenario is simple but very useful:

User or groupScopeAllowed actionsNot allowed
Bobdev namespace onlygetlist podsCreate, update, delete pods; access pods in prod; list pods across all namespaces
admin groupCluster-wideManage podsInitially cannot read logs or exec into pods until subresources are added
AliceMember of admin groupInherits permissions from the groupCannot 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:

ObjectScopePurpose
RoleNamespaceDefines permissions inside one namespace
ClusterRoleCluster-wideDefines permissions across namespaces or for cluster-scoped resources
RoleBindingNamespaceConnects a Role or ClusterRole to users, groups, or service accounts inside one namespace
ClusterRoleBindingCluster-wideConnects 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.
  • get allows reading a specific pod.
  • list allows listing multiple pods.
  • This Role applies only in the dev namespace.

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

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: ["*"]

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:

OperationRBAC resource
Read pod objectpods
Read pod logspods/log
Exec into a podpods/exec
Attach to a podpods/attach
Scale a deploymentdeployments/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 RoleBinding where possible.
  • Avoid cluster-admin for daily work.
  • Avoid adding users to the system:masters group.
  • 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-i during 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:

  1. Define permissions with a Role or ClusterRole.
  2. Bind those permissions to a user, group, or service account.
  3. Test access with real kubectl commands.
  4. Validate the result with kubectl auth can-i.
  5. 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/

Post a Comment

0 Comments

Post a Comment (0)

#buttons=(Ok, Go it!) #days=(20)

This site uses cookies from Google to deliver its services and analyze traffic. Your IP address and user-agent are shared with Google along with performance and security metrics to ensure quality of service, generate usage statistics, and to detect and address abuse. More Info
Ok, Go it!