Kubernetes RBAC decides what every user, controller and pod can do against the API server, and a handful of verbs and default bindings quietly turn ordinary permissions into cluster takeover. Most clusters run with dozens of ClusterRoleBindings nobody can explain, service accounts that inherited edit or cluster-admin from an install script, and operators granted wildcards "for now". This guide explains how Role, ClusterRole, RoleBinding and ClusterRoleBinding actually combine, which permissions are equivalent to privilege escalation, how service account tokens are issued and bound, and how to roll out least privilege without breaking workloads. It is written for platform engineers, cluster administrators and security teams who own production Kubernetes, managed or self-hosted.
Key takeaways
- RBAC is additive only. There are no deny rules, so every binding widens access and the only way to remove a permission is to remove or narrow a binding.
- Some verbs are escalation in disguise.
liston Secrets, pod creation,escalate,bind,impersonate,getonnodes/proxyandcreateonserviceaccounts/tokeneach lead to more access than they appear to grant. - Service accounts are the largest identity population. Each one is a non-human identity with a token; bind roles to application-specific service accounts, never to
defaultor to thesystem:serviceaccountsgroup. - Use bound, short-lived tokens. Projected tokens from the TokenRequest API expire (one hour by default) and are bound to the pod; legacy Secret-based tokens never expired and should be hunted down.
- system:masters bypasses everything. Membership skips RBAC and authorisation webhooks and cannot be revoked by deleting bindings, so treat any credential carrying it as break-glass only.
How Kubernetes RBAC works
The RBAC authoriser uses four objects in the rbac.authorization.k8s.io API group. It is enabled by listing the RBAC authoriser in the file passed to --authorization-config, or in older configurations with --authorization-mode=...,RBAC. Managed services (EKS, AKS, GKE) enable it by default, usually alongside their own cloud IAM authoriser or mapping layer.
| Object | Scope | What it does | Typical use |
|---|---|---|---|
| Role | One namespace | Lists rules (apiGroups, resources, verbs, optional resourceNames) | Application or team permissions inside its own namespace |
| ClusterRole | Cluster | Same rules, plus cluster-scoped resources (nodes, namespaces, PersistentVolumes) and non-resource URLs | Reusable permission sets; cluster-wide operators |
| RoleBinding | One namespace | Grants a Role, or a ClusterRole limited to that namespace, to subjects | Granting view or edit in one namespace |
| ClusterRoleBinding | Cluster | Grants a ClusterRole across all namespaces | Controllers, node components, genuine cluster administrators |
- Subjects: users and groups come from the authenticator (client certificates, OIDC, cloud IAM mapping); Kubernetes has no user objects. Service accounts are named
system:serviceaccount:<namespace>:<name>and belong to the groupssystem:serviceaccounts:<namespace>andsystem:serviceaccounts. - roleRef is immutable: changing which role a binding points to fails validation; you delete and recreate the binding. This is deliberate, so that
updateon a binding cannot silently swap in a more powerful role. - Aggregated ClusterRoles: a ClusterRole with an
aggregationRulehas its rules filled in by the control plane from every ClusterRole matching a label selector. The built-inadmin,editandviewroles aggregate any ClusterRole labelledrbac.authorization.k8s.io/aggregate-to-admin,-editor-view, which means installing a CRD or operator can silently widen what everyeditholder can do. - Wildcards:
"*"in resources or verbs covers object types created in the future, including CRDs added by later installs.
Default roles and what they really grant
API servers create default ClusterRoles and bindings labelled kubernetes.io/bootstrapping=rbac-defaults. On every start-up the API server re-adds missing permissions and subjects to them (auto-reconciliation) unless the annotation rbac.authorization.kubernetes.io/autoupdate is set to false, so hardening edits to system: roles are overwritten unless you opt out, and opting out can break clusters.
| ClusterRole | Default binding | Hidden reach |
|---|---|---|
cluster-admin | system:masters group | Any action on any resource. Bound with a RoleBinding it is still full control of that namespace, including the namespace object itself. |
admin | None | Read/write on most namespaced resources, including creating Roles and RoleBindings in the namespace. No write to ResourceQuota or the namespace itself. |
edit | None | Cannot view or modify roles, but can read Secrets and run pods as any service account in the namespace, so it holds the API access of every service account there. |
view | None | Read-only on most objects; deliberately excludes Secrets because reading them would expose service account credentials. |
system:basic-user, system:discovery | system:authenticated | Self-information and API discovery. Before v1.14 also bound to unauthenticated users. |
system:public-info-viewer | system:authenticated and system:unauthenticated | Non-sensitive cluster information. Disable anonymous access entirely with --anonymous-auth=false if you do not need it. |
Default RBAC grants no permissions to service accounts outside kube-system beyond discovery. Every other service account permission in your cluster was added by someone, usually by a Helm chart, an operator manifest or a quick fix. See the Authorisation Models Guide for how RBAC compares with attribute and relationship-based models.
Privilege escalation paths in RBAC
The RBAC API prevents users from creating a Role or binding that grants permissions they do not already hold, unless they have the escalate verb on roles/clusterroles or the bind verb on the referenced role. That protection is narrow. The Kubernetes RBAC good practices page lists permissions that let a subject reach more than its rules say, and the CIS Kubernetes Benchmark (section 5.1) turns most of them into checks.
| Permission | Why it escalates | CIS 5.1 check | ATT&CK |
|---|---|---|---|
get, list or watch on secrets | List and watch responses include full Secret contents, not just names | 5.1.2 | T1552.007 |
create on pods or any workload resource | A pod can mount any Secret, ConfigMap or volume in the namespace and run as any service account there; privileged pods reach the node | 5.1.4 | T1610, T1611 |
create on PersistentVolumes | Allows hostPath volumes, giving access to the node filesystem and kubelet credentials | 5.1.9 | T1611 |
get on nodes/proxy | Kubelet API access, including exec into pods on that node, bypassing API audit logging and admission control; get is not read-only here | 5.1.10 | T1609 |
escalate, bind, impersonate | Create roles beyond your own rights, bind roles you do not hold, or act as another user, group or service account | 5.1.8 | T1098.006 |
create CSRs plus update on certificatesigningrequests/approval (signer kubernetes.io/kube-apiserver-client) | Mint client certificates with any name, including system component names | 5.1.11 | T1098.006 |
create on serviceaccounts/token | Issue tokens for any existing service account in scope | 5.1.13 | T1528 |
Write on validatingwebhookconfigurations or mutatingwebhookconfigurations | Read every admitted object; mutating webhooks can alter them | 5.1.12 | T1098.006 |
patch on Namespaces | Change labels that Pod Security Admission or NetworkPolicy rely on, weakening enforcement for that namespace | None | None mapped |
| Wildcard rules | Cover every current and future resource and verb | 5.1.3 | T1098.006 |
Two structural points follow. First, namespace boundaries are weak for anyone who can create workloads: treat "can create pods in namespace X" as "holds every credential in namespace X". Second, a user or service account name that is deleted and later recreated inherits any bindings that still reference it, so remove bindings when identities are deprovisioned. The patch on Namespaces risk is described by Kubernetes but has no dedicated CIS check or ATT&CK technique; enforce it through admission policy. See the Kubernetes Admission Control Guide.
Service accounts as non-human identities
Every namespace gets a default service account and every pod runs as one. That makes service accounts the bulk of identities in a cluster and the ones attackers find first, because a compromised container already holds its token at /var/run/secrets/kubernetes.io/serviceaccount/token unless mounting is disabled. Kubernetes documents five ways to grant service account permissions, from most to least secure:
- Application-specific service account (best practice): set
serviceAccountNamein the pod spec and bind a narrow Role tosystem:serviceaccount:my-namespace:my-sa. - The namespace
defaultservice account: every pod withoutserviceAccountNamegets these permissions. Avoid; CIS 5.1.5 checks that default service accounts are not actively used. - All service accounts in a namespace via the group
system:serviceaccounts:my-namespace. - All service accounts cluster-wide via
system:serviceaccounts(discouraged). - cluster-admin to all service accounts (strongly discouraged): Kubernetes warns this gives any user who can read Secrets or create any pod full control of the cluster.
Token types and lifetimes
| Token | How issued | Lifetime and binding | Action |
|---|---|---|---|
| Projected (bound) token | Kubelet requests it through the TokenRequest API (stable since v1.22) and mounts it in a projected volume | Expires when the pod is deleted or after its lifespan (default one hour; the kubelet refreshes it), audience is the API server, bound to the pod; node-bound tokens are GA in v1.33 | Default and preferred. No individual revocation: delete the pod to expire its tokens |
kubectl create token | TokenRequest from a client | Time-limited; can be bound to a Pod or other object with --bound-object-kind | Use for short-lived human or job access instead of long-lived Secrets |
Legacy Secret token (kubernetes.io/service-account-token) | Auto-created before v1.24, or created manually since | Never expires. Since v1.29 unused auto-generated tokens are marked invalid after one year (label kubernetes.io/legacy-token-invalid-since) and later purged | Inventory, check kubernetes.io/legacy-token-last-used, migrate and delete |
- Disable automount where the API is not needed: set
automountServiceAccountToken: falseon the ServiceAccount or the pod (the pod setting wins). Most application pods never call the Kubernetes API. CIS 5.1.6 checks this. - Cloud credentials ride on the same identity: EKS, AKS and GKE workload identity features exchange the projected service account token for cloud credentials, so a service account bound to a cloud role is a cloud identity too. See the Workload Identity Federation Guide.
- Ownership: record an owner, purpose and expected API calls for every non-system service account, exactly as for any other machine identity. See the Kubernetes NHI Security Guide and the Kubernetes Service Account Token Guide.
Designing roles: patterns that hold up
Pick the pattern by who the subject is and how many clusters you run.
| Subject | Recommended pattern | Avoid |
|---|---|---|
| Application workload | Dedicated service account, namespaced Role naming exact resources and resourceNames where possible; automount off unless needed | Binding to default; reusing one service account for several apps |
| Developers | IdP groups bound to view cluster-wide and edit only in their team namespaces, via RoleBinding | Per-user bindings; edit in shared namespaces holding other teams' Secrets |
| Platform administrators | Low-privilege day-to-day identity with impersonate scoped to an admin group, or just-in-time elevation through the cloud provider or a PAM tool | Standing cluster-admin; shared kubeconfigs with system:masters certificates |
| Operators and controllers | ClusterRole limited to the CRDs and core resources they reconcile; read Secrets only in named namespaces | Accepting upstream manifests with "*" rules or cluster-wide Secret access without review |
| CI/CD deployers | One service account per pipeline and target namespace, short-lived tokens or federated cloud credentials | A cluster-wide deployer with cluster-admin; long-lived kubeconfig stored as a CI secret |
| Monitoring and security tools | Custom read-only ClusterRole without Secrets and without nodes/proxy | Binding to cluster-admin because the vendor documentation said so |
Small versus large estates: a single-team cluster can manage RBAC as reviewed YAML in Git and apply it with kubectl auth reconcile. Multi-tenant and multi-cluster estates need group-based bindings from the identity provider, a policy engine (ValidatingAdmissionPolicy, OPA Gatekeeper or Kyverno) blocking dangerous rules at admission, and a central inventory of bindings across clusters. In managed services, remember that cloud IAM (for example EKS access entries or AKS Entra ID integration) decides who reaches the cluster, while Kubernetes RBAC decides what they can do once there; review both.
Rolling out least privilege
- Inventory: export all ClusterRoleBindings and RoleBindings with their subjects (
kubectl get clusterrolebindings,rolebindings -A -o json), and list who holdscluster-admin, members ofsystem:mastersin issued certificates or cloud mappings, and any bindings tosystem:unauthenticatedorsystem:serviceaccounts. - Find escalation holders: for each risky permission in the table above, enumerate subjects with
kubectl auth can-i(for examplekubectl auth can-i list secrets -n prod --as system:serviceaccount:prod:app-sa) or an RBAC analysis tool. Fix bindings todefaultservice accounts and cluster-wide service account groups first. - Turn on audit logging: pass an audit policy with
--audit-policy-file(no file means no events). Log atMetadatalevel broadly, and atRequestResponsefor RBAC objects,serviceaccounts/tokenand webhook configurations. Avoid logging Secret bodies. - Derive real usage: collect 30 days of audit events per service account and user, and build replacement Roles from the verbs and resources actually used.
- Pilot: apply new Roles in one non-production namespace with
kubectl auth reconcile -f rbac.yaml --dry-run=clientfirst, then for real. Watch audit logs for403responses from the affected subjects. - Remove the old grants: once a pilot ring runs clean for a release cycle, apply
kubectl auth reconcilewith--remove-extra-subjects --remove-extra-permissions, and setautomountServiceAccountToken: falseon service accounts that never call the API. - Prevent regression: enforce admission rules that reject new ClusterRoleBindings to
cluster-admin, wildcard rules, and bindings todefaultservice accounts, starting in audit or warn mode before deny. - Rollback: keep the previous RBAC manifests in Git and reapply them if a workload breaks; because RBAC is additive, rollback is re-adding a binding, which is instant and does not require restarting pods.
Detection and measurement
RBAC changes and token activity are visible only in API server audit logs, so ship them off the control plane. On managed services enable the provider's control plane audit log export.
- Binding changes:
create,updateorpatchonclusterrolebindingsandrolebindings, especially any referencingcluster-admin,adminoredit(ATT&CK T1098.006). - Token minting:
createonserviceaccounts/tokenby anything other than the kubelet or known controllers. - Secret enumeration:
listorwatchon Secrets across namespaces from workload service accounts (T1552.007). - Discovery from pods: bursts of
SelfSubjectAccessRevieworSelfSubjectRulesReviewrequests, which is whatkubectl auth can-i --listproduces when run by an intruder inside a container (T1613). - Use from unexpected places: a service account token presented from an IP outside the pod network, or a user agent such as
kubectlon a workload identity (T1078.004 where the cluster is a cloud service).
| Metric | Target |
|---|---|
Subjects bound to cluster-admin (excluding break-glass) | Fewer than five per cluster, each with a named owner |
| Service accounts holding any escalation permission from the table above | Trending to zero outside kube-system and reviewed operators |
Pods running as a namespace default service account with bindings | Zero |
Legacy kubernetes.io/service-account-token Secrets | Zero, or each with a documented exception and expiry |
| Pods with automounted tokens that never call the API | Falling each quarter |
| Bindings reviewed in the last 90 days | 100% of ClusterRoleBindings, risk-based sample of RoleBindings |
Framework mapping
| Framework | Reference | How RBAC evidence supports it |
|---|---|---|
| NIST SP 800-53 Rev. 5 | AC-2 Account Management, AC-3 Access Enforcement, AC-5 Separation of Duties, AC-6 Least Privilege, IA-5 Authenticator Management, AU-2 Event Logging, AU-6 Audit Record Review | Binding inventory, scoped Roles, token lifetime policy, audit policy and review records |
| NIST CSF 2.0 | PR.AA-01, PR.AA-05, DE.CM-09 | Managed service account identities; least privilege permissions reviewed; runtime monitoring of the API |
| CIS Kubernetes Benchmark | Section 5.1, checks 5.1.1 to 5.1.13 (numbering unchanged from v1.10 to v2.0) | cluster-admin use, Secret access, wildcards, pod creation, default service accounts, token mounting, system:masters, escalation verbs |
| CIS Controls v8.1 | Control 5 Account Management, Control 6 Access Control Management (6.8 role-based access control) | Service account inventory and role definitions |
| ISO/IEC 27001:2022 Annex A | 5.15 Access control, 5.18 Access rights, 8.2 Privileged access rights | Documented role model, periodic binding reviews, controlled cluster-admin |
| MITRE ATT&CK | T1098.006, T1552.007, T1528, T1609, T1610, T1611, T1613, T1078.004 | Detection rules on audit logs |
Common failure patterns
- Mounted tokens with real permissions in exposed workloads: in the Kubeflow cryptomining attacks, Microsoft reported that attackers used the service account mounted in notebook servers to deploy containers after entering through internet-exposed dashboards. An internet-facing pod should have automount off or a service account with no write verbs.
- Cloud keys reachable from the cluster: in the Tesla Kubernetes cryptojacking case, an unauthenticated Kubernetes console led to a pod holding AWS credentials. RBAC that lets a console or pod read Secrets cluster-wide converts one exposed interface into cloud access.
- Granting
editand believing it is safe: teams giveeditbecause it cannot change RBAC, missing that it reads Secrets and runs pods as any service account in the namespace. - Vendor manifests with
cluster-adminor wildcards: agents and operators installed from upstream YAML or charts often ship broad ClusterRoles that nobody reviews. See the Helm Security Guide. - Treating
get nodes/proxyas read-only: monitoring tools are given it for metrics, but it allows exec into pods and bypasses audit logging and admission control. - Break-glass certificates that never expire: client certificates with
O=system:mastersissued at cluster creation and copied into laptops and CI cannot be revoked through RBAC at all.
Practitioner checklist
- Export every ClusterRoleBinding and RoleBinding and assign an owner to each non-system subject.
- Remove bindings to
system:unauthenticated,system:serviceaccountsand namespacedefaultservice accounts. - Restrict
list/watchon Secrets, pod creation, PersistentVolume creation,nodes/proxy,escalate,bind,impersonate, CSR approval,serviceaccounts/tokenand webhook configuration writes to named subjects. - Give every workload its own service account and set
automountServiceAccountToken: falsewhere the API is not called. - Find and retire legacy
kubernetes.io/service-account-tokenSecrets; use projected tokens orkubectl create token. - Keep
system:masterscredentials offline as break-glass and use impersonation or just-in-time elevation for daily administration. - Review ClusterRoles labelled for aggregation into
admin,editandviewafter every CRD or operator install. - Enable API server audit logging and alert on binding changes, token minting and cross-namespace Secret listing.
- Enforce RBAC rules at admission and run CIS Benchmark section 5.1 checks in CI against every cluster.
Standards and references
- Kubernetes: Using RBAC Authorization
- Kubernetes: Role Based Access Control Good Practices
- Kubernetes: Managing Service Accounts
- Kubernetes: Configure Service Accounts for Pods
- Kubernetes: Auditing
- CIS: Kubernetes Benchmark
- NIST: SP 800-53 Rev. 5
- MITRE ATT&CK: Additional Container Cluster Roles
Related NHI Mgmt Group resources: Kubernetes Security Guide · Kubernetes NHI Security Guide · Kubernetes Service Account Token Guide · Container Runtime Security Guide · Service Account Security Guide