Intro
Role-Based Access Control (RBAC) is the primary authorization mechanism in Kubernetes, controlling who can perform which actions on which resources. Misconfigured RBAC is one of the most common causes of access denied errors, failed deployments, and security audit findings. This guide provides a practical, command-driven approach to troubleshooting RBAC issues in Kubernetes clusters. You will learn how to systematically identify permission problems, verify configurations, apply least-privilege fixes, and avoid common pitfalls.
This article is intended for developers, DevOps engineers, and platform operators who manage Kubernetes clusters and need to resolve authorization failures quickly without compromising security. Every command includes expected output or a clear signal of success, and every recommendation is scoped to a specific Kubernetes version (1.24+ unless otherwise noted). The goal is operational safety: understand the current state, make minimal changes, verify the result, and document recovery steps.
Version and Environment Inventory
Before troubleshooting RBAC, confirm the version and environment characteristics. RBAC is stable across all supported Kubernetes versions, but API group names and certain flags may vary. Run these read-only commands to capture the current state:
kubectl version --short(deprecated in 1.26, usekubectl versionwithout--short)kubectl cluster-infokubectl get nodes -o wide
Example output for kubectl version on a client:
Client Version: v1.27.3
Kustomize Version: v5.0.1
Server Version: v1.26.5
Confirm that the server version matches your expectations. RBAC API is rbac.authorization.k8s.io/v1 since Kubernetes 1.8, so no version-specific changes are needed for basic troubleshooting.
Identify the exact component failing. Is it a user, a service account, or a group? Use kubectl auth can-i --list to see all permissions for the current user, or specify another user with --as:
kubectl auth can-i --list --as=system:serviceaccount:default:my-app
This outputs a table of resources and allowed verbs. If you get no output, the user may have no permissions at all.
For service accounts, confirm the namespace and existence:
kubectl get serviceaccount my-app -n default
If not found, the pod referencing it will fail to start with a CreateContainerConfigError. Check events:
kubectl describe pod my-pod -n default | grep -A5 Events
Always start with read-only observations and avoid making changes until you understand the blast radius.
Safe Configuration Path
RBAC configuration consists of four main object types: Role, ClusterRole, RoleBinding, and ClusterRoleBinding. A safe troubleshooting path begins with viewing existing objects and testing permissions before modifying anything.
Step 1: View current RBAC objects. List roles and bindings in the relevant namespace:
kubectl get roles,rolebindings -n my-namespace
kubectl get clusterroles,clusterrolebindings | grep my-app
Step 2: Test permissions without changing anything. Use kubectl auth can-i to check specific actions for a user or service account:
kubectl auth can-i create deployments --as=system:serviceaccount:default:my-app -n my-namespace
Expected output if allowed:
yes
If denied:
no
Step 3: Inspect the effective rules. Use kubectl describe to view the role's rules:
kubectl describe role my-app-role -n my-namespace
Example output:
Name: my-app-role
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
pods [] [] [get list watch]
deployments [] [] [get list watch create update patch delete]
Step 4: Make the smallest justified change. If the role lacks a required verb, create a patch file or use kubectl edit with care. For example, to add delete permission for pods, create a YAML patch:
# add-delete-pods.yaml
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch", "delete"]
Apply with:
kubectl patch role my-app-role -n my-namespace --patch "$(cat add-delete-pods.yaml)"
Step 5: Verify the change. Re-run kubectl auth can-i to confirm the new permission is effective. Also, check that the binding still references the correct role and subject:
kubectl get rolebinding my-app-binding -n my-namespace -o yaml
Never use wildcard * in verbs or resources unless absolutely necessary, and avoid granting cluster-admin to service accounts.
Verification and Diagnostics
After making a change, verify both the configuration and the actual behavior. Here are key diagnostic commands:
Check all permissions for a service account:
kubectl auth can-i --list --as=system:serviceaccount:default:my-app -n my-namespace
Example output snippet:
Resources Non-Resource URLs Resource Names Verbs
pods [] [] [get list watch]
deployments.apps [] [] [create delete get list patch update watch]
Check a specific action on a specific resource:
kubectl auth can-i get secret/my-secret --as=system:serviceaccount:default:my-app -n my-namespace
View RBAC-related events in the namespace:
kubectl get events -n my-namespace --field-selector reason=FailedToGetResource
Examine API server audit logs (if enabled). For managed clusters, check cloud provider logs. For self-managed, grep the audit log:
grep "my-app" /var/log/kubernetes/audit.log | grep "forbidden"
Audit logs provide detailed reasons for denials, such as missing verbs or missing resource names. If you see forbidden: User "system:serviceaccount:default:my-app" cannot get resource "pods", it indicates a missing RBAC permission.
Test pod execution with the service account. Create a temporary pod with the service account and run kubectl inside it (if the image has kubectl) or use kubectl run:
kubectl run test-pod --image=bitnami/kubectl:latest --restart=Never --serviceaccount=my-app -n my-namespace -- sleep 3600
Then exec into it and run a command that should be allowed or denied:
kubectl exec -it test-pod -n my-namespace -- kubectl get pods
If denied, you will see a Forbidden error. Clean up with kubectl delete pod test-pod -n my-namespace.
Failure Modes and Recovery
RBAC failures typically manifest as Forbidden errors in logs, failed API calls, or pods stuck in CrashLoopBackOff. Here are the most common failure modes and how to recover.
1. Missing permission for a service account used by a controller.
Symptoms: Controller logs show 403 Forbidden when watching resources. Example:
E0120 10:00:00.123456 1 reflector.go:138] pkg/mod/k8s.io/[email protected]/tools/cache/reflector.go:167: Failed to watch *v1.Pod: failed to list *v1.Pod: pods is forbidden: User "system:serviceaccount:my-namespace:my-controller" cannot list resource "pods" in API group "" in the namespace "my-namespace"
Recovery: Create or update a Role granting list and watch on pods, and bind it to the service account. Apply and restart the controller pod to pick up changes (if not using dynamic reload).
2. User cannot perform action but has a role that seems sufficient.
Symptom: kubectl auth can-i returns no despite having a role with the verb. Possible causes: role binding in wrong namespace, subject name mismatch, or API group mismatch.
Recovery: Verify binding details:
kubectl get rolebinding my-binding -n my-namespace -o yaml
Check subjects and roleRef. Ensure the user's name is exactly as authenticated (case-sensitive). For cluster-scoped resources, ensure you used ClusterRole and ClusterRoleBinding, not namespaced ones.
3. Wildcard permissions causing unintended access.
Symptom: A service account has more access than intended, discovered in an audit. Recovery: Replace wildcard rules with explicit resources and verbs. Example bad rule:
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
Replace with explicit list, e.g., only pods and deployments with specific verbs. Use kubectl auth can-i --list to document the reduced permissions.
4. ClusterRoleBinding created instead of RoleBinding for namespaced access.
Symptom: User can access all namespaces, not just intended one. Recovery: Delete the ClusterRoleBinding and create a RoleBinding in the specific namespace. Verify with kubectl auth can-i --list --as=user -n target-ns.
5. RBAC changes not taking effect.
Symptom: After applying a new role, access is still denied. RBAC is eventually consistent but changes should propagate within seconds. If not, check for typos or that the API server has the RBAC authorizer enabled (default). Verify with kubectl get clusterrolebinding system:node -o yaml to ensure RBAC is functioning. If issues persist, restart the API server (only for self-managed clusters).
Operations Checklist
Use this checklist during any RBAC troubleshooting session. Assign an owner for each item (e.g., the on-call engineer) and review the checklist after every incident or change. For ongoing RBAC hygiene, review permissions quarterly.
| # | Item | Owner | Command or Action | Expected Result | Frequency / Review |
|---|---|---|---|---|---|
| 1 | Confirm cluster version and RBAC API | DevOps Lead | kubectl version | Server v1.24+ | Once per cluster setup |
| 2 | Identify failing user or service account | On-call Engineer | kubectl auth can-i --list --as=<subject> | List of allowed resources | Per incident |
| 3 | Check current roles and bindings | On-call Engineer | kubectl get roles,rolebindings -n <ns> | Objects exist | Per incident |
| 4 | Test specific permission | On-call Engineer | kubectl auth can-i <verb> <resource> --as=<subject> | yes or no | Per incident |
| 5 | Inspect role rules | Security Engineer | kubectl describe role <name> -n <ns> | Rules match desired access | Quarterly audit |
| 6 | Apply minimal RBAC change | DevOps Lead | kubectl apply -f updated-role.yaml | Change applied | Per change |
| 7 | Verify permission after change | On-call Engineer | Re-run kubectl auth can-i | Output changes to yes if intended | Per change |
| 8 | Review audit logs for denials | Security Engineer | grep "forbidden" /var/log/kubernetes/audit.log | No unexpected denials | Monthly |
| 9 | Rotate service account tokens if compromised | Security Engineer | kubectl create token <sa> -n <ns> | New token issued | As needed |
| 10 | Document RBAC policies and changes | DevOps Lead | Update internal wiki | Up-to-date documentation | After each change |
Common Pitfalls
Avoid these frequent mistakes when managing Kubernetes RBAC.
Pitfall 1: Assigning cluster-admin to service accounts. This is tempting for quick fixes but creates a massive security risk. If the pod is compromised, the attacker gains full cluster control.
Why it happens: Developers often want to avoid permission issues during development. How to avoid: Use least privilege from the start. Create specific roles for each service account. Use tools like kubectl auth can-i to determine exact permissions needed. Recovery: Replace ClusterRoleBinding with a namespaced RoleBinding with minimal permissions.
*Pitfall 2: Using wildcard in verbs or resources.** Wildcards grant broad access that may violate security policies and make auditing harder.
Why it happens: It is quicker than listing every resource. How to avoid: Spend the extra minutes to list precise resources and verbs. Recovery: Audit existing wildcards with kubectl get clusterroles,roles -o yaml | grep -B5 -A5 '\*' and replace them with explicit rules.
Pitfall 3: Forgetting that RBAC is additive only. There is no "deny" rule in RBAC. If a user has any role allowing an action, they can perform it, even if another role seems to deny it.
Why it happens: Misunderstanding RBAC semantics. How to avoid: Design roles to be mutually exclusive or use external policy engines like OPA if you need deny rules. Recovery: Review all bindings for the user and remove unwanted roles.
Pitfall 4: Mismatching API groups. For example, granting deployments in the core API group instead of apps will not work.
Why it happens: Confusion about resource groups. How to avoid: Use kubectl api-resources to find the correct group for a resource. Recovery: Update the role with the correct apiGroups field.
Pitfall 5: Ignoring namespace scoping. Creating a RoleBinding instead of a ClusterRoleBinding for cluster-scoped resources or vice versa.
Why it happens: Not checking whether the resource is namespaced or cluster-scoped. How to avoid: Use kubectl api-resources --namespaced=true/false to determine scope. Recovery: Convert the binding type appropriately.
Conclusion
Troubleshooting Kubernetes RBAC requires a systematic approach: identify the subject, inspect current permissions, test with kubectl auth can-i, make minimal changes, and verify. By following the practices in this guide, you can resolve access issues quickly while maintaining a secure, least-privilege posture. Always document changes and review RBAC policies regularly to prevent future incidents.
As a next step, run kubectl auth can-i --list for your own user and for any service accounts in your cluster to identify overly broad permissions. Then, choose one service account and tighten its permissions using the safe configuration path described above. Record the current state, apply a minimal role update, and verify with a test command. Remember: the goal is not just to fix the immediate error but to ensure long-term security and auditability.