58%
of K8s clusters have a cluster-admin equivalent service account (Palo Alto 2022)
5 phases
to a complete RBAC audit: bindings, wildcards, secrets access, orphans, escalation paths
30 min
estimated time to complete this audit on clusters under 50 namespaces

Kubernetes RBAC is the primary access control layer for your cluster. When it drifts -- through Helm chart defaults, debugging shortcuts, or forgotten bindings -- a single compromised pod can become full cluster compromise. This checklist walks through five audit phases with exact kubectl commands, covering ClusterAdmin bindings, wildcard permissions, secrets access, orphaned service accounts, and privilege escalation paths. Each phase includes remediation one-liners and an output template you can paste directly into a tracking spreadsheet.

Why RBAC Audits Get Skipped (and Why That's a Problem)

Kubernetes RBAC is configured at deployment and rarely revisited. Helm charts grant overly broad permissions for ease of installation. Debugging sessions add temporary bindings that become permanent. The result: most production clusters have at least one service account that can read all secrets or patch deployments cluster-wide. The 2022 Twistlock / Palo Alto Cloud-Native Security Report found that 58% of Kubernetes clusters had at least one service account with cluster-admin or equivalent. A compromised pod with an overprivileged service account becomes immediate cluster compromise.

Pre-Audit Setup: What You Need

You need kubectl configured with cluster-admin access and jq installed for JSON parsing. Confirm access: kubectl auth can-i '' '' --all-namespaces should return 'yes'. For large clusters, run the audit commands against a read-only kubeconfig to avoid accidental writes. Estimated time: 30 minutes for a cluster with fewer than 50 namespaces.

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.

Phase 1: Find ClusterAdmin Bindings (5 Minutes)

This is the highest-severity check. Anything with cluster-admin can read all secrets, delete workloads, and escalate to node compromise.

List all cluster-admin principals:

kubectl get clusterrolebindings -o json | \
  jq -r '.items[] | select(.roleRef.name=="cluster-admin") | 
    "Binding: " + .metadata.name + 
    "  Subjects: " + ([.subjects[]? | .kind + "/" + .name] | join(", "))'

Expected output: Only your break-glass admin users and the system:masters group for control plane components. Flag anything else immediately.

List all RoleBindings and ClusterRoleBindings that reference the cluster-admin role:

kubectl get rolebindings,clusterrolebindings \
  --all-namespaces -o json | \
  jq '.items[] | select(.roleRef.name=="cluster-admin") | 
    {binding: .metadata.name, namespace: .metadata.namespace, subjects: .subjects}'

Phase 2: Find Wildcard Permissions (7 Minutes)

Wildcard verbs ('') or wildcard resources ('') grant more than almost any legitimate workload needs.

Find all ClusterRoles with wildcard verbs:

kubectl get clusterroles -o json | \
  jq '.items[] | select(.rules[]?.verbs[]? == "*") | .metadata.name'

Find all ClusterRoles with wildcard resources:

kubectl get clusterroles -o json | \
  jq '.items[] | select(.rules[]?.resources[]? == "*") | .metadata.name'

Find Roles (namespace-scoped) with wildcards:

kubectl get roles --all-namespaces -o json | \
  jq '.items[] | select(.rules[]?.verbs[]? == "*" or .rules[]?.resources[]? == "*") | 
    {name: .metadata.name, namespace: .metadata.namespace}'

For each result, enumerate what service accounts or users are bound to that role:

kubectl get clusterrolebindings,rolebindings \
  --all-namespaces -o json | \
  jq '.items[] | select(.roleRef.name=="ROLE_NAME_HERE") | 
    {binding: .metadata.name, subjects: .subjects}'

Phase 3: Audit Access to Secrets (5 Minutes)

Any role with get, list, or watch on secrets can read credentials across namespaces. This is the most commonly exploited misconfiguration in container breakout scenarios.

Find roles that can read secrets cluster-wide:

kubectl get clusterroles -o json | \
  jq '.items[] | select(
    .rules[]? | 
    (.resources[]? == "secrets" or .resources[]? == "*") and 
    (.verbs[]? == "get" or .verbs[]? == "list" or .verbs[]? == "*")
  ) | .metadata.name'

Check if the default service account in a namespace can read secrets:

kubectl auth can-i get secrets \
  --as=system:serviceaccount:NAMESPACE:default \
  -n NAMESPACE

This should return 'no' for every namespace. If it returns 'yes', remove or restrict the default service account binding immediately.

Phase 4: Find Unused and Orphaned Service Accounts (5 Minutes)

Service accounts accumulate over time. Each unused account is an attack surface if a token is extracted from a compromised node.

List all service accounts across namespaces:

kubectl get serviceaccounts --all-namespaces \
  -o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name'

Find service accounts with no running pods using them:

# Get all SA names in use
kubectl get pods --all-namespaces -o json | \
  jq -r '.items[] | .metadata.namespace + "/" + .spec.serviceAccountName' | \
  sort -u > /tmp/sa_in_use.txt

# Get all SA names that exist
kubectl get serviceaccounts --all-namespaces -o json | \
  jq -r '.items[] | .metadata.namespace + "/" + .metadata.name' | \
  sort -u > /tmp/sa_all.txt

# Find orphans
comm -23 /tmp/sa_all.txt /tmp/sa_in_use.txt

Filter out the default, kube-system, and cert-manager service accounts, then review the remainder for deletion.

Phase 5: Check for Privilege Escalation Paths (8 Minutes)

Some permissions don't look dangerous in isolation but enable privilege escalation.

Dangerous permission combinations to check for:

PermissionWhy It's Dangerous
create podsCan mount any secret or hostPath
bind verbs on ClusterRolesCan bind cluster-admin to any account
escalate verbCan grant higher privileges than account currently has
impersonate users/groupsCan act as any user including system:masters
patch/update ClusterRoleBindingsCan add subjects to existing high-privilege bindings
get/list nodesReveals node IPs, can aid lateral movement

Check if any service account can create pods (potential secret exfiltration):

for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
  for sa in $(kubectl get sa -n $ns -o jsonpath='{.items[*].metadata.name}'); do
    result=$(kubectl auth can-i create pods \
      --as=system:serviceaccount:$ns:$sa -n $ns 2>/dev/null)
    [ "$result" = "yes" ] && echo "$ns/$sa can create pods"
  done
done

Check for bind/escalate/impersonate permissions:

kubectl get clusterroles -o json | \
  jq '.items[] | select(
    .rules[]?.verbs[]? == "bind" or 
    .rules[]?.verbs[]? == "escalate" or 
    .rules[]?.verbs[]? == "impersonate"
  ) | .metadata.name'

Remediation Playbook: One-Liners for Common Fixes

Remove a ClusterRoleBinding:

kubectl delete clusterrolebinding BINDING_NAME

Remove a subject from a ClusterRoleBinding (edit in place):

kubectl edit clusterrolebinding BINDING_NAME
# Remove the subject entry from the subjects: array

Restrict a wildcard ClusterRole to specific verbs:

kubectl edit clusterrole ROLE_NAME
# Change verbs: ["*"] to verbs: ["get", "list", "watch"]

Delete an unused service account (after confirming no pods use it):

kubectl delete serviceaccount SA_NAME -n NAMESPACE

Disable automounting of service account tokens for a deployment:

# In pod spec or service account spec
automountServiceAccountToken: false

Create a dedicated service account instead of using default:

kubectl create serviceaccount my-app-sa -n my-namespace
# Update deployment to reference it
kubectl set serviceaccount deployment/my-app my-app-sa -n my-namespace

Ongoing RBAC Governance: Preventing Drift

A one-time audit decays quickly. Implement these controls to maintain a clean RBAC posture:

Admission control: Use OPA/Gatekeeper or Kyverno policies to block creation of ClusterRoleBindings granting cluster-admin outside approved namespaces. Example Kyverno policy:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: restrict-cluster-admin
spec:
  validationFailureAction: Enforce
  rules:
    - name: no-cluster-admin-binding
      match:
        resources:
          kinds: [ClusterRoleBinding]
      validate:
        message: "cluster-admin bindings require approval"
        deny:
          conditions:
            - key: "{{request.object.roleRef.name}}"
              operator: Equals
              value: cluster-admin

Alerting: Use Falco rule k8s_cluster_admin_binding to alert on runtime creation of cluster-admin bindings.

Quarterly re-audit: Schedule a recurring audit using this checklist. Any new binding added since the last audit that grants wildcard or cluster-admin permissions requires documented justification.

RBAC Audit Output Template

Copy this table into a Google Sheet or Confluence page after completing the audit:

FindingBinding/Role NameNamespacePrincipals AffectedSeverityRemediationStatus
ClusterAdmin bindingops-bootstrap-bindingcluster-wideSA: ops/bootstrapCriticalDelete bindingOpen
Wildcard verbs on core APIlegacy-app-roleproductionSA: production/legacyHighRestrict to get,listOpen
Secrets read cluster-widemonitoring-readercluster-wideSA: monitoring/prometheusMediumScope to monitoring ns onlyOpen

Target: zero Critical findings, High findings remediated within 7 days, Medium within 30 days.

The bottom line

Kubernetes RBAC drift is silent. Set a quarterly audit cadence, enforce new bindings through admission control, and use Falco runtime alerts to catch any cluster-admin binding created outside of change management. A clean RBAC posture is one of the highest-leverage controls in a containerized environment.

Frequently asked questions

How do I list all ClusterRoleBindings that grant cluster-admin?

Run: kubectl get clusterrolebindings -o json | jq '.items[] | select(.roleRef.name=="cluster-admin") | {name: .metadata.name, subjects: .subjects}'. This shows every principal (user, group, service account) with unrestricted cluster-wide access. Remove any binding that is not a break-glass account.

What kubectl command finds service accounts with wildcard permissions?

kubectl get clusterroles,roles --all-namespaces -o json | jq '.items[] | select(.rules[]?.verbs[]? == "*" or .rules[]?.resources[]? == "*") | .metadata.name'. Follow up with kubectl auth can-i --list --as=system:serviceaccount:NAMESPACE:SA_NAME to enumerate what each flagged account can actually do.

How do I find service accounts that have never been used?

Kubernetes does not track service account last-used natively, but you can audit token creation date via: kubectl get secrets --all-namespaces --field-selector type=kubernetes.io/service-account-token -o json | jq '.items[] | {name: .metadata.name, ns: .metadata.namespace, created: .metadata.creationTimestamp}'. Accounts with tokens created long ago and no associated pods are candidates for deletion.

What is the difference between a Role and a ClusterRole in Kubernetes?

A Role grants permissions within a single namespace. A ClusterRole grants permissions cluster-wide, including non-namespaced resources like nodes, persistent volumes, and namespaces themselves. Prefer Roles over ClusterRoles when access only needs to be scoped to a single namespace.

How do I check what a specific pod's service account can do?

First find the service account: kubectl get pod POD_NAME -n NAMESPACE -o jsonpath='{.spec.serviceAccountName}'. Then audit its permissions: kubectl auth can-i --list --as=system:serviceaccount:NAMESPACE:SA_NAME -n NAMESPACE. For cluster-wide permissions add --all-namespaces.

Which RBAC audit tools automate this process?

rbac-police (open source) evaluates service account permissions against a policy ruleset and flags violations. Kubiscan maps the attack path from low-privilege accounts to cluster-admin via role aggregation. For ongoing monitoring, Falco rules can alert on new ClusterRoleBinding creation events in real time.

What are the most common Kubernetes RBAC misconfigurations in production?

The top five are: (1) service accounts with cluster-admin bound by Helm chart defaults, (2) wildcard verb permissions on core API groups, (3) get/list/watch on secrets cluster-wide (allows reading all credentials), (4) default service account used instead of a dedicated one, (5) bindings created for debugging never removed.

Sources & references

  1. NSA Kubernetes Hardening Guidance
  2. CIS Kubernetes Benchmark
  3. Palo Alto Cloud-Native Security Report
  4. Falco Open Source Runtime Security
  5. MITRE ATT&CK
  6. Wiz State of Cloud Security 2024

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, weekly, 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 weekly.

Free Brief

The Mythos Brief is free.

AI that finds 27-year-old zero-days. What it means for your security program.

Joins Decryption Digest. Unsubscribe anytime.

Daily Briefing

Get briefings like this every morning

Actionable threat intelligence for working practitioners. Free. No spam. Trusted by 50,000+ SOC analysts, CISOs, and security engineers.

Unsubscribe anytime.

Mythos Brief

Anthropic's AI finds zero-days your scanners miss.