mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-12 14:14:56 +03:00
286 lines
13 KiB
Python
286 lines
13 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Kubernetes RBAC Auditor
|
|
|
|
Analyzes Kubernetes RBAC configurations to identify overly permissive
|
|
roles, dangerous permissions, unnecessary ClusterRoleBindings, and
|
|
service account security issues.
|
|
"""
|
|
|
|
import json
|
|
import datetime
|
|
from typing import Dict, List, Set
|
|
from dataclasses import dataclass, field
|
|
|
|
|
|
@dataclass
|
|
class K8sRole:
|
|
"""Kubernetes Role or ClusterRole."""
|
|
name: str
|
|
namespace: str # empty for ClusterRole
|
|
is_cluster_role: bool
|
|
rules: List[Dict] = field(default_factory=list)
|
|
# Each rule: {"apiGroups": [...], "resources": [...], "verbs": [...]}
|
|
|
|
|
|
@dataclass
|
|
class K8sBinding:
|
|
"""Kubernetes RoleBinding or ClusterRoleBinding."""
|
|
name: str
|
|
namespace: str
|
|
is_cluster_binding: bool
|
|
role_ref: str
|
|
role_ref_kind: str # Role or ClusterRole
|
|
subjects: List[Dict] = field(default_factory=list)
|
|
# Each subject: {"kind": "User/Group/ServiceAccount", "name": "...", "namespace": "..."}
|
|
|
|
|
|
@dataclass
|
|
class RBACFinding:
|
|
severity: str
|
|
category: str
|
|
title: str
|
|
description: str
|
|
recommendation: str = ""
|
|
affected_resources: List[str] = field(default_factory=list)
|
|
|
|
|
|
class KubernetesRBACAuditor:
|
|
"""Audits Kubernetes RBAC for security issues."""
|
|
|
|
DANGEROUS_VERBS = {"*", "escalate", "bind", "impersonate"}
|
|
SENSITIVE_RESOURCES = {"secrets", "roles", "clusterroles", "rolebindings",
|
|
"clusterrolebindings", "nodes", "persistentvolumes"}
|
|
EXEC_RESOURCES = {"pods/exec", "pods/attach"}
|
|
|
|
def __init__(self):
|
|
self.roles: List[K8sRole] = []
|
|
self.bindings: List[K8sBinding] = []
|
|
self.findings: List[RBACFinding] = []
|
|
|
|
def load_roles(self, roles: List[Dict]):
|
|
for r in roles:
|
|
self.roles.append(K8sRole(**r))
|
|
|
|
def load_bindings(self, bindings: List[Dict]):
|
|
for b in bindings:
|
|
self.bindings.append(K8sBinding(**b))
|
|
|
|
def audit_all(self) -> List[RBACFinding]:
|
|
self.findings = []
|
|
self._audit_wildcard_permissions()
|
|
self._audit_dangerous_verbs()
|
|
self._audit_cluster_admin_bindings()
|
|
self._audit_service_account_bindings()
|
|
self._audit_exec_permissions()
|
|
self._audit_secret_access()
|
|
self._audit_rbac_modification_permissions()
|
|
return self.findings
|
|
|
|
def _audit_wildcard_permissions(self):
|
|
for role in self.roles:
|
|
for rule in role.rules:
|
|
if "*" in rule.get("resources", []) or "*" in rule.get("verbs", []):
|
|
scope = "ClusterRole" if role.is_cluster_role else f"Role in {role.namespace}"
|
|
self.findings.append(RBACFinding(
|
|
severity="critical",
|
|
category="Wildcard Permissions",
|
|
title=f"Wildcard permissions in {scope} '{role.name}'",
|
|
description=f"Resources: {rule.get('resources')}, Verbs: {rule.get('verbs')}. "
|
|
"Wildcard grants excessive access violating least privilege.",
|
|
recommendation="Replace wildcards with explicit resource and verb lists.",
|
|
affected_resources=[role.name]
|
|
))
|
|
|
|
def _audit_dangerous_verbs(self):
|
|
for role in self.roles:
|
|
for rule in role.rules:
|
|
dangerous = set(rule.get("verbs", [])) & self.DANGEROUS_VERBS
|
|
if dangerous and "*" not in dangerous: # wildcard already caught
|
|
self.findings.append(RBACFinding(
|
|
severity="critical",
|
|
category="Dangerous Verbs",
|
|
title=f"Dangerous verbs in '{role.name}': {', '.join(dangerous)}",
|
|
description="escalate/bind allow privilege escalation. "
|
|
"impersonate allows identity spoofing.",
|
|
recommendation="Remove dangerous verbs. Only cluster-admin should have these.",
|
|
affected_resources=[role.name]
|
|
))
|
|
|
|
def _audit_cluster_admin_bindings(self):
|
|
cluster_admin_bindings = [
|
|
b for b in self.bindings
|
|
if b.role_ref == "cluster-admin" and b.is_cluster_binding
|
|
]
|
|
for binding in cluster_admin_bindings:
|
|
for subject in binding.subjects:
|
|
if subject.get("kind") == "ServiceAccount":
|
|
self.findings.append(RBACFinding(
|
|
severity="critical",
|
|
category="Cluster Admin",
|
|
title=f"ServiceAccount bound to cluster-admin: {subject.get('name')}",
|
|
description=f"Service account '{subject.get('name')}' in namespace "
|
|
f"'{subject.get('namespace', 'default')}' has full cluster admin access.",
|
|
recommendation="Create a dedicated ClusterRole with minimum required permissions.",
|
|
affected_resources=[binding.name]
|
|
))
|
|
elif subject.get("kind") == "Group" and subject.get("name") not in (
|
|
"system:masters",
|
|
):
|
|
self.findings.append(RBACFinding(
|
|
severity="high",
|
|
category="Cluster Admin",
|
|
title=f"Group bound to cluster-admin: {subject.get('name')}",
|
|
description=f"All members of group '{subject.get('name')}' have full cluster admin.",
|
|
recommendation="Review group membership. Use namespace-scoped roles instead.",
|
|
affected_resources=[binding.name]
|
|
))
|
|
|
|
def _audit_service_account_bindings(self):
|
|
default_sa_bindings = []
|
|
for binding in self.bindings:
|
|
for subject in binding.subjects:
|
|
if (subject.get("kind") == "ServiceAccount" and
|
|
subject.get("name") == "default"):
|
|
default_sa_bindings.append(binding)
|
|
|
|
if default_sa_bindings:
|
|
self.findings.append(RBACFinding(
|
|
severity="high",
|
|
category="Service Account",
|
|
title=f"Default service account has {len(default_sa_bindings)} custom bindings",
|
|
description="Default service account should not have additional permissions. "
|
|
"All pods without explicit SA use the default SA.",
|
|
recommendation="Create dedicated service accounts per application. "
|
|
"Remove bindings from default SA.",
|
|
affected_resources=[b.name for b in default_sa_bindings]
|
|
))
|
|
|
|
def _audit_exec_permissions(self):
|
|
for role in self.roles:
|
|
for rule in role.rules:
|
|
resources = set(rule.get("resources", []))
|
|
exec_resources = resources & self.EXEC_RESOURCES
|
|
if exec_resources:
|
|
self.findings.append(RBACFinding(
|
|
severity="high",
|
|
category="Pod Exec",
|
|
title=f"Pod exec/attach permission in '{role.name}'",
|
|
description="pods/exec allows running commands inside containers. "
|
|
"This can be used for lateral movement.",
|
|
recommendation="Restrict exec access to debugging roles. "
|
|
"Monitor exec usage in audit logs.",
|
|
affected_resources=[role.name]
|
|
))
|
|
|
|
def _audit_secret_access(self):
|
|
for role in self.roles:
|
|
for rule in role.rules:
|
|
resources = set(rule.get("resources", []))
|
|
verbs = set(rule.get("verbs", []))
|
|
if "secrets" in resources:
|
|
write_verbs = verbs & {"create", "update", "patch", "delete", "*"}
|
|
if write_verbs:
|
|
self.findings.append(RBACFinding(
|
|
severity="high",
|
|
category="Secret Access",
|
|
title=f"Secret write access in '{role.name}'",
|
|
description=f"Write verbs on secrets: {', '.join(write_verbs)}. "
|
|
"This allows creating/modifying secrets.",
|
|
recommendation="Limit secret write access to operators and CI/CD only.",
|
|
affected_resources=[role.name]
|
|
))
|
|
|
|
def _audit_rbac_modification_permissions(self):
|
|
rbac_resources = {"roles", "clusterroles", "rolebindings", "clusterrolebindings"}
|
|
for role in self.roles:
|
|
if role.name in ("cluster-admin", "admin"):
|
|
continue # Skip built-in roles
|
|
for rule in role.rules:
|
|
resources = set(rule.get("resources", []))
|
|
if resources & rbac_resources:
|
|
verbs = set(rule.get("verbs", []))
|
|
write_verbs = verbs & {"create", "update", "patch", "delete", "*"}
|
|
if write_verbs:
|
|
self.findings.append(RBACFinding(
|
|
severity="critical",
|
|
category="RBAC Modification",
|
|
title=f"RBAC modification permissions in '{role.name}'",
|
|
description=f"Can modify RBAC objects: {resources & rbac_resources}. "
|
|
"This enables privilege escalation.",
|
|
recommendation="Remove RBAC modification permissions from non-admin roles.",
|
|
affected_resources=[role.name]
|
|
))
|
|
|
|
def generate_report(self) -> str:
|
|
if not self.findings:
|
|
self.audit_all()
|
|
|
|
lines = [
|
|
"=" * 70,
|
|
"KUBERNETES RBAC AUDIT REPORT",
|
|
"=" * 70,
|
|
f"Report Date: {datetime.datetime.now().isoformat()}",
|
|
f"Roles/ClusterRoles Audited: {len(self.roles)}",
|
|
f"Bindings Audited: {len(self.bindings)}",
|
|
f"Findings: {len(self.findings)}",
|
|
"-" * 70, ""
|
|
]
|
|
|
|
severity_order = {"critical": 0, "high": 1, "medium": 2, "low": 3}
|
|
for f in sorted(self.findings, key=lambda x: severity_order.get(x.severity, 5)):
|
|
lines.append(f"[{f.severity.upper()}] {f.title}")
|
|
lines.append(f" Category: {f.category}")
|
|
lines.append(f" {f.description}")
|
|
if f.recommendation:
|
|
lines.append(f" Fix: {f.recommendation}")
|
|
if f.affected_resources:
|
|
lines.append(f" Affected: {', '.join(f.affected_resources)}")
|
|
lines.append("")
|
|
|
|
critical = sum(1 for f in self.findings if f.severity == "critical")
|
|
lines.append("=" * 70)
|
|
lines.append(f"OVERALL: {'FAIL' if critical else 'PASS'}")
|
|
lines.append("=" * 70)
|
|
return "\n".join(lines)
|
|
|
|
|
|
def main():
|
|
auditor = KubernetesRBACAuditor()
|
|
|
|
auditor.load_roles([
|
|
{"name": "developer", "namespace": "app-team", "is_cluster_role": False,
|
|
"rules": [
|
|
{"apiGroups": ["", "apps"], "resources": ["pods", "deployments", "services"], "verbs": ["get", "list", "create", "update", "delete"]},
|
|
{"apiGroups": [""], "resources": ["secrets"], "verbs": ["get", "list"]},
|
|
{"apiGroups": [""], "resources": ["pods/exec"], "verbs": ["create"]}
|
|
]},
|
|
{"name": "ci-deployer", "namespace": "", "is_cluster_role": True,
|
|
"rules": [
|
|
{"apiGroups": ["*"], "resources": ["*"], "verbs": ["*"]}
|
|
]},
|
|
{"name": "custom-admin", "namespace": "production", "is_cluster_role": False,
|
|
"rules": [
|
|
{"apiGroups": ["rbac.authorization.k8s.io"], "resources": ["roles", "rolebindings"], "verbs": ["create", "update", "delete"]},
|
|
{"apiGroups": [""], "resources": ["secrets"], "verbs": ["create", "update", "delete"]}
|
|
]},
|
|
])
|
|
|
|
auditor.load_bindings([
|
|
{"name": "ci-deployer-binding", "namespace": "", "is_cluster_binding": True,
|
|
"role_ref": "cluster-admin", "role_ref_kind": "ClusterRole",
|
|
"subjects": [{"kind": "ServiceAccount", "name": "ci-deployer", "namespace": "ci-cd"}]},
|
|
{"name": "dev-binding", "namespace": "app-team", "is_cluster_binding": False,
|
|
"role_ref": "developer", "role_ref_kind": "Role",
|
|
"subjects": [{"kind": "Group", "name": "dev-team"}]},
|
|
{"name": "default-elevated", "namespace": "app-team", "is_cluster_binding": False,
|
|
"role_ref": "developer", "role_ref_kind": "Role",
|
|
"subjects": [{"kind": "ServiceAccount", "name": "default", "namespace": "app-team"}]},
|
|
])
|
|
|
|
print(auditor.generate_report())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|