DevOps

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:

  1. WHO (User? ServiceAccount? Group?)
  2. WHAT (verbs on which resources?)
  3. WHERE (which namespace or cluster-wide?)

Answer those 3 → you have your Role + Binding.

Free resources

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.

Recommended

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 →
Never miss an article