PRACTITIONER GUIDE
Practitioner Guide10 min read

Kubernetes RBAC Audit: Finding Overpermissioned Service Accounts, ClusterRole Wildcards, and Least Privilege Remediation

automountServiceAccountToken: false
ServiceAccount and Pod spec field that prevents Kubernetes from automatically mounting a service account token at /var/run/secrets/kubernetes.io/serviceaccount/token inside every pod; should be the default for all pods that do not need to call the Kubernetes API
rakkess
kubectl plugin that generates a matrix showing which verbs (get, list, create, delete, etc.) are allowed for each resource type for the current user or a specified service account; installs via krew with kubectl krew install access-matrix
verbs: ['*']
Kubernetes RBAC wildcard verb permission granting full create/read/update/delete/patch access to all specified resources; commonly found in operator and controller service account roles where least privilege was not applied during development
audit2rbac
tool that reads Kubernetes API audit logs and generates the minimal RBAC Role and RoleBinding that would have allowed exactly the API calls observed, enabling data-driven least privilege remediation rather than manual permission guessing

SponsoredRetool

Retool's new app builder is where AI-generated code ships safely

Building apps with AI is easy. Getting them to production safely is another story.

Start building for free today

Audited a three-year-old Kubernetes cluster for a client preparing for SOC 2 and found 23 service accounts with cluster-admin ClusterRoleBindings. Eleven of those service accounts were attached to active production workloads. The remaining twelve were orphaned — the Deployments had been deleted but the service accounts and bindings remained. Four of the eleven active cluster-admin service accounts were for monitoring tools (Prometheus, Grafana) that needed read access to metrics endpoints, not unrestricted cluster management. The cluster-admin bindings had been created during initial setup when the monitoring stack was being configured and nobody went back to narrow the permissions.

This is the normal state of K8s RBAC in clusters that were built under deadline pressure. The problem is not negligence — it is that creating least-privilege RBAC policies requires knowing exactly what API calls each application makes, which is not documented in most Helm charts and not obvious from application logs. audit2rbac, rakkess, and structured audit processes exist to make this discoverable and fixable without requiring application developers to enumerate their API call patterns from memory.

Discovery: building a complete picture of who can do what

Kubernetes RBAC discovery requires assembling three separate data sources: the RBAC resources themselves (Roles, ClusterRoles, RoleBindings, ClusterRoleBindings), the workloads that reference each service account, and the actual API calls each workload makes from audit logs. Any one of these sources in isolation gives an incomplete picture — the RBAC resources show what is permitted, audit logs show what is actually used, and workload relationships show what is at risk if a specific service account is compromised.

Export all RBAC resources to a single file for offline analysis and diffing between audit cycles

Export the complete RBAC configuration to a structured YAML file for documentation and cross-audit comparison using kubectl get to extract all four resource types in one command: kubectl get roles,clusterroles,rolebindings,clusterrolebindings -A -o yaml > rbac-audit-$(date +%Y%m%d).yaml. Store the dated YAML export in a version-controlled repository so that the diff between two consecutive audit cycles shows every RBAC change made in the period — new ClusterRoleBindings added, roles that had permissions expanded, and service accounts whose bindings changed. The git diff output between two consecutive audit exports is the most direct evidence of RBAC configuration drift. Use yq to parse and query the exported YAML for specific patterns: yq '.items[] | select(.kind=="ClusterRoleBinding") | select(.roleRef.name=="cluster-admin")' rbac-audit-20260714.yaml lists all cluster-admin ClusterRoleBindings without requiring kubectl access to the live cluster.

Map service accounts to their Pods and Deployments to understand the blast radius of each overpermissioned binding

Map each service account found with excessive permissions to its consuming workloads by joining the service account name against pod specs to understand what process runs with that identity and what network access it has. Run kubectl get pods -A -o json | jq '.items[] | {namespace: .metadata.namespace, pod: .metadata.name, serviceAccount: .spec.serviceAccountName, containers: [.spec.containers[].image]}' to produce a list of all pods with their service account names and container images. Join this output against the RBAC audit findings to produce a combined risk picture: a cluster-admin service account used by a pod with a public-facing network service (ingress controller, API gateway) is a critical risk because a remote code execution vulnerability in that service gives an attacker cluster-admin access. A cluster-admin service account used by a pod with no network listeners and no public exposure is a lower priority. Prioritize remediation by combining permission level with network exposure to focus effort on the highest-impact findings first.

Remediation: building least-privilege roles for real workloads

Least-privilege RBAC remediation has one reliable pattern: start from what the application actually does rather than from what you think it needs. Applications are routinely given broader permissions than required during development because the developer did not know exactly which API calls were needed, or because a wider permission simplified testing. The audit log approach — observe actual API calls then generate the minimum permission set — produces roles that pass real-world application testing without the permission excess that makes a compromised pod into a cluster takeover.

Replace cluster-admin bindings for monitoring tools with read-only ClusterRoles scoped to specific metric and health resources

Prometheus and similar monitoring tools are the most common cluster-admin binding false positives — they need read access to pods, nodes, services, and endpoints for service discovery, but not write access to any resource. Create a replacement ClusterRole with the specific resources Prometheus actually reads: rules covering get, list, watch verbs for pods, nodes, services, endpoints, namespaces, configmaps (for some service discovery configurations), and the metrics.k8s.io API group. Apply this ClusterRole with a ClusterRoleBinding for the Prometheus service account, then remove the cluster-admin binding. Monitor the Prometheus pod logs for permission denied errors after the change — if Prometheus uses any API resource that was not included in the replacement ClusterRole, it logs an error identifying exactly which resource was denied, enabling targeted addition of that specific resource to the role without re-expanding to cluster-admin. This targeted debug loop converges on the exact minimum ClusterRole in one or two iterations in most cases.

Use Kyverno admission policies to enforce RBAC standards and block future permission escalation

Deploy Kyverno ClusterPolicies that enforce RBAC standards as admission controls, preventing new ClusterRoleBindings with cluster-admin roleRef and Pods that mount tokens from the default ServiceAccount without explicit annotation. A Kyverno validate rule that matches ClusterRoleBinding resources and applies a deny condition when roleRef.name equals cluster-admin blocks the creation of new cluster-admin bindings at admission time, before they reach the cluster state. Add a mechanism for legitimate exceptions: a label or annotation (rbac-exception: approved-by-security-team) on the ClusterRoleBinding resource that the Kyverno policy checks before applying the deny rule. This pattern shifts cluster-admin governance from periodic detection (finding the bindings after they have been running for months) to point-of-creation prevention, while preserving an exception path for legitimate use cases that require documented approval. The admission policy becomes a continuous enforcement complement to the periodic RBAC audit.

Free daily briefing

Briefings like this, every morning before 9am.

Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.

The bottom line

Kubernetes RBAC least privilege starts with a complete discovery of all ClusterRoleBindings for cluster-admin subjects, wildcard verb/resource permissions in ClusterRoles, and service accounts with automounted tokens in Pods that make no Kubernetes API calls. Use rakkess for permission matrix visualization and audit2rbac for generating data-driven least privilege roles from actual API call logs. Prioritize remediation by combining permission level with network exposure of the consuming Pod. Replace cluster-admin bindings for monitoring and observability tools with specific read-only ClusterRoles. Disable automountServiceAccountToken on ServiceAccounts and Pods that have no Kubernetes API access requirements. Scope ClusterRole permissions to specific namespaces using RoleBindings rather than cluster-wide ClusterRoleBindings where possible. Deploy Kyverno admission policies to enforce RBAC standards continuously between audit cycles.

Frequently asked questions

How do I find all service accounts with cluster-admin bindings in my Kubernetes cluster?

Find all service accounts with cluster-admin ClusterRoleBindings using kubectl to extract and filter the binding subjects. Run kubectl get clusterrolebindings -o json | jq '.items[] | select(.roleRef.name=="cluster-admin") | {name: .metadata.name, subjects: .subjects}' which outputs all ClusterRoleBindings referencing the cluster-admin ClusterRole with their subjects. Filter the output for ServiceAccount kind subjects to identify service accounts with cluster-admin access: kubectl get clusterrolebindings -o json | jq '.items[] | select(.roleRef.name=="cluster-admin") | .subjects[]? | select(.kind=="ServiceAccount") | {namespace: .namespace, name: .name}'. Each result is a service account with unrestricted access to all Kubernetes API operations in all namespaces — the equivalent of root access to the entire cluster. Also check namespace-scoped RoleBindings for cluster-admin (possible but unusual): kubectl get rolebindings -A -o json | jq '.items[] | select(.roleRef.name=="cluster-admin") | {namespace: .metadata.namespace, subject: .subjects}'. For each cluster-admin service account found, identify the Deployment or Pod that uses it via kubectl get pods -A -o json | jq and trace it to its owning application to determine the minimum permissions that application actually requires.

How do I use rakkess to visualize service account permissions?

Use rakkess (access-matrix kubectl plugin) to visualize what verbs a specific service account is allowed to perform across all resource types in a namespace or cluster. Install via krew: kubectl krew install access-matrix. Run kubectl access-matrix --sa NAMESPACE/SERVICE_ACCOUNT_NAME to see the full permission matrix for a specific service account — the output shows each resource type as a row and each verb (get, list, watch, create, update, patch, delete) as a column, with a checkmark for allowed and an X for denied. The matrix immediately reveals overpermissioned service accounts: a service account for a read-only monitoring application that shows checkmarks for create, update, patch, and delete indicates that the Role or ClusterRole was written with broader permissions than the application needs. Run kubectl access-matrix --namespace production to see the access matrix for the current user in the production namespace, useful for verifying that a role you wrote provides exactly the access you intended. Compare the rakkess output against the actual Kubernetes API calls the application makes (visible in audit logs) to identify which permissions are actually used versus which are granted but never exercised.

How do I disable service account token automounting for pods that do not need API access?

Disable service account token automounting at both the ServiceAccount level and the Pod spec level to ensure that pods without Kubernetes API access requirements do not have a token mounted that could be used to escalate privileges from a compromised container. At the ServiceAccount level, add automountServiceAccountToken: false to the ServiceAccount manifest — this applies to all Pods using that ServiceAccount unless overridden: apiVersion: v1, kind: ServiceAccount, metadata: {name: my-app, namespace: production}, automountServiceAccountToken: false. For Pods using the default ServiceAccount (which you cannot modify without affecting all Pods in the namespace that use the default), add automountServiceAccountToken: false at the Pod spec level: spec: { automountServiceAccountToken: false }. Audit current automounting status across all namespaces with kubectl get pods -A -o json | jq '.items[] | select(.spec.automountServiceAccountToken != false) | {namespace: .metadata.namespace, name: .metadata.name, serviceAccount: .spec.serviceAccountName}' — each result is a Pod that currently mounts a service account token. For applications that genuinely need Kubernetes API access (operators, controllers, service mesh components), keep automounting enabled but ensure the associated ServiceAccount has a Role or ClusterRole limited to exactly the resources and verbs the application requires.

How do I use audit2rbac to generate least privilege RBAC policies from actual API usage?

Use audit2rbac to generate minimal RBAC policies by enabling Kubernetes API audit logging, running the application in a staging environment to capture its actual API calls, then using audit2rbac to generate the exact Role and RoleBinding that permits only those calls. Enable audit logging in the Kubernetes API server configuration (kube-apiserver flags: --audit-log-path=/var/log/kube-audit.log --audit-policy-file=/etc/kubernetes/audit-policy.yaml) with a policy that logs RequestResponse level events for the service account you are auditing. Run the application through its normal operation cycle in staging for 30-60 minutes to capture a representative sample of its API calls. Download audit2rbac from its GitHub releases and run audit2rbac --filename /var/log/kube-audit.log --serviceaccount NAMESPACE/SERVICE_ACCOUNT_NAME which reads the audit log, extracts all API calls made by the service account, and outputs a Role and RoleBinding YAML that permits exactly those calls. Review the generated policy before applying — audit2rbac generates the minimum policy for observed behavior but may miss calls that happen during error conditions or rare code paths. Apply the generated policy to staging, run the full application test suite, and monitor for permission denied errors in the application logs before promoting to production.

How do I find and remediate wildcard permissions in Kubernetes ClusterRoles?

Find wildcard permissions in ClusterRoles by querying for rules containing asterisk characters in verbs or resources arrays. Run kubectl get clusterroles -o json | jq '.items[] | select(.rules[]? | (.verbs[]? == "*") or (.resources[]? == "*")) | .metadata.name' which lists all ClusterRoles with wildcard verb or resource permissions. Exclude system ClusterRoles (which legitimately use wildcards for cluster-admin, cluster-admin-aggregation, and system: prefixed roles) by adding | select(.metadata.name | startswith("system:") | not) to the jq filter. For each wildcard ClusterRole found, inspect the Pods and ServiceAccounts that use it via kubectl get clusterrolebindings -o json | jq and identify the applications. Remediate by replacing the wildcard rules with specific resources and verbs: instead of verbs: ["*"] resources: ["*"], write the specific verbs the application uses (get, list, watch for read-only monitoring; get, list, watch, create, update, patch, delete for controllers that manage a specific resource type) against the specific resources the application accesses. Test the remediated ClusterRole in staging using rakkess to verify the new permissions match expectations before removing the wildcard ClusterRole from production.

How do I scope ClusterRoles to namespaces using RoleBindings instead of ClusterRoleBindings?

Scope ClusterRole permissions to specific namespaces by creating RoleBindings (namespace-scoped) that reference a ClusterRole, rather than ClusterRoleBindings (cluster-scoped) that grant permissions across all namespaces. A ClusterRole defines the permissions (which resources and verbs are allowed) but does not determine the scope — the binding type determines scope. A ClusterRoleBinding grants the ClusterRole's permissions across the entire cluster in all namespaces. A RoleBinding in a specific namespace grants the ClusterRole's permissions only within that namespace. For an operator that manages resources in a single namespace, create a ClusterRole with the required permissions and a RoleBinding in that specific namespace: apiVersion: rbac.authorization.k8s.io/v1, kind: RoleBinding, metadata: {name: my-operator-binding, namespace: production}, roleRef: {apiGroup: rbac.authorization.k8s.io, kind: ClusterRole, name: my-operator-role}, subjects: [{kind: ServiceAccount, name: my-operator, namespace: production}]. This grants the operator permissions only within the production namespace, not cluster-wide. If the operator needs to manage resources in multiple specific namespaces, create separate RoleBindings in each namespace rather than escalating to a ClusterRoleBinding.

How do I implement a Kubernetes RBAC audit as part of a regular security review process?

Implement a Kubernetes RBAC audit process that runs on a recurring 90-day cycle using a structured checklist and automated tooling to identify permission drift between reviews. The checklist covers five areas: cluster-admin bindings (run the jq query for cluster-admin subjects and verify each is documented and justified), wildcard permissions (run the wildcard ClusterRole query and verify each wildcard is documented with a remediation plan or exception), service account token automounting (run the automounting audit query and verify all Pods with mounted tokens have RBAC policies justifying API access), default ServiceAccount usage (kubectl get pods -A -o json | jq to find Pods using the default ServiceAccount, which typically has no RBAC but should be verified), and new ClusterRole/ClusterRoleBinding creations (review kubectl get events or GitOps history for RBAC resource changes since the last audit). Automate the detection phase using a CI/CD pipeline that runs the kubectl and jq queries against a read-only audit kubeconfig daily and exports the results to a spreadsheet or dashboard. Use Kyverno ClusterPolicy with validate rules to enforce RBAC standards continuously: a policy that denies ClusterRoleBinding subjects where roleRef.name == cluster-admin prevents new cluster-admin bindings without going through the exception process, shifting RBAC governance from periodic detection to continuous prevention.

Sources & references

  1. Kubernetes RBAC Documentation
  2. rakkess RBAC Tool
  3. rbac-lookup Tool
  4. Kubernetes Service Account Security

Free resources

25
Free download

Critical CVE Reference Card 2025–2026

25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.

No spam. Unsubscribe anytime.

Free download

Ransomware Incident Response Playbook

Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.

No spam. Unsubscribe anytime.

Free newsletter

Get threat intel before your inbox does.

50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.

Unsubscribe anytime. We never sell your data.

Eric Bang
Author

Founder & Cybersecurity Evangelist, Decryption Digest

Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.

Black Hat Giveaway

Win a $2,495 Black Hat pass.

Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.

Joins Decryption Digest daily briefing. Unsubscribe anytime.

Giveaway: Black Hat USA 2026 Full-Access Pass ($2,495 value)

Details →
Daily Briefing

Subscribe to enter the giveaway

Every subscriber is automatically entered. You also get daily threat intel every morning: zero-days, ransomware, and nation-state campaigns. Free. No spam.

Already subscribed? You're already entered.

Giveaway

Win a $2,495 Black Hat USA 2026 pass.