Kubernetes RBAC in production — 15 real-world scenarios explained
Roles, ClusterRoles, RoleBindings, ServiceAccounts — from zero to production. 15 scenarios ranked easy to hard, with working YAML.
Kubernetes RBAC (Role-Based Access Control) confuses most beginners. The docs list every option but never show what teams actually use in production.
This guide covers 15 real-world RBAC scenarios common when running CI/CD on Kubernetes — from “give this intern read-only access” to “restrict pod exec by namespace”. Every scenario has working YAML.
The 4 building blocks (2-line explanation)
- ServiceAccount — identity for a pod (like a user, but for workloads)
- Role — list of allowed actions in ONE namespace
- ClusterRole — same, but cluster-wide
- RoleBinding / ClusterRoleBinding — links a Role to a subject (user/SA/group)
That’s it. Everything else is combinations.
Scenario 1 — Give an intern read-only access to one namespace (EASY)
Common: “Priya just joined, let her look at logs in staging but not touch anything.”
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: staging
name: read-only
rules:
- apiGroups: [""]
resources: ["pods", "pods/log", "services"]
verbs: ["get", "list", "watch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: priya-readonly
namespace: staging
subjects:
- kind: User
name: priya@company.com
roleRef:
kind: Role
name: read-only
apiGroup: rbac.authorization.k8s.io
Priya can kubectl get pods -n staging but NOT kubectl delete pod ....
Scenario 2 — Team can deploy but not delete (EASY)
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch"] # NO delete
Scenario 3 — CI/CD SA that only deploys, nothing else (EASY-MEDIUM)
Tekton needs to apply Deployments after building images. It should NOT delete PVCs or read secrets from other namespaces.
Create ServiceAccount + Role scoped to just apps API + deployments/services:
apiVersion: v1
kind: ServiceAccount
metadata:
name: cicd
namespace: staging
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: staging
name: cicd-deployer
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "create", "update", "patch"]
- apiGroups: [""]
resources: ["services", "configmaps"]
verbs: ["get", "list", "create", "update", "patch"]
Bind:
kind: RoleBinding
metadata:
name: cicd-can-deploy
namespace: staging
subjects:
- kind: ServiceAccount
name: cicd
namespace: staging
roleRef:
kind: Role
name: cicd-deployer
apiGroup: rbac.authorization.k8s.io
Now Tekton’s SA can deploy, nothing more.
Scenario 4 — Access a specific secret only (MEDIUM)
You have a db-password secret. Only ONE app should read it — not every pod in the namespace.
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: read-db-secret
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["db-password"] # ← THIS specific secret only
verbs: ["get"]
Scenario 5 — SA for multiple namespaces (MEDIUM)
You want CI/CD to deploy to BOTH staging and prod.
Create a ClusterRole (cluster-wide permission definition) BUT bind it with RoleBinding (scoped per namespace):
kind: ClusterRole
metadata:
name: cicd-deployer
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "create", "update", "patch"]
---
# Bind in staging
kind: RoleBinding
metadata:
name: cicd-in-staging
namespace: staging
subjects: [{ kind: ServiceAccount, name: cicd, namespace: default }]
roleRef: { kind: ClusterRole, name: cicd-deployer, apiGroup: rbac.authorization.k8s.io }
---
# Bind in prod (same ClusterRole, different namespace)
kind: RoleBinding
metadata:
name: cicd-in-prod
namespace: prod
subjects: [{ kind: ServiceAccount, name: cicd, namespace: default }]
roleRef: { kind: ClusterRole, name: cicd-deployer, apiGroup: rbac.authorization.k8s.io }
Same permissions, two namespaces. No duplicate Role YAML.
Scenarios 6-15 (summaries — full YAML on GitHub)
- 6. Restrict a role to specific resource names — e.g. can only touch Deployment
myapp, not others - 7. “No delete” cluster admin — for panicky junior devs
- 8. Hybrid ClusterRole + RoleBinding — powerful pattern (used above)
- 9. RBAC for CRDs — allow accessing custom resources like ArgoCD Applications
- 10. Impersonation — user A acts as user B (for testing perms)
- 11. Aggregation labels — combine multiple ClusterRoles automatically
- 12. Non-resource URLs — allow access to
/healthz,/metrics - 13. Restrict pod exec by namespace — allow shell into pods only in dev, not prod
- 14. Multi-tenant isolation — each team can’t see other teams’ resources
- 15. Just-In-Time admin — auto-expiring admin roles (via Cert-Manager tricks)
Debugging: “why did this get forbidden?”
kubectl auth can-i <verb> <resource> --as <user> -n <namespace>
Examples:
kubectl auth can-i delete deployments --as priya@company.com -n staging
# → no
kubectl auth can-i list pods --as system:serviceaccount:default:cicd -n prod
# → yes
Example of a denied response — checking whether a specific user has permission to delete pods returns no:
Example of an allowed response — the current admin can list pods and, via wildcards, do everything:
This ONE command has saved hours of “why is it denied?” debugging.
The pattern
Every RBAC problem breaks into 3 questions:
- WHO (User? ServiceAccount? Group?)
- WHAT (verbs on which resources?)
- WHERE (which namespace or cluster-wide?)
Answer those 3 → you have your Role + Binding.
Free resources
- 50+ production K8s YAMLs (paid pack): see the pack
Common mistakes I hit
- Forgot
apiGroups: [""]— core resources need empty string, not omitted - Bound to wrong namespace — RoleBinding is namespaced, must be in same NS as Role
- Used ClusterRoleBinding when RoleBinding needed — gave cluster-wide access by mistake
- Wildcards
verbs: ["*"]in prod — always spell out verbs explicitly
RBAC is boring until you get bit by an over-permissive role. Then it becomes the most important K8s topic.
DevOps YAML Pack
36 production-ready configs — Kubernetes, Docker Compose, GitHub Actions, Terraform, Helm, Ansible. Every file heavily commented. Copy, paste, ship.
Get the pack — ₹499 →