Articles / Kubernetes RBAC Done Right
Kubernetes · RBAC

Kubernetes RBAC Done Right

Kubernetes ships with a capable, deny-by-default authorization model: no subject can do anything until a role grants it. That is the good news. The bad news is how quickly real clusters erode it — a cluster-admin binding handed out to unblock a deployment, a wildcard verb copied from a Stack Overflow answer, a service account token mounted into every pod by default. RBAC done right is less about knowing the API objects than about resisting the small, reasonable-looking grants that add up to a flat trust model.

RBAC has four objects and one rule. Roles and ClusterRoles are sets of permissions — verbs (get, list, create…) on resources (pods, secrets…). RoleBindings and ClusterRoleBindings attach those permission sets to subjects: users, groups, or service accounts. A Role is namespaced; a ClusterRole is cluster-wide. The rule: permissions are purely additive and there are no deny rules, so the effective access of any subject is the union of everything bound to it.

The three grants that flatten a cluster

Subjectuser / service accountRoleBinding→ ClusterRoleGranted verbswildcards, secrets,pod-createEscalateread secrets / run podsCluster-wide accessWildcards & broadsecret accessflatten leastprivilege
How one over-broad grant turns a service account into cluster-wide access.
  • cluster-admin to humans and service accounts. It is god mode over the whole cluster. Every binding to it is a full compromise waiting for one leaked credential. Treat it as break-glass, not a default.
  • Wildcards. verbs: ["*"] or resources: ["*"] grants permissions you did not consciously decide to give, including on resource types that did not exist when you wrote the rule.
  • Broad secrets access. get/list on secrets in a namespace hands over every credential in it. And the right to create pods often equals the right to read secrets — because a pod can mount them.
Why it matters: RBAC is additive with no deny. That makes least privilege a subtractive discipline you have to sustain — every over-grant is permanent until someone notices and removes it, and attackers are very good at noticing first.

The privilege-escalation edges people miss

Some permissions are more powerful than they look because they are transitive. The verb escalate lets a subject grant itself permissions it does not already hold; bind lets it attach existing roles to new subjects; impersonate lets it act as another user or group entirely. Rights over workload controllers (Deployments, DaemonSets, Jobs) are effectively rights to run arbitrary pods, which is effectively node-level access. And anyone who can create a pod with a chosen service account inherits that account's permissions. Auditing RBAC means reasoning about these reachability edges, not just reading role names — which is what tooling designed to compute the closure over these edges is for.

Working practices that hold up

  • Start from zero and add. Grant the specific verbs on the specific resources a workload needs, scoped to a namespace with a Role wherever a ClusterRole is not truly required.
  • One service account per workload. Never share the default. Set automountServiceAccountToken: false on pods that make no API calls — most application pods don't.
  • Prefer groups for humans. Bind roles to groups from your identity provider, not individual users, so access follows joiners and leavers automatically.
  • Review the dangerous verbs explicitly. Regularly enumerate who holds escalate, bind, impersonate, secret access, and pod-create, and justify each.
  • Audit continuously. Use kubectl auth can-i --list and RBAC analysis tooling to see effective permissions, and alert on new bindings to privileged roles.
A tight, namespaced Role — not a wildcard in sightkind: Role
metadata: { namespace: payments, name: app-reader }
rules:
- apiGroups: [""]
  resources: ["configmaps"]
  resourceNames: ["app-config"]  # scoped to one object, one verb
  verbs: ["get"]

RBAC done right is never finished; it is a standing practice of granting narrowly and reviewing often, fighting the entropy that pulls every cluster toward flat, over-permissioned trust. And it presupposes you know what is running: an unaudited workload with a mounted admin token is a privilege-escalation path you cannot close because you cannot see it. You can't scope access for a service account you never knew existed.

Test for it — in practice
  • List the bindings to cluster-admin and see how many humans and service accounts hold it — anything beyond a tiny break-glass set is worth questioning.
  • Run kubectl auth can-i --list as one workload's service account and skim for wildcard verbs or broad secrets access it has no reason to need.

Eyeballing one account is manageable; computing effective permissions and the escalation edges across every subject in the cluster is what our assessment runs for you.

Keep reading
Pod Security Standards and Admission Control