RBAC — Controlling Who Can Do What in Kubernetes
The Four RBAC Objects — Explained Simply
◈ DIAGRAM
+---------------------------+ +---------------------------+ +---------------------------+| WHO | | WHAT (permissions) | | SCOPE || | | | | || ServiceAccount | | Role | | One namespace only || User | --> | ClusterRole | --> | Entire cluster || Group | | | | |+---------------------------+ +---------------------------+ +---------------------------+ Binding objects connect WHO to WHAT:+------------------------------------------+| RoleBinding -> Role or ClusterRole in ONE namespace || ClusterRoleBinding -> ClusterRole across ALL namespaces |+------------------------------------------+A Concrete Team Setup — PhonePe Multi-Team Cluster
PhonePe has three teams sharing one cluster, each with different access needs:
◈ DIAGRAM
+------------------------+ +------------------------+ +------------------------+| payments-team | | platform-team | | ci-bot || | | | | || manage pods/deploys | | read across all | | update deployments || in their namespace | | namespaces to debug | | (image tags) only || only | | | | |+------------------------+ +------------------------+ +------------------------+
YAML
# ── SCENARIO 1: Namespace-scoped developer access ──apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: developer namespace: payments-prodrules: - apiGroups: ["apps", ""] resources: ["deployments", "pods", "services", "configmaps"] verbs: ["get", "list", "watch", "create", "update", "patch"] - apiGroups: [""] resources: ["secrets"] verbs: ["get", "list"] # Can READ secrets, not create or delete themapiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: payments-developer-binding namespace: payments-prodsubjects: - kind: User name: rahul@devops.in # A specific engineer apiGroup: rbac.authorization.k8s.ioroleRef: kind: Role name: developer apiGroup: rbac.authorization.k8s.io
YAML
# ── SCENARIO 2: Cluster-wide read-only for platform team ──apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: platform-readonlyrules: - apiGroups: ["*"] resources: ["*"] verbs: ["get", "list", "watch"] # Read everything, touch nothingapiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: platform-team-bindingsubjects: - kind: Group name: platform-engineers # Entire group — not individual users apiGroup: rbac.authorization.k8s.ioroleRef: kind: ClusterRole name: platform-readonly apiGroup: rbac.authorization.k8s.io
YAML
# ── SCENARIO 3: CI bot — only update deployments ──apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: ci-deployer namespace: payments-prodrules: - apiGroups: ["apps"] resources: ["deployments"] verbs: ["get", "patch", "update"] # Just enough to update image tagsapiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: ci-bot-binding namespace: payments-prodsubjects: - kind: ServiceAccount name: github-actions-bot namespace: payments-prodroleRef: kind: Role name: ci-deployer apiGroup: rbac.authorization.k8s.ioThe Verbs Cheatsheet
| Verb | What it allows |
|---|---|
get |
Read a specific named resource |
list |
List all resources of a type |
watch |
Stream changes to resources in real-time |
create |
Create new resources |
update |
Replace an entire resource object |
patch |
Modify specific fields of a resource |
delete |
Delete a single resource |
deletecollection |
Delete all resources matching a selector |
* |
All verbs — full admin access, use sparingly |
How RBAC Request Evaluation Works
Bash
+------------------------------------------+| kubectl request arrives at API server | <- e.g. rahul tries to delete a Secret+------------------------------------------+ | v+------------------------------------------+| Authentication: who is making this call? | <- Certificate, token, or kubeconfig+------------------------------------------+ | v+------------------------------------------+| Authorization: is this action allowed? | <- RBAC checks Role/ClusterRole rules+------------------------------------------+ | | v v +------------+ +------------+ | ALLOWED | | FORBIDDEN | | 200 OK | | 403 Error | +------------+ +------------+Testing RBAC Without Guessing
Bash
# Can the GitHub Actions SA update deployments in payments-prod?kubectl auth can-i update deployments \ --as=system:serviceaccount:payments-prod:github-actions-bot \ -n payments-prod# Output: yes # Can it delete pods? (should be no)kubectl auth can-i delete pods \ --as=system:serviceaccount:payments-prod:github-actions-bot \ -n payments-prod# Output: no # Full dump of everything YOUR current user can do in a namespacekubectl auth can-i --list -n payments-prod # List all RoleBindings in a namespace to audit who has whatkubectl get rolebindings -n payments-prod -o wide # Find all ClusterRoleBindings that grant cluster-adminkubectl get clusterrolebindings -o json | \ jq '.items[] | select(.roleRef.name=="cluster-admin") | .subjects'Common RBAC Mistakes
| Mistake | Consequence | Fix |
|---|---|---|
Using ClusterRoleBinding for team access |
Team can read secrets in all namespaces | Use RoleBinding scoped to their namespace only |
Granting * verbs on * resources |
Full cluster admin for app pods | List exact verbs and resources — least privilege |
Forgetting the apiGroups field |
Rules silently do not apply | Use "" for core resources, "apps" for Deployments |
Binding cluster-admin to CI service accounts |
Compromised pipeline = full cluster takeover | Create a minimal ci-deployer Role with only patch/update on deployments |
| Not auditing RBAC after team changes | Departed engineers retain access | Audit bindings quarterly with kubectl get rolebindings -A |
COMMON MISTAKE / WARNING**Security:** Never grant `*` verbs on `secrets` to non-admin users or service accounts. Secrets contain database passwords, API keys, and TLS certificates. On a Zerodha-scale trading platform, a compromised pod with secret read access can exfiltrate every downstream service credential in minutes.
PLACEMENT PRO TIP**Tip:** Run `kubectl auth can-i --list -n` to get a full dump of what your current user is allowed to do. This is the fastest way to debug a `403 Forbidden` error in a CI pipeline without reading through 10 YAML files.
REMEMBER THIS**Remember:** `RoleBinding` can reference a `ClusterRole` — and this is intentional. Define a `ClusterRole` with standard developer permissions once, then bind it per-namespace with individual `RoleBindings`. This avoids duplicating Role YAML across every team namespace.
COMMON MISTAKE / WARNING**Common Mistake:** Granting `cluster-admin` to service accounts used by CI tools like GitHub Actions or ArgoCD. If the pipeline is compromised, the attacker has unrestricted access to the entire cluster. Always create a minimal `ci-deployer` Role with exactly the verbs the pipeline needs — nothing more.