kube-system's Default SA Bound to cluster-admin
A cluster-admin ClusterRoleBinding attached to the default service account in kube-system means every pod landing there without an explicit serviceAccountName inherits a live, unscoped API token. RBAC reviews scoped to named accounts miss it entirely, and one compromised kube-system pod becomes full cluster takeover.
Operational summary
At a glance
- Symptom
- Any kube-system pod without an explicit serviceAccountName inherits the default SA's cluster-admin token via automount.
- Likely cause
- A ClusterRoleBinding grants the cluster-admin ClusterRole to system:serviceaccount:kube-system:default.
- Impact
- A single compromised kube-system pod obtains cluster-admin API access, enabling secret theft, privileged pod creation, and RBAC persistence cluster-wide.
- Verification signal
- Re-run the detection test and a controlled negative-path test.
- Safe correction
- Delete the offending ClusterRoleBinding, disable automount on the default SA, and enforce named ServiceAccounts with scoped RoleBindings via a Gatekeeper constraint.
- Rollback or recovery
- Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.
The Trap
A ClusterRoleBinding — commonly named permissive-binding or copied from an old Kubernetes Dashboard install guide — binds the cluster-admin ClusterRole to the subject system:serviceaccount:kube-system:default. This exact command still circulates in bootstrap scripts, Helm post-install hooks, and internal wiki pages: kubectl create clusterrolebinding permissive-binding --clusterrole=cluster-admin --serviceaccount=kube-system:default.
The Default State
Engineers add this binding to unblock a stuck dashboard, metrics-server, or CI runner during initial cluster setup, then never remove it. Because the target is the default service account rather than a named one, it doesn’t show up when access reviews grep for specific ServiceAccount names. Combine this with automountServiceAccountToken left at its cluster-wide default of true, and any pod scheduled into kube-system without an explicit serviceAccountName field silently mounts a cluster-admin-capable token at /var/run/secrets/kubernetes.io/serviceaccount.
The Blast Radius
DaemonSets, log shippers, node-exporter sidecars, and misconfigured operator pods routinely land in kube-system and inherit this SA by omission, not by design. A single container escape or dependency CVE inside any of those pods now has an authenticated, cluster-admin bearer token: it can read every Secret in every namespace, create privileged pods on arbitrary nodes, rewrite RBAC to plant persistence, and exfiltrate node kubelet credentials. OPA/Gatekeeper policies written against workload PodSecurity context rarely inspect ClusterRoleBinding subjects, so this path survives PodSecurity admission entirely and only surfaces in a full RBAC subject audit.
The Lead Mechanic Fix
Run kubectl get clusterrolebindings -o json | jq '.items[] | select(.subjects[]?.name=="default" and .subjects[]?.namespace=="kube-system")' to enumerate offending bindings, then kubectl delete clusterrolebinding permissive-binding. Patch the default SA with kubectl patch serviceaccount default -n kube-system -p '{"automountServiceAccountToken": false}'. Require every workload to declare a named, minimally scoped ServiceAccount bound via RoleBinding rather than ClusterRoleBinding, and add a Gatekeeper ConstraintTemplate that denies any ClusterRoleBinding whose subject kind is ServiceAccount with name default. Validate ongoing drift with rbac-lookup 'system:serviceaccount:kube-system:default' in CI.
Apply the safer control
Before you change production
Confirm the affected scope, export the current configuration, and test the replacement control in a non-production environment first.
Fix commands and configuration
kubectl get clusterrolebindings -o json | jq '.items[] | select(.subjects[]?.name=="default" and .subjects[]?.namespace=="kube-system")'kubectl delete clusterrolebinding permissive-bindingkubectl patch serviceaccount default -n kube-system -p '{"automountServiceAccountToken": false}'Verify, roll back or escalate
Verify
Re-run the detection test and a controlled negative-path test. Confirm the unsafe behaviour is blocked while approved traffic still succeeds.
Rollback
Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.
Escalate
Escalate when the blast radius is uncertain, the control cannot be tested safely, or remediation requires an outage or security exception.