Kubernetes Cluster RBAC: The Mental Model That Actually Sticks

RBAC (Role-Based Access Control) is how Kubernetes answers one question after it knows who you are: may this identity perform this action on this object? It is not authentication. It is not network policy. It is the authorization layer on the API server—and once you see the four objects and the request path, cluster permissions stop feeling like magic.

In short

Define permissions in a Role or ClusterRole (rules: apiGroups, resources, verbs). Attach them to subjects with a RoleBinding or ClusterRoleBinding. Namespace-scoped roles stay inside one namespace; cluster roles can reach cluster-wide resources or be reused across namespaces via bindings. Test with kubectl auth can-i and grant the smallest rule set that still lets work flow.

Where RBAC sits in the cluster

Every change and read in Kubernetes flows through the kube-apiserver. Before your request mutates or returns data, the API server runs a chain of checks—authentication first, then authorization, then admission controllers. For background on the control plane, see Kubernetes architecture in simple terms.

  1. Authentication — Proves identity. Examples: client certificate (common for humans and some controllers), bearer token (ServiceAccount JWT), OIDC (many enterprises), webhook token review.
  2. Authorization — Decides if that identity may do the operation. RBAC is the default and most common mode here.
  3. Admission — Enforces policies (Pod Security, resource quotas, OPA/Kyverno, mutating webhooks) after authorization says “allowed.”

RBAC never replaces step one. A perfect Role does nothing if the caller cannot authenticate. Conversely, strong cloud IAM at the edge does not remove the need for in-cluster RBAC: anything that can reach the API still needs explicit rules inside the cluster.

The four objects (and only four you need daily)

RBAC in Kubernetes is deliberately small. Think in two pairs:

Permission definition Who gets it
Role — rules in one namespace RoleBinding — links Role → subject(s) in that namespace
ClusterRole — rules cluster-wide or reusable template ClusterRoleBinding — links ClusterRole → subject(s) everywhere it applies

Role + RoleBinding is the default shape for application teams: “this group may manage Deployments and read Secrets in team-a.” ClusterRole + ClusterRoleBinding is for platform operators, CRD installers, or anyone who must touch nodes, namespaces, PersistentVolumes, or cluster-scoped custom resources.

Important nuance: a ClusterRole does not automatically mean “god mode.” It only defines what could be granted. Scope is determined by the binding: a RoleBinding that references a ClusterRole grants only the rules needed in that binding’s namespace (a common pattern for reusing the built-in view, edit, admin roles per namespace).

Anatomy of a rule

Permissions are a list of rules. Each rule names API groups, resources, optional resource names, and verbs (actions). A minimal read-only rule for Pods in the core API group looks like this:

rules:
- apiGroups: [""]           # "" = core group (Pods, Services, ConfigMaps, …)
  resources: ["pods"]
  verbs: ["get", "list", "watch"]

Useful fields:

  • apiGroups"" for core; apps for Deployments; rbac.authorization.k8s.io for Roles themselves; * for all groups (avoid in custom roles unless you truly mean it).
  • resources — Plural resource names (pods, deployments). Subresources use a slash: pods/log, pods/exec (exec is powerful—treat it like shell access).
  • resourceNames — Restricts the rule to specific objects (least privilege for one Secret or ConfigMap).
  • verbs — Common set: get, list, watch, create, update, patch, delete. Aggregated roles often add deletecollection. Non-resource URLs use nonResourceURLs and verbs like get (e.g. /healthz, /metrics).

Discover what exists before you write YAML: kubectl api-resources lists resources; kubectl explain pod.spec --api-version=v1 helps with schema. For manifest structure practice, the hands-on YAML anatomy post pairs well with this one.

Subjects: who receives the role

Bindings point at subjects:

  • User — Opaque string from your authentication layer (e.g. [email protected] from OIDC). Kubernetes does not store User objects; RBAC only references the name the authenticator returns.
  • Group — Same story—groups come from your IdP or certificate org field.
  • ServiceAccount — A namespaced identity for Pods and controllers. This is how workloads call the API without sharing human credentials.

Production hygiene: one ServiceAccount per workload (or per trust boundary), automount tokens only when needed, and bind narrowly. GitOps controllers (Flux, Argo CD) each deserve their own SA and ClusterRole scoped to the namespaces or clusters they manage—see GitOps principles for why blast radius matters.

How the API server decides (evaluation in plain language)

On each request, RBAC roughly:

  1. Collects all RoleBindings and ClusterRoleBindings whose subjects match the caller (user, group, or service account).
  2. Resolves each binding to its Role or ClusterRole rules.
  3. If any rule allows the requested verb on the requested resource (and namespace, if namespaced), access is granted.
  4. Otherwise the request fails with 403 Forbidden.

RBAC is additive only—there is no “deny” rule in standard RBAC. To block something you combine least-privilege grants with admission policy (OPA, Kyverno) or avoid handing out wildcards. If you are used to AWS IAM, this differs from explicit Deny statements in policy evaluation; the mental model is closer to “union of allows” with separate guardrails elsewhere. For IAM document anatomy on AWS, see IAM policy JSON anatomy.

Built-in ClusterRoles you should recognize

Kubernetes ships aggregated roles. Teams often bind these instead of inventing new ones:

  • view — Read-only access to most namespaced objects (no Secrets).
  • edit — Read/write on most namespaced objects (no Roles/Bindings, no quota changes).
  • admin — Full control within a namespace, including Roles and RoleBindings in that namespace.
  • cluster-admin — Unrestricted cluster control. Use sparingly; break-glass only for many orgs.

Platform teams frequently create custom ClusterRoles that embed only what CI, observability agents, or namespace admins need—then bind with RoleBinding per namespace so the same role definition stays DRY.

Worked patterns (copy the idea, not blindly the YAML)

Namespace developer

Grant edit in team-a via RoleBinding to OIDC group team-a-devs. Developers deploy apps; they cannot edit cluster nodes or other namespaces.

Read-only SRE across one namespace

Custom Role with get/list/watch on pods, events, deployments, and pods/log—no delete, no pods/exec.

CI pipeline in its namespace

ServiceAccount ci-deploy in team-a with permission to patch Deployments and read ConfigMaps—nothing cluster-wide.

Cluster operator

Separate break-glass ClusterRoleBinding for humans; day-to-day operators get a custom ClusterRole for nodes, storage classes, and CRDs—still not cluster-admin unless unavoidable.

Verify before you ship

RBAC bugs show up as mysterious “Forbidden” in CI at 2 a.m. Make checking habitual:

# As your current kubectl user
kubectl auth can-i create deployments -n team-a

# Impersonate a ServiceAccount (needs permission to impersonate)
kubectl auth can-i list secrets -n team-a \
  --as=system:serviceaccount:team-a:ci-deploy

# List all permissions for a subject in a namespace
kubectl auth can-i --list -n team-a \
  --as=system:serviceaccount:team-a:ci-deploy

In CI, run can-i after applying manifests, or use policy tests (e.g. validate rendered Roles with kubeconform, chart tests, or OPA conftest) so overly broad rules never merge.

Least privilege that survives real teams

  • Prefer namespace boundaries — Align Roles with team or product namespaces; use ClusterRoles only for true cluster surfaces.
  • Avoid * on verbs or resources in custom roles unless you are generating a platform-wide admin role—and then protect it like root keys.
  • Split human and automation identities — No shared “deploy” user; ServiceAccounts for controllers, OIDC users for people.
  • Watch subresourcespods/exec, pods/portforward, and secrets access deserve explicit review.
  • Aggregate carefully — Label-based ClusterRole aggregation is powerful for extensions; understand what your Helm chart adds to the API surface.
  • Audit bindings — Periodically list ClusterRoleBindings; cluster-admin bindings should be countable on one hand.

RBAC vs other controls (do not confuse them)

  • NetworkPolicy / service mesh — Controls traffic between Pods; does not stop a authorized caller from deleting a Deployment via the API.
  • Pod Security admission — Restricts Pod spec fields; unrelated to who may call kubectl delete.
  • Node / kubelet access — SSH to a node is a different trust domain; lock down nodes separately.
  • Cloud IAM — Governs AWS/GCP/Azure APIs; EKS/IRSA and GKE Workload Identity federate cloud identity into ServiceAccounts—they do not replace Kubernetes RBAC for in-cluster API calls.

Common pitfalls

  • Binding the wrong subject kind — Typo in ServiceAccount namespace or name silently leaves the workload powerless—or you bind the default SA in default by mistake.
  • ClusterRoleBinding when RoleBinding would do — Grants cluster-wide reach when you meant one namespace.
  • Forgetting subresources — Allowing pods but not pods/log breaks debugging; allowing pods/exec is equivalent to handing out shells.
  • Assuming “view” sees Secrets — It does not; if someone needs Secret access, name that explicitly and narrowly.
  • Editing live bindings only — Drift from Git undermines audit; store Roles and Bindings in version control (GitOps).
  • Relying on ABAC or legacy modes — Modern clusters use RBAC; understand your distribution’s defaults on first boot.

A practical learning path

  1. Stand up a lab cluster (hands-on Part 1).
  2. Create a namespace, a ServiceAccount, a tight Role, and a RoleBinding; deploy a Pod that uses the SA.
  3. Run kubectl auth can-i --list before and after each change.
  4. Intentionally trigger 403, fix the rule, and document the binding in Git.
  5. Read one production Helm chart’s RBAC templates—compare what the chart requests vs what you are willing to grant.

Further reading

  • Kubernetes documentation — Using RBAC Authorization (official reference for verbs, defaults, and escalation)
  • Kubernetes documentation — Controlling Access to the Kubernetes API (authentication modes overview)
  • CNCF and vendor guides — EKS/GKE/AKS integration patterns for OIDC and workload identity
  • NIST and CIS Kubernetes benchmarks — RBAC and audit configuration baselines

Blog index · Kubernetes architecture · GitOps principles · IAM policy anatomy

Back to blog list