Files
Anthropic-Cybersecurity-Skills/skills/auditing-kubernetes-rbac-permissions/scripts/agent.py
T
mukul975 c21af3347e Complete folder anatomy for all 649 cybersecurity skills + update LICENSE to Mahipal
- Add scripts/agent.py and references/api-reference.md to all remaining skills
- Update all 648 LICENSE files: copyright now reads 'Mahipal'
- Add implementing-security-monitoring-with-datadog (new skill with full anatomy)
- All 649 skills now have: SKILL.md, LICENSE, scripts/agent.py, references/api-reference.md
2026-03-11 00:22:12 +01:00

111 lines
5.0 KiB
Python

#!/usr/bin/env python3
"""Kubernetes RBAC Audit Agent - Audits cluster RBAC permissions for security misconfigurations."""
import json
import logging
import argparse
from datetime import datetime
from kubernetes import client, config
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)
DANGEROUS_VERBS = {"*", "create", "delete", "patch", "update", "escalate", "bind", "impersonate"}
DANGEROUS_RESOURCES = {"secrets", "pods/exec", "pods/attach", "serviceaccounts", "clusterroles", "clusterrolebindings", "roles", "rolebindings", "*"}
def load_kube_config(kubeconfig=None):
"""Load Kubernetes configuration."""
if kubeconfig:
config.load_kube_config(config_file=kubeconfig)
else:
try:
config.load_incluster_config()
except config.ConfigException:
config.load_kube_config()
return client.RbacAuthorizationV1Api(), client.CoreV1Api()
def audit_cluster_roles(rbac_api):
"""Audit ClusterRoles for overly permissive rules."""
findings = []
roles = rbac_api.list_cluster_role()
for role in roles.items:
if role.metadata.name.startswith("system:"):
continue
for rule in (role.rules or []):
verbs = set(rule.verbs or [])
resources = set(rule.resources or [])
api_groups = rule.api_groups or [""]
if "*" in verbs and "*" in resources:
findings.append({"role": role.metadata.name, "type": "ClusterRole", "issue": "Full wildcard access (*/*)", "severity": "critical", "rule": {"verbs": list(verbs), "resources": list(resources)}})
elif verbs & DANGEROUS_VERBS and resources & DANGEROUS_RESOURCES:
findings.append({"role": role.metadata.name, "type": "ClusterRole", "issue": f"Dangerous permission: {verbs & DANGEROUS_VERBS} on {resources & DANGEROUS_RESOURCES}", "severity": "high", "rule": {"verbs": list(verbs), "resources": list(resources)}})
logger.info("Audited %d ClusterRoles, %d findings", len(roles.items), len(findings))
return findings
def audit_role_bindings(rbac_api):
"""Audit ClusterRoleBindings for excessive privilege grants."""
findings = []
bindings = rbac_api.list_cluster_role_binding()
for binding in bindings.items:
if binding.metadata.name.startswith("system:"):
continue
role_ref = binding.role_ref
subjects = binding.subjects or []
for subject in subjects:
if role_ref.name in ("cluster-admin", "admin") and subject.kind != "ServiceAccount":
findings.append({"binding": binding.metadata.name, "role": role_ref.name, "subject": f"{subject.kind}/{subject.name}", "severity": "critical" if role_ref.name == "cluster-admin" else "high", "issue": f"{subject.kind} bound to {role_ref.name}"})
if subject.kind == "Group" and subject.name in ("system:unauthenticated", "system:authenticated"):
findings.append({"binding": binding.metadata.name, "role": role_ref.name, "subject": subject.name, "severity": "critical", "issue": f"Broad group {subject.name} bound to {role_ref.name}"})
return findings
def audit_service_accounts(core_api, rbac_api):
"""Audit service accounts for default token mounting and elevated permissions."""
findings = []
sas = core_api.list_service_account_for_all_namespaces()
for sa in sas.items:
if sa.metadata.name == "default":
if sa.automount_service_account_token is not False:
findings.append({"namespace": sa.metadata.namespace, "service_account": "default", "issue": "Default SA auto-mounts token", "severity": "medium"})
return findings
def generate_report(role_findings, binding_findings, sa_findings):
"""Generate RBAC audit report."""
all_findings = role_findings + binding_findings + sa_findings
critical = [f for f in all_findings if f.get("severity") == "critical"]
report = {
"timestamp": datetime.utcnow().isoformat(),
"total_findings": len(all_findings),
"critical": len(critical),
"role_findings": role_findings,
"binding_findings": binding_findings,
"service_account_findings": sa_findings,
}
print(f"RBAC REPORT: {len(all_findings)} findings ({len(critical)} critical)")
return report
def main():
parser = argparse.ArgumentParser(description="Kubernetes RBAC Audit Agent")
parser.add_argument("--kubeconfig", help="Path to kubeconfig file")
parser.add_argument("--output", default="rbac_report.json")
args = parser.parse_args()
rbac_api, core_api = load_kube_config(args.kubeconfig)
role_findings = audit_cluster_roles(rbac_api)
binding_findings = audit_role_bindings(rbac_api)
sa_findings = audit_service_accounts(core_api, rbac_api)
report = generate_report(role_findings, binding_findings, sa_findings)
with open(args.output, "w") as f:
json.dump(report, f, indent=2)
logger.info("Report saved to %s", args.output)
if __name__ == "__main__":
main()