Over-permissioned ServiceAccounts — the most common RBAC failure
What you'll learn
- Identify the most common patterns of over-permissioned ServiceAccounts in production
- Audit a cluster for over-permissioned SAs with `kubectl auth can-i` and audit scripts
- Migrate over-permissioned SAs to minimum-surface Roles
- Recognise the failure modes (Helm chart defaults, default SA bindings, wildcard ClusterRoles)
Prerequisites
Verified against Kubernetes 1.34.x · kubeadm 1.34.x · kubectl 1.34.x · etcd 3.6.x · CoreDNS 1.11.x · containerd 1.7.x / 2.x · 2026-08-16
Over-permissioned ServiceAccounts are the most common
RBAC failure in production Kubernetes. A SA with
cluster-admin is one compromised Pod away from a full
cluster compromise. A SA with * verbs on Pods is one
compromised Pod away from a workload takeover. This
lesson covers the patterns, the audit, and the
migration.
The most common patterns
Five patterns recur in production:
- Helm chart defaults bind to cluster-admin. Many
Helm charts ship with a
ClusterRoleBindingtocluster-adminfor the chart’s SA. The intent is to make the install “just work.” The result is that every install of that chart creates a cluster-admin SA.
# From a typical Helm chart's rbac.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ .Chart.Name }}
subjects:
- kind: ServiceAccount
name: {{ .Chart.Name }}
namespace: {{ .Release.Namespace }}
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
- Wildcard ClusterRole. A chart’s ClusterRole has
apiGroups: ['*'], resources: ['*'], verbs: ['*']. The SA bound to this ClusterRole can do anything. - Default SA with broad binding. A namespace’s
defaultSA is bound to a Role or ClusterRole that allows write operations. Every Pod that does not explicitly use a different SA inherits this. - Operator SA with cluster-wide write. A
monitoring or logging operator SA is bound to a
ClusterRole that allows
create,update,patch,deleteon resources it does not manage. The operator can modify RBAC objects, CustomResourceDefinitions, and admission policies. - CI/CD SA with no minimum-surface discipline. The
CI SA is bound to a
Rolewith all verbs on Deployments and Services, plus read on Secrets. A compromise of the CI runner yields the same.
Auditing over-permissioned SAs
The audit walks every SA and compares its effective permissions to the expected surface:
#!/bin/bash
# audit-sas.sh — find over-permissioned ServiceAccounts
declare -A EXPECTED=(
["ci:ci-runner"]="deployments [get, list, watch]"
["monitoring:prometheus"]="nodes/metrics [] services [get, list, watch]"
["prod:api"]="configmaps [get] pods [get, list, watch]"
)
for ns in $(kubectl get ns -o name | cut -d/ -f2); do
for sa in $(kubectl get sa -n "$ns" -o name | cut -d/ -f2); do
KEY="$ns:$sa"
EFFECTIVE=$(kubectl auth can-i --list -n "$ns" \
--as=system:serviceaccount:$ns:$sa 2>/dev/null)
# Check for the worst patterns
if kubectl auth can-i create clusterrolebindings \
--as=system:serviceaccount:$ns:$sa 2>/dev/null; then
echo "CRITICAL: $KEY can create ClusterRoleBindings"
fi
if kubectl auth can-i '*' '*' \
--as=system:serviceaccount:$ns:$sa 2>/dev/null; then
echo "CRITICAL: $KEY has wildcard RBAC"
fi
done
done
The script reports SAs that can create RBAC objects (privilege escalation primitives) and SAs with wildcard RBAC. Both are Critical findings.
Verifying with kubectl auth can-i
For a single SA, the verification is:
# Effective permissions
kubectl auth can-i --list -n prod \
--as=system:serviceaccount:prod:api
# Worst-case: can it create RBAC objects?
kubectl auth can-i create rolebindings -n prod \
--as=system:serviceaccount:prod:api
# Should be: no
# Worst-case: can it read secrets?
kubectl auth can-i get secrets -n prod \
--as=system:serviceaccount:prod:api
# Should be: no (unless the workload genuinely needs secrets)
Migrating to minimum-surface SAs
The migration has three steps:
- Audit every SA’s effective permissions and compare to the expected surface.
- Document the minimum surface for each SA (the verbs × resources the workload needs).
- Replace the over-permissioned binding with a minimum-surface binding.
# Before: cluster-admin
kind: ClusterRoleBinding
subjects:
- kind: ServiceAccount
name: myapp
namespace: prod
roleRef:
kind: ClusterRole
name: cluster-admin
# After: minimum surface
kind: Role
metadata:
name: myapp-runtime
namespace: prod
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get"]
resourceNames: ["app-config"]
---
kind: RoleBinding
metadata:
name: myapp-runtime
namespace: prod
subjects:
- kind: ServiceAccount
name: myapp
namespace: prod
roleRef:
kind: Role
name: myapp-runtime
The new Role allows the workload to read one specific ConfigMap. The old cluster-admin binding is removed.
Production failure modes
- Helm chart reinstalls the cluster-admin binding. The chart was fixed; the cluster was upgraded; the next chart install re-creates the binding. The fix is to track the chart’s RBAC and re-audit after every install.
- Default SA in a namespace has broad RBAC. A
developer deployed a workload with no SA specified;
it uses
default;defaultis bound tocluster-admin. The workload is now cluster-admin. - Audit script is not run after every Helm install. The audit runs quarterly; the chart installs weekly. The audit always finds stale data. The fix is to run the audit in CI/CD against every Helm install.
- CI/CD SA is over-permissioned to “make things work.” The CI SA has cluster-admin because the team did not want to debug RBAC failures. The fix is to write minimum-surface Roles and update the CI SA’s bindings.
Cross-course references
- The Observability course covers the audit log entries for over-permissioned SA usage.
- The Linux course covers the file permissions on Secret data that an over-permissioned SA can read.
Quiz
Knowledge check · 4 questions
Q1. What is the risk of the `default` ServiceAccount in a namespace having any RBAC binding?
Q2. Many Helm charts ship with a ClusterRoleBinding that grants the chart's SA `cluster-admin`. This is an intentional security feature that should be preserved.
Q3. Your audit script reports that `monitoring:prometheus` is bound to `cluster-admin`. The team investigates: the Prometheus Helm chart's default `ClusterRoleBinding` is the source. The chart was reinstalled last week after an upgrade. The team fixed the chart three months ago by overriding the binding with a custom Role. Why is the SA still bound to cluster-admin?
The team has a `values.yaml` override that sets `clusterRoleBinding.create: false` and provides a custom Role. The override was applied. Three months later, a Helm upgrade was performed by a different operator. The override was preserved in the Helm release, but the chart's default `ClusterRoleBinding` was re-created.
Q4. Describe the three-step migration from an over-permissioned SA to a minimum-surface SA.
Passing score: 75%. Answers are checked in this browser.
Production discipline
Over-permissioned SAs are the most common RBAC failure because Helm charts, tutorials, and “just make it work” defaults ship with cluster-admin bindings. The discipline is to audit every SA’s effective permissions, document the minimum surface for each, and replace the over-permissioned bindings with minimum-surface bindings. The audit runs in CI/CD against every Helm install, and a quarterly sweep catches the drift. A cluster whose SAs are over-permissioned has an RBAC programme that is not defensible; a cluster whose SAs are minimum-surface has an RBAC programme that is auditable.