Skip to content

Grant least privilege with Kubernetes RBAC

Severity: criticalApplies to: Kubernetes 1.29+Applies to: Kubernetes 1.34
The fix
# A Role scoped to one namespace and specific verbs — not ClusterRole, not *
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: orders
name: orders-app
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch"]

cluster-admin is unrestricted control of the entire cluster: every namespace, every secret, every workload, and the ability to grant others the same. It is the Kubernetes equivalent of root, and it is bound far more widely than it should be — to CI pipelines, to every engineer’s kubeconfig, to operators and controllers that need a fraction of it.

Every over-broad binding is a blast radius. A leaked CI token bound to cluster-admin is the whole cluster. A compromised pod whose service account can get secrets cluster-wide reads every credential you have. RBAC is where you decide that a leak is bounded to one namespace and one verb instead of everything.

Three RBAC privileges are special because they let a subject grant themselves more — they’re how limited access becomes cluster-admin:

  • escalate on roles — create a role with more permissions than you have.
  • bind on rolebindings — bind yourself to a more powerful role.
  • impersonate — act as another user or service account, including admins.

A role that grants create/update on roles and rolebindings, or any of the verbs above, is effectively cluster-admin even if it looks scoped. Also watch: create pods is close to node-level power, because a pod can mount host paths and service account tokens (see privileged containers), and access to secrets is access to whatever those secrets protect.

Role, not ClusterRole; verbs, not wildcards

Section titled “Role, not ClusterRole; verbs, not wildcards”

Two habits do most of the work:

  • Role (namespaced), not ClusterRole, unless the thing genuinely operates cluster-wide. A ClusterRole bound with a ClusterRoleBinding applies in every namespace — the most common way “just for this app” becomes cluster-wide.
  • Named verbs and resources, never *. verbs: ["*"] or resources: ["*"] grants everything including future resources you’ve never heard of. List what the workload calls.

Applications almost never need to write cluster-scoped objects. If a manifest asks for a ClusterRole with wildcards, that’s the finding.