Skip to main content
cd ../config-traps
risk/register/clusterrolebinding-to-systemauthenticated-cluster-admin-for-free.html
Kubernetes RBACcritical severityKubernetes

ClusterRoleBinding to system:authenticated: Cluster-Admin for Free

Overview

A ClusterRoleBinding scoped to the system:authenticated group hands its permissions to every service account token in the cluster, not just human users. Node compromise, RBAC audits, and NetworkPolicy all fail to catch it because the binding looks like ordinary discovery access until a permissive ClusterRole gets attached underneath it.

At a glance

Unsafe setting
A ClusterRoleBinding grants a permissive ClusterRole to the system:authenticated group instead of named subjects.
Failure trigger
Any pod's mounted ServiceAccount token is used against the API server after compromise, inheriting group-wide permissions with no further authorisation check.
Blast radius
Any authenticated principal, including default ServiceAccount tokens in low-privilege pods, gains cluster-admin-like access across all namespaces.
Recommended control
Audit all bindings for system:authenticated or system:unauthenticated subjects and enforce an admission policy rejecting non-default roles bound to those groups.

Fix commands and configuration

kubectl get clusterrolebindings,rolebindings -A -o json | jq '.items[] | select(.subjects[]?.name=="system:authenticated" or .subjects[]?.name=="system:unauthenticated")'
subjects[].name
system:authenticated

The Trap

A ClusterRoleBinding (or RoleBinding) whose subject is the built-in group system:authenticated, paired with a ClusterRole that carries write, exec, or secrets-read verbs. Because every principal that presents a valid credential to the API server — including default ServiceAccount tokens mounted into pods, kubelet client certs, and OIDC users — is automatically placed in system:authenticated, binding real permissions to that group grants them cluster-wide to anything that can talk to the API, with zero further authorisation checks.

The Default State

Kubernetes ships three sanctioned bindings to this group: system:discovery, system:basic-user, and system:public-info-viewer, all deliberately read-only and metadata-only. The trap appears when teams migrating off ABAC, or debugging a broken RoleBinding, run something like kubectl create clusterrolebinding quick-fix --clusterrole=edit --group=system:authenticated to unblock a CI pipeline, intending it as temporary. It is never removed. Some older Helm charts for cluster add-ons also shipped ClusterRoleBindings scoped this way to avoid maintaining per-namespace ServiceAccount lists, which is functionally identical to disabling RBAC for that ClusterRole’s verbs.

The Blast Radius

Every pod’s default ServiceAccount token — mounted automatically unless automountServiceAccountToken: false is set — now authenticates as a member of system:authenticated. A single RCE in any low-privilege workload lets the attacker use that in-pod token to list secrets, patch deployments, or exec into pods across every namespace, because the binding does not distinguish between a human OIDC login and a ServiceAccount from a public-facing web frontend. RBAC audit tooling that checks kubectl auth can-i per ServiceAccount subject often misses this because the grant is inherited via group membership, not a direct subject binding, so it doesn’t surface in per-account permission reports unless the reviewer explicitly enumerates group-based bindings.

The Lead Mechanic Fix

Run kubectl get clusterrolebindings,rolebindings -A -o json | jq '.items[] | select(.subjects[]?.name=="system:authenticated" or .subjects[]?.name=="system:unauthenticated")' and treat any result referencing a ClusterRole other than the three built-in read-only ones as an incident. Delete the binding, then re-grant access explicitly to named ServiceAccounts or OIDC groups with least-privilege ClusterRoles. Add an OPA Gatekeeper or Kyverno ClusterPolicy that rejects any RoleBinding or ClusterRoleBinding admission where subjects[].name equals system:authenticated unless the referenced role is explicitly allow-listed by name.