# auditing-kubernetes-cluster-rbac

Auditing Kubernetes cluster RBAC configurations to identify overly permissive roles, wildcard permissions, dangerous ClusterRoleBindings, service account abuse, and privilege escalation paths using kubectl, rbac-tool, KubiScan, and Kubeaudit.

- **Kind:** skill
- **Source:** https://github.com/mukul975/Anthropic-Cybersecurity-Skills
- **Page:** https://forefy.com/skills/0128896b-e2b1-4fba-be3f-e1f3fd73123a
- **API (JSON + files):** https://forefy.com/api/asr/0128896b-e2b1-4fba-be3f-e1f3fd73123a

---

## LICENSE

```

```

## SKILL.md

---
name: auditing-kubernetes-cluster-rbac
description: 'Auditing Kubernetes cluster RBAC configurations to identify overly permissive
  roles, wildcard permissions, dangerous ClusterRoleBindings, service account abuse,
  and privilege escalation paths using kubectl, rbac-tool, KubiScan, and Kubeaudit.

  '
domain: cybersecurity
subdomain: cloud-security
tags:
- cloud-security
- kubernetes
- rbac
- access-control
- eks
- gke
- aks
version: '1.0'
author: mahipal
license: Apache-2.0
nist_csf:
- PR.IR-01
- ID.AM-08
- GV.SC-06
- DE.CM-01
mitre_attack:
- T1098.006
- T1552.007
- T1611
- T1613
- T1078.004
mitre_f3:
  version: '1.1'
  tactics:
  - initial-access
  - positioning
  - defense-impairment
  techniques:
  - id: F1033
    name: Insider Access Abuse
    tactic: initial-access
    source: f3
  - id: F1005
    name: Account Manipulation
    tactic: positioning
    source: f3
  - id: F1005.002
    name: 'Account Manipulation: Add Authorized User'
    tactic: positioning
    source: f3
  - id: T1531
    name: Account Access Removal
    tactic: positioning
    source: attack
---

# Auditing Kubernetes Cluster RBAC

## When to Use

- When performing security assessments of Kubernetes clusters (EKS, GKE, AKS, or self-managed)
- When validating that RBAC policies enforce least privilege for users and service accounts
- When investigating potential lateral movement or privilege escalation within a Kubernetes cluster
- When compliance audits require documentation of access controls and permissions
- When onboarding new teams to a shared cluster and defining appropriate RBAC policies

**Do not use** for network policy auditing (use Cilium or Calico network policy tools), for container image scanning (use Trivy or Grype), or for runtime security monitoring (use Falco or Sysdig Secure).

## Prerequisites

- kubectl configured with cluster-admin or equivalent read permissions to the target cluster
- rbac-tool installed (`kubectl krew install rbac-tool` or binary from GitHub)
- KubiScan installed (`pip install kubiscan`)
- Kubeaudit installed (`brew install kubeaudit` or from GitHub releases)
- Access to the cluster's audit logs for correlating RBAC findings with actual API access

## Workflow

### Step 1: Enumerate ClusterRoles and Roles with Dangerous Permissions

Identify roles with wildcard permissions, secret access, pod exec, or escalation capabilities.

```bash
# List all ClusterRoles with wildcard verb access
kubectl get clusterroles -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for role in data['items']:
    name = role['metadata']['name']
    for rule in role.get('rules', []):
        verbs = rule.get('verbs', [])
        resources = rule.get('resources', [])
        if '*' in verbs or '*' in resources:
            print(f'ClusterRole: {name}')
            print(f'  Verbs: {verbs}')
            print(f'  Resources: {resources}')
            print(f'  API Groups: {rule.get(\"apiGroups\", [])}')
            print()
"

# Find roles that can read secrets
kubectl get clusterroles -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for role in data['items']:
    name = role['metadata']['name']
    for rule in role.get('rules', []):
        resources = rule.get('resources', [])
        verbs = rule.get('verbs', [])
        if ('secrets' in resources or '*' in resources) and ('get' in verbs or 'list' in verbs or '*' in verbs):
            if not name.startswith('system:'):
                print(f'ClusterRole: {name} -> can access secrets (verbs: {verbs})')
"

# Find roles with pod/exec permissions (container escape risk)
kubectl get clusterroles -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for role in data['items']:
    name = role['metadata']['name']
    for rule in role.get('rules', []):
        resources = rule.get('resources', [])
        if 'pods/exec' in resources or 'pods/*' in resources:
            print(f'ClusterRole: {name} -> has pods/exec access')
"
```

### Step 2: Audit ClusterRoleBindings and RoleBindings

Review bindings to identify who has elevated access and detect overly broad group assignments.

```bash
# List all ClusterRoleBindings with the subjects
kubectl get clusterrolebindings -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for binding in data['items']:
    name = binding['metadata']['name']
    role = binding['roleRef']['name']
    subjects = binding.get('subjects', [])
    for subject in subjects:
        kind = subject.get('kind', '')
        subj_name = subject.get('name', '')
        ns = subject.get('namespace', 'cluster-wide')
        print(f'{name} -> Role: {role} | {kind}: {subj_name} ({ns})')
" | sort

# Find bindings to cluster-admin
kubectl get clusterrolebindings -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for binding in data['items']:
    if binding['roleRef']['name'] == 'cluster-admin':
        print(f\"Binding: {binding['metadata']['name']}\")
        for subject in binding.get('subjects', []):
            print(f\"  {subject.get('kind')}: {subject.get('name')} (ns: {subject.get('namespace', 'N/A')})\")
"

# Find bindings granting access to all authenticated users
kubectl get clusterrolebindings -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for binding in data['items']:
    for subject in binding.get('subjects', []):
        if subject.get('name') in ['system:authenticated', 'system:unauthenticated']:
            print(f\"WARNING: {binding['metadata']['name']} grants {binding['roleRef']['name']} to {subject['name']}\")
"
```

### Step 3: Scan with rbac-tool for Comprehensive Analysis

Use rbac-tool for automated RBAC analysis including who-can queries and policy generation.

```bash
# Who can get secrets across all namespaces
kubectl rbac-tool who-can get secrets

# Who can create pods (potential for container escape)
kubectl rbac-tool who-can create pods

# Who can exec into pods
kubectl rbac-tool who-can create pods/exec

# Who can escalate privileges (bind/escalate verbs)
kubectl rbac-tool who-can bind clusterroles
kubectl rbac-tool who-can escalate clusterroles

# Generate RBAC policy report
kubectl rbac-tool analysis

# Visualize RBAC relationships
kubectl rbac-tool viz --outformat dot > rbac-graph.dot
dot -Tpng rbac-graph.dot -o rbac-graph.png
```

### Step 4: Run KubiScan for Risky Permissions Detection

Use KubiScan to automatically identify risky service accounts, pods, and RBAC configurations.

```bash
# Run KubiScan to find risky roles
python3 -m kubiscan -rroles   # List risky Roles
python3 -m kubiscan -rcr      # List risky ClusterRoles
python3 -m kubiscan -rrb      # List risky RoleBindings
python3 -m kubiscan -rcrb     # List risky ClusterRoleBindings

# Find risky service accounts
python3 -m kubiscan -rs       # Risky service accounts

# Find pods running with risky service accounts
python3 -m kubiscan -rp       # Risky pods

# Check for privilege escalation paths
python3 -m kubiscan -pe       # Privilege escalation vectors

# Generate full report
python3 -m kubiscan -a        # All checks
```

### Step 5: Audit Service Account Token Mounting and Usage

Check for unnecessary service account token mounts that could enable lateral movement from compromised pods.

```bash
# Find pods with automounted service account tokens
kubectl get pods --all-namespaces -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for pod in data['items']:
    name = pod['metadata']['name']
    ns = pod['metadata']['namespace']
    sa = pod['spec'].get('serviceAccountName', 'default')
    automount = pod['spec'].get('automountServiceAccountToken', True)
    if automount and sa != 'default':
        print(f'{ns}/{name} -> SA: {sa} (token auto-mounted)')
"

# Find service accounts with non-default token secrets
kubectl get serviceaccounts --all-namespaces -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for sa in data['items']:
    name = sa['metadata']['name']
    ns = sa['metadata']['namespace']
    secrets = sa.get('secrets', [])
    if name != 'default' and len(secrets) > 0:
        print(f'{ns}/{name}: {len(secrets)} secret(s) bound')
"

# Check for pods running as privileged or with host access
kubectl get pods --all-namespaces -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for pod in data['items']:
    name = pod['metadata']['name']
    ns = pod['metadata']['namespace']
    for container in pod['spec'].get('containers', []):
        sc = container.get('securityContext', {})
        if sc.get('privileged', False) or sc.get('runAsUser', 1) == 0:
            print(f'RISK: {ns}/{name}/{container[\"name\"]} - privileged={sc.get(\"privileged\",False)} runAsRoot={sc.get(\"runAsUser\",\"not set\")==0}')
"
```

### Step 6: Run Kubeaudit for RBAC and Security Policy Validation

Execute Kubeaudit for comprehensive security checks including RBAC-related findings.

```bash
# Run all kubeaudit checks
kubeaudit all --kubeconfig ~/.kube/config

# Run specific RBAC-related checks
kubeaudit privesc    # Check for allowPrivilegeEscalation
kubeaudit rootfs     # Check for readOnlyRootFilesystem
kubeaudit nonroot    # Check for runAsNonRoot
kubeaudit capabilities  # Check for dangerous capabilities

# Output as JSON for processing
kubeaudit all --kubeconfig ~/.kube/config -f json > kubeaudit-results.json
```

## Key Concepts

| Term | Definition |
|------|------------|
| RBAC | Role-Based Access Control in Kubernetes, a method for regulating access to cluster resources based on the roles of individual users or service accounts |
| ClusterRole | Cluster-wide role definition that specifies permissions (verbs on resources) applicable across all namespaces |
| ClusterRoleBinding | Associates a ClusterRole with subjects (users, groups, service accounts) at the cluster scope |
| Service Account | Identity associated with pods for authenticating to the Kubernetes API server, automatically mounted unless disabled |
| automountServiceAccountToken | Pod spec field controlling whether the service account token is automatically mounted into the pod filesystem |
| Privilege Escalation | RBAC verbs (bind, escalate, impersonate) that allow a user to grant themselves or others elevated permissions |

## Tools & Systems

- **kubectl**: Primary CLI for querying Kubernetes RBAC resources (roles, bindings, service accounts)
- **rbac-tool**: kubectl plugin for RBAC analysis including who-can queries, visualization, and policy generation
- **KubiScan**: Python tool for scanning Kubernetes RBAC for risky permissions and privilege escalation paths
- **Kubeaudit**: Security auditing tool that checks pods and workloads for security anti-patterns including RBAC issues
- **rakkess**: kubectl plugin showing access matrix for the current user across all resource types

## Common Scenarios

### Scenario: Auditing an EKS Cluster Shared by Multiple Development Teams

**Context**: A shared EKS cluster serves four development teams. RBAC was configured during initial setup but has not been reviewed in 12 months. Teams report being able to access other teams' namespaces.

**Approach**:
1. List all ClusterRoleBindings to identify bindings granting broad access to authenticated users
2. Run `kubectl rbac-tool who-can get secrets` to find subjects that can read secrets across namespaces
3. Discover that a ClusterRoleBinding grants `edit` to `system:authenticated`, giving all users write access cluster-wide
4. Run KubiScan to identify service accounts with risky permissions and pods running with elevated service accounts
5. Replace the ClusterRoleBinding with namespace-scoped RoleBindings for each team
6. Disable automountServiceAccountToken for workloads that do not need API access
7. Create a NetworkPolicy to isolate namespace traffic between teams

**Pitfalls**: Removing ClusterRoleBindings can break CI/CD pipelines and operators that rely on cluster-wide access. Always audit which workloads use the bindings before removing them. EKS maps IAM roles to Kubernetes groups via aws-auth ConfigMap, so RBAC changes must be coordinated with IAM role mappings.

## Output Format

```
Kubernetes RBAC Audit Report
===============================
Cluster: production-eks (EKS 1.28)
Audit Date: 2026-02-23
Namespaces: 12

RBAC INVENTORY:
  ClusterRoles: 48 (18 custom, 30 system)
  ClusterRoleBindings: 32 (12 custom, 20 system)
  Roles (namespaced): 24
  RoleBindings (namespaced): 36
  Service Accounts: 67

CRITICAL FINDINGS:
[RBAC-001] ClusterRoleBinding Grants edit to system:authenticated
  Binding: authenticated-edit
  Effect: ALL authenticated users have edit access across ALL namespaces
  Risk: Any user can modify resources in any namespace
  Remediation: Replace with namespace-scoped RoleBindings per team

[RBAC-002] Custom ClusterRole with Wildcard Permissions
  ClusterRole: developer-admin
  Rules: verbs=["*"], resources=["*"], apiGroups=["*"]
  Bindings: 4 users via developer-admin-binding
  Risk: Equivalent to cluster-admin without the name
  Remediation: Scope to specific resources and verbs needed

SUMMARY:
  Principals with cluster-admin: 6 (recommended: <= 3)
  Roles with wildcard permissions: 4
  Service accounts with secret access: 12
  Pods with auto-mounted tokens: 45 / 67
  Privileged containers: 8
```

## references

```

```

## references/api-reference.md

# API Reference: Auditing Kubernetes Cluster RBAC

## kubernetes (Python Client)

### Configuration

```python
from kubernetes import client, config

config.load_kube_config()  # From ~/.kube/config
# or
config.load_incluster_config()  # Inside a pod
```

### List ClusterRoles

```python
rbac = client.RbacAuthorizationV1Api()
roles = rbac.list_cluster_role()
for role in roles.items:
    print(role.metadata.name)
    for rule in role.rules or []:
        print(f"  verbs={rule.verbs} resources={rule.resources}")
```

### List ClusterRoleBindings

```python
bindings = rbac.list_cluster_role_binding()
for b in bindings.items:
    print(b.metadata.name, "->", b.role_ref.name)
    for s in b.subjects or []:
        print(f"  {s.kind}: {s.name}")
```

### List Pods (Security Context)

```python
v1 = client.CoreV1Api()
pods = v1.list_pod_for_all_namespaces()
for pod in pods.items:
    for c in pod.spec.containers:
        sc = c.security_context
        if sc and sc.privileged:
            print(f"PRIVILEGED: {pod.metadata.namespace}/{pod.metadata.name}")
```

## Key RBAC Resources

| Resource | API | Description |
|----------|-----|-------------|
| ClusterRole | `rbac.list_cluster_role()` | Cluster-wide permission definitions |
| ClusterRoleBinding | `rbac.list_cluster_role_binding()` | Binds roles to subjects cluster-wide |
| Role | `rbac.list_namespaced_role(ns)` | Namespace-scoped permissions |
| RoleBinding | `rbac.list_namespaced_role_binding(ns)` | Namespace-scoped binding |
| ServiceAccount | `v1.list_service_account_for_all_namespaces()` | Pod identities |

## Dangerous RBAC Patterns to Detect

| Pattern | Risk |
|---------|------|
| `verbs: ["*"], resources: ["*"]` | Equivalent to cluster-admin |
| `resources: ["secrets"], verbs: ["get"]` | Can read all secrets |
| `resources: ["pods/exec"]` | Can exec into containers |
| `subjects: system:authenticated` | All users get this role |
| `automountServiceAccountToken: true` | Token available in pod |

### References

- kubernetes Python client: https://pypi.org/project/kubernetes/
- K8s RBAC docs: https://kubernetes.io/docs/reference/access-authn-authz/rbac/
- KubiScan: https://github.com/cyberark/KubiScan

## scripts

```

```

## scripts/agent.py

```python
#!/usr/bin/env python3
"""Agent for auditing Kubernetes cluster RBAC configurations."""

import os
import json
import argparse
from datetime import datetime

from kubernetes import client, config


def load_kube_config(kubeconfig=None, context=None):
    """Load Kubernetes configuration."""
    if kubeconfig:
        config.load_kube_config(config_file=kubeconfig, context=context)
    else:
        try:
            config.load_incluster_config()
        except config.ConfigException:
            config.load_kube_config(context=context)


def list_cluster_roles_with_wildcards():
    """Find ClusterRoles with wildcard verb or resource permissions."""
    rbac = client.RbacAuthorizationV1Api()
    roles = rbac.list_cluster_role()
    risky = []
    for role in roles.items:
        for rule in role.rules or []:
            verbs = rule.verbs or []
            resources = rule.resources or []
            if "*" in verbs or "*" in resources:
                risky.append({
                    "name": role.metadata.name,
                    "verbs": verbs,
                    "resources": resources,
                    "api_groups": rule.api_groups or [],
                })
    return risky


def list_secret_access_roles():
    """Find ClusterRoles that can read secrets."""
    rbac = client.RbacAuthorizationV1Api()
    roles = rbac.list_cluster_role()
    results = []
    for role in roles.items:
        for rule in role.rules or []:
            resources = rule.resources or []
            verbs = rule.verbs or []
            if ("secrets" in resources or "*" in resources) and \
               ("get" in verbs or "list" in verbs or "*" in verbs):
                if not role.metadata.name.startswith("system:"):
                    results.append({
                        "role": role.metadata.name,
                        "verbs": verbs,
                        "resources": resources,
                    })
    return results


def list_cluster_admin_bindings():
    """Find all ClusterRoleBindings that grant cluster-admin."""
    rbac = client.RbacAuthorizationV1Api()
    bindings = rbac.list_cluster_role_binding()
    results = []
    for binding in bindings.items:
        if binding.role_ref.name == "cluster-admin":
            subjects = []
            for s in binding.subjects or []:
                subjects.append({
                    "kind": s.kind,
                    "name": s.name,
                    "namespace": s.namespace or "cluster-wide",
                })
            results.append({
                "binding": binding.metadata.name,
                "subjects": subjects,
            })
    return results


def find_dangerous_bindings():
    """Find bindings granting access to system:authenticated or system:unauthenticated."""
    rbac = client.RbacAuthorizationV1Api()
    bindings = rbac.list_cluster_role_binding()
    dangerous = []
    for binding in bindings.items:
        for s in binding.subjects or []:
            if s.name in ("system:authenticated", "system:unauthenticated"):
                dangerous.append({
                    "binding": binding.metadata.name,
                    "role": binding.role_ref.name,
                    "subject": s.name,
                })
    return dangerous


def audit_service_account_tokens():
    """Find pods with automounted service account tokens."""
    v1 = client.CoreV1Api()
    pods = v1.list_pod_for_all_namespaces()
    risky_pods = []
    for pod in pods.items:
        spec = pod.spec
        sa = spec.service_account_name or "default"
        automount = spec.automount_service_account_token
        if automount is not False and sa != "default":
            risky_pods.append({
                "namespace": pod.metadata.namespace,
                "pod": pod.metadata.name,
                "service_account": sa,
                "automount": True,
            })
    return risky_pods


def find_privileged_containers():
    """Find containers running as privileged or root."""
    v1 = client.CoreV1Api()
    pods = v1.list_pod_for_all_namespaces()
    privileged = []
    for pod in pods.items:
        for container in pod.spec.containers or []:
            sc = container.security_context
            if sc:
                is_privileged = getattr(sc, "privileged", False)
                run_as_root = getattr(sc, "run_as_user", None) == 0
                if is_privileged or run_as_root:
                    privileged.append({
                        "namespace": pod.metadata.namespace,
                        "pod": pod.metadata.name,
                        "container": container.name,
                        "privileged": is_privileged,
                        "run_as_root": run_as_root,
                    })
    return privileged


def main():
    parser = argparse.ArgumentParser(description="Kubernetes RBAC Audit Agent")
    parser.add_argument("--kubeconfig", default=os.getenv("KUBECONFIG"))
    parser.add_argument("--context", help="Kubernetes context to use")
    parser.add_argument("--output", default="k8s_rbac_audit.json")
    parser.add_argument("--action", choices=[
        "wildcards", "secrets", "cluster_admin", "dangerous",
        "tokens", "privileged", "full_audit"
    ], default="full_audit")
    args = parser.parse_args()

    load_kube_config(args.kubeconfig, args.context)
    report = {"audit_date": datetime.utcnow().isoformat(), "findings": {}}

    if args.action in ("wildcards", "full_audit"):
        wildcards = list_cluster_roles_with_wildcards()
        report["findings"]["wildcard_roles"] = wildcards
        print(f"[+] Wildcard ClusterRoles: {len(wildcards)}")

    if args.action in ("secrets", "full_audit"):
        secret_roles = list_secret_access_roles()
        report["findings"]["secret_access_roles"] = secret_roles
        print(f"[+] Roles with secret access: {len(secret_roles)}")

    if args.action in ("cluster_admin", "full_audit"):
        admins = list_cluster_admin_bindings()
        report["findings"]["cluster_admin_bindings"] = admins
        total_subjects = sum(len(a["subjects"]) for a in admins)
        print(f"[+] cluster-admin bindings: {len(admins)} ({total_subjects} subjects)")

    if args.action in ("dangerous", "full_audit"):
        danger = find_dangerous_bindings()
        report["findings"]["dangerous_bindings"] = danger
        print(f"[+] Dangerous bindings: {len(danger)}")

    if args.action in ("tokens", "full_audit"):
        tokens = audit_service_account_tokens()
        report["findings"]["automounted_tokens"] = tokens
        print(f"[+] Pods with automounted tokens: {len(tokens)}")

    if args.action in ("privileged", "full_audit"):
        priv = find_privileged_containers()
        report["findings"]["privileged_containers"] = priv
        print(f"[+] Privileged containers: {len(priv)}")

    with open(args.output, "w") as f:
        json.dump(report, f, indent=2, default=str)
    print(f"[+] Report saved to {args.output}")


if __name__ == "__main__":
    main()
```

