Kubernetes RBAC least privilege: from admin to scoped roles
Replace blanket cluster-admin with namespaced Roles, audit who can do what, and test access with kubectl auth can-i.
Most clusters accumulate cluster-admin bindings the way garages accumulate boxes — a CI service account here, a dashboard SA there, a break-glass group that never got cleaned up. RBAC least privilege means every human and service account gets exactly the verbs and resources it needs, in exactly the namespaces it operates in. The proof is not a spreadsheet; it is kubectl auth can-i.
The goal is not zero cluster-admin forever — someone needs break-glass — but every binding should be intentional, scoped, and auditable. Start by listing what a service account can actually do today, then replace blanket grants with namespaced Roles. The Kubernetes administration course covers RBAC mechanics if Role versus ClusterRole still feels fuzzy.
The token is identity. RBAC is permission. A leaked long-lived token is dangerous — prefer projected, short-lived, audience-bound JWTs.
Audit first, tighten second, test third. Skipping the audit means you revoke something production depends on at 2am.
Audit what a service account can do today
Before you write a single Role, see the world through the service account's eyes. kubectl auth can-i --list prints every allowed verb/resource pair. If you see * in the Verbs column, you have work to do.
kubectl auth can-i --list --as=system:serviceaccount:shop:webResources Verbspods [get list watch]configmaps [get]if this prints * [*] you have work to doPrefer namespaced Role over ClusterRole
A Role applies to one namespace. A ClusterRole applies cluster-wide. Default to Role unless the workload genuinely needs cluster-scoped resources like Nodes or PersistentVolumes. Grant specific verbs on specific resources — no wildcards.
apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata:namespace: shopname: web-readerrules:- apiGroups: [""]resources: [pods, configmaps]verbs: [get, list, watch]---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata:namespace: shopname: web-readersubjects:- kind: ServiceAccountname: webnamespace: shoproleRef:kind: Rolename: web-readerapiGroup: rbac.authorization.k8s.io
Find who still has cluster-admin
ClusterRoleBindings are the dangerous ones — they grant cluster-wide power. Every name in this list is a full-cluster takeover if compromised.
kubectl get clusterrolebindings -o wide | grep cluster-adminNAME ROLE SUBJECTScluster-admin ClusterRole/cluster-admin Group system:mastersjenkins-deployer ClusterRole/cluster-admin SA ci:jenkinsdefault-dashboard ClusterRole/cluster-admin SA kube-system:dashboardevery name here is a full-cluster takeover if compromisedUse Role aggregation sparingly
ClusterRoles like view, edit, and admin exist for human operators and are composed through RoleBinding to a namespaced Role. That is fine for developers who need broad namespace access. For automation — CI deployers, operators, controllers — never bind edit or admin; write a Role with exactly the verbs that controller calls in its reconcile loop.
Test before and after with auth can-i
Run the negative test explicitly. A reader that cannot delete pods but can read Secrets is still over-privileged. Check every verb that would hurt if an attacker had it. Document the intended access matrix in the Role's description or in your GitOps repo so reviewers know why each verb is there.
# should be yes — the Role grants readkubectl auth can-i get pods \--as=system:serviceaccount:shop:web -n shop# should be no — delete is not in the Rolekubectl auth can-i delete pods \--as=system:serviceaccount:shop:web -n shop
Where this goes next
RBAC controls who can call the API. NetworkPolicy controls who can talk on the wire. Admission controllers like Gatekeeper enforce what objects can exist at all. The Kubernetes security (CKS) path walks all three layers with hands-on labs.
Go deeper in a courseKubernetes security (CKS-aligned)RBAC, Pod Security, admission control and runtime hardening.View course