mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-26 11:44:37 +03:00
258 lines
9.4 KiB
Python
258 lines
9.4 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Kubernetes RBAC Permissions Auditor
|
|
|
|
Audits RBAC configurations for overly permissive roles,
|
|
dangerous permission combinations, and privilege escalation paths.
|
|
"""
|
|
|
|
import subprocess
|
|
import json
|
|
import sys
|
|
from dataclasses import dataclass, field
|
|
|
|
DANGEROUS_VERBS = {"*", "escalate", "bind", "impersonate"}
|
|
DANGEROUS_RESOURCES = {"*", "secrets", "pods", "clusterroles", "clusterrolebindings", "roles", "rolebindings"}
|
|
HIGH_RISK_COMBINATIONS = [
|
|
({"*"}, {"*"}, "CRITICAL", "Wildcard access on all resources (cluster-admin equivalent)"),
|
|
({"create", "update", "patch"}, {"clusterrolebindings", "rolebindings"}, "CRITICAL", "Can create role bindings for privilege escalation"),
|
|
({"escalate"}, {"clusterroles", "roles"}, "CRITICAL", "Can escalate role permissions beyond own level"),
|
|
({"impersonate"}, {"users", "groups", "serviceaccounts"}, "CRITICAL", "Can impersonate any identity"),
|
|
({"get", "list", "watch"}, {"secrets"}, "HIGH", "Can read all secrets in scope"),
|
|
({"create"}, {"pods"}, "HIGH", "Can create pods (deploy workloads)"),
|
|
({"create"}, {"pods/exec"}, "HIGH", "Can exec into pods (command execution)"),
|
|
({"delete"}, {"pods", "nodes", "namespaces"}, "HIGH", "Can delete critical resources"),
|
|
]
|
|
|
|
|
|
@dataclass
|
|
class RBACFinding:
|
|
resource_type: str
|
|
resource_name: str
|
|
namespace: str
|
|
severity: str
|
|
issue: str
|
|
details: str
|
|
remediation: str
|
|
|
|
|
|
@dataclass
|
|
class RBACAuditReport:
|
|
findings: list = field(default_factory=list)
|
|
cluster_roles: int = 0
|
|
roles: int = 0
|
|
cluster_role_bindings: int = 0
|
|
role_bindings: int = 0
|
|
service_accounts: int = 0
|
|
|
|
|
|
def run_kubectl_json(args: list):
|
|
cmd = ["kubectl"] + args + ["-o", "json"]
|
|
try:
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
|
if result.returncode != 0:
|
|
return None
|
|
return json.loads(result.stdout)
|
|
except (subprocess.TimeoutExpired, json.JSONDecodeError, FileNotFoundError):
|
|
return None
|
|
|
|
|
|
def check_role_rules(rules: list, role_name: str, role_type: str, namespace: str, report: RBACAuditReport):
|
|
"""Analyze role rules for dangerous permissions."""
|
|
for rule in rules:
|
|
verbs = set(rule.get("verbs", []))
|
|
resources = set(rule.get("resources", []))
|
|
api_groups = rule.get("apiGroups", [])
|
|
|
|
for req_verbs, req_resources, severity, description in HIGH_RISK_COMBINATIONS:
|
|
verb_match = "*" in verbs or bool(verbs & req_verbs)
|
|
resource_match = "*" in resources or bool(resources & req_resources)
|
|
|
|
if verb_match and resource_match:
|
|
report.findings.append(RBACFinding(
|
|
resource_type=role_type,
|
|
resource_name=role_name,
|
|
namespace=namespace,
|
|
severity=severity,
|
|
issue=description,
|
|
details=f"verbs={list(verbs)}, resources={list(resources)}, apiGroups={api_groups}",
|
|
remediation=f"Restrict {role_type} '{role_name}' to minimum required permissions"
|
|
))
|
|
break
|
|
|
|
|
|
def audit_cluster_roles(report: RBACAuditReport):
|
|
"""Audit all ClusterRoles."""
|
|
print("[*] Auditing ClusterRoles...")
|
|
data = run_kubectl_json(["get", "clusterroles"])
|
|
if not data:
|
|
return
|
|
|
|
items = data.get("items", [])
|
|
report.cluster_roles = len(items)
|
|
|
|
for cr in items:
|
|
name = cr["metadata"]["name"]
|
|
# Skip well-known system roles
|
|
if name.startswith("system:") and name not in ("system:aggregate-to-admin", "system:aggregate-to-edit"):
|
|
continue
|
|
|
|
rules = cr.get("rules", [])
|
|
check_role_rules(rules, name, "ClusterRole", "cluster-wide", report)
|
|
|
|
|
|
def audit_roles(report: RBACAuditReport):
|
|
"""Audit all namespace Roles."""
|
|
print("[*] Auditing Roles...")
|
|
data = run_kubectl_json(["get", "roles", "-A"])
|
|
if not data:
|
|
return
|
|
|
|
items = data.get("items", [])
|
|
report.roles = len(items)
|
|
|
|
for role in items:
|
|
name = role["metadata"]["name"]
|
|
namespace = role["metadata"]["namespace"]
|
|
rules = role.get("rules", [])
|
|
check_role_rules(rules, name, "Role", namespace, report)
|
|
|
|
|
|
def audit_bindings(report: RBACAuditReport):
|
|
"""Audit ClusterRoleBindings for dangerous subject assignments."""
|
|
print("[*] Auditing ClusterRoleBindings...")
|
|
|
|
data = run_kubectl_json(["get", "clusterrolebindings"])
|
|
if not data:
|
|
return
|
|
|
|
items = data.get("items", [])
|
|
report.cluster_role_bindings = len(items)
|
|
|
|
dangerous_subjects = {"system:anonymous", "system:unauthenticated"}
|
|
admin_roles = {"cluster-admin", "admin", "edit"}
|
|
|
|
for crb in items:
|
|
name = crb["metadata"]["name"]
|
|
role_ref = crb.get("roleRef", {}).get("name", "")
|
|
subjects = crb.get("subjects", []) or []
|
|
|
|
for subject in subjects:
|
|
s_name = subject.get("name", "")
|
|
s_kind = subject.get("kind", "")
|
|
|
|
if s_name in dangerous_subjects and role_ref in admin_roles:
|
|
report.findings.append(RBACFinding(
|
|
resource_type="ClusterRoleBinding",
|
|
resource_name=name,
|
|
namespace="cluster-wide",
|
|
severity="CRITICAL",
|
|
issue=f"Dangerous subject '{s_name}' bound to '{role_ref}'",
|
|
details=f"Subject {s_kind}/{s_name} has {role_ref} access",
|
|
remediation=f"Remove or restrict ClusterRoleBinding '{name}'"
|
|
))
|
|
|
|
# Check for system:authenticated bound to admin roles
|
|
if s_name == "system:authenticated" and role_ref in admin_roles:
|
|
report.findings.append(RBACFinding(
|
|
resource_type="ClusterRoleBinding",
|
|
resource_name=name,
|
|
namespace="cluster-wide",
|
|
severity="CRITICAL",
|
|
issue=f"All authenticated users have '{role_ref}' access",
|
|
details=f"Group system:authenticated bound to {role_ref}",
|
|
remediation=f"Remove binding, use specific user/group bindings"
|
|
))
|
|
|
|
|
|
def audit_service_accounts(report: RBACAuditReport):
|
|
"""Audit service accounts for over-permissioning."""
|
|
print("[*] Auditing Service Accounts...")
|
|
|
|
data = run_kubectl_json(["get", "serviceaccounts", "-A"])
|
|
if not data:
|
|
return
|
|
|
|
items = data.get("items", [])
|
|
report.service_accounts = len(items)
|
|
|
|
# Check default SAs that have non-default bindings
|
|
crbs = run_kubectl_json(["get", "clusterrolebindings"])
|
|
rbs = run_kubectl_json(["get", "rolebindings", "-A"])
|
|
|
|
if crbs:
|
|
for crb in crbs.get("items", []):
|
|
for subject in crb.get("subjects", []) or []:
|
|
if subject.get("kind") == "ServiceAccount" and subject.get("name") == "default":
|
|
report.findings.append(RBACFinding(
|
|
resource_type="ServiceAccount",
|
|
resource_name=f"default ({subject.get('namespace', 'unknown')})",
|
|
namespace=subject.get("namespace", "unknown"),
|
|
severity="HIGH",
|
|
issue=f"Default SA bound to ClusterRole '{crb['roleRef']['name']}'",
|
|
details="Default service account should not have additional permissions",
|
|
remediation="Create dedicated service account, remove default SA binding"
|
|
))
|
|
|
|
|
|
def print_report(report: RBACAuditReport):
|
|
print("\n" + "=" * 70)
|
|
print("KUBERNETES RBAC AUDIT REPORT")
|
|
print("=" * 70)
|
|
print(f"ClusterRoles: {report.cluster_roles}")
|
|
print(f"Roles: {report.roles}")
|
|
print(f"ClusterRoleBindings: {report.cluster_role_bindings}")
|
|
print(f"RoleBindings: {report.role_bindings}")
|
|
print(f"ServiceAccounts: {report.service_accounts}")
|
|
print(f"Total Findings: {len(report.findings)}")
|
|
print("=" * 70)
|
|
|
|
for severity in ["CRITICAL", "HIGH", "MEDIUM", "LOW"]:
|
|
findings = [f for f in report.findings if f.severity == severity]
|
|
if findings:
|
|
print(f"\n{severity} ({len(findings)}):")
|
|
print("-" * 70)
|
|
for f in findings:
|
|
print(f" [{f.resource_type}] {f.resource_name}")
|
|
print(f" Issue: {f.issue}")
|
|
print(f" Details: {f.details}")
|
|
print(f" Fix: {f.remediation}")
|
|
print()
|
|
|
|
|
|
def main():
|
|
print("[*] Kubernetes RBAC Permissions Auditor\n")
|
|
|
|
report = RBACAuditReport()
|
|
audit_cluster_roles(report)
|
|
audit_roles(report)
|
|
audit_bindings(report)
|
|
audit_service_accounts(report)
|
|
print_report(report)
|
|
|
|
output = {
|
|
"summary": {
|
|
"cluster_roles": report.cluster_roles,
|
|
"roles": report.roles,
|
|
"findings": len(report.findings),
|
|
},
|
|
"findings": [
|
|
{"type": f.resource_type, "name": f.resource_name, "namespace": f.namespace,
|
|
"severity": f.severity, "issue": f.issue, "remediation": f.remediation}
|
|
for f in report.findings
|
|
],
|
|
}
|
|
|
|
with open("rbac_audit_report.json", "w") as f:
|
|
json.dump(output, f, indent=2)
|
|
print("[*] Report saved to rbac_audit_report.json")
|
|
|
|
critical = sum(1 for f in report.findings if f.severity == "CRITICAL")
|
|
if critical > 0:
|
|
print(f"\n[!] {critical} CRITICAL findings found")
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|