Files
Anthropic-Cybersecurity-Skills/skills/auditing-kubernetes-cluster-rbac/scripts/agent.py
T
mukul975 27c6414ca5 Add folder anatomy (scripts/agent.py + references/api-reference.md) for 648 cybersecurity skills
Complete skill folder anatomy across all cybersecurity skills:
- scripts/agent.py: 80-150 line Python agents using real libraries (impacket,
  boto3, azure-mgmt-*, kubernetes, pefile, yara, scapy, shodan, stix2, etc.)
- references/api-reference.md: real API documentation with method signatures
- LICENSE: MIT license for all skill folders
2026-03-10 21:02:12 +01:00

192 lines
6.9 KiB
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()