mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-29 23:46:53 +03:00
Expand 39 api-reference stubs with real tool docs, expand 15 agent.py boilerplate stubs
This commit is contained in:
@@ -1,61 +1,253 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Kubernetes RBAC hardening audit."""
|
||||
import argparse, json
|
||||
"""Kubernetes RBAC hardening audit agent.
|
||||
|
||||
Audits Kubernetes Role-Based Access Control configuration for security
|
||||
weaknesses including overly permissive ClusterRoles, wildcard permissions,
|
||||
privilege escalation paths, and service account misconfigurations.
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
requests = None
|
||||
|
||||
def audit_config(target, token):
|
||||
findings = []
|
||||
if not requests: return [{"error": "requests required"}]
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
|
||||
def run_kubectl(args_list, timeout=60):
|
||||
"""Execute kubectl and return parsed JSON."""
|
||||
cmd = ["kubectl"] + args_list + ["-o", "json"]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
|
||||
if result.returncode != 0:
|
||||
return None
|
||||
try:
|
||||
resp = requests.get(f"{target}/api/v1/status", headers=headers, timeout=10)
|
||||
if resp.status_code == 200:
|
||||
data = resp.json()
|
||||
if not data.get("enabled", True):
|
||||
findings.append({"check": "Service Status", "status": "DISABLED", "severity": "CRITICAL"})
|
||||
elif resp.status_code == 401:
|
||||
findings.append({"check": "Authentication", "status": "UNAUTHORIZED", "severity": "HIGH"})
|
||||
except requests.RequestException as e:
|
||||
findings.append({"error": str(e)})
|
||||
return json.loads(result.stdout)
|
||||
except json.JSONDecodeError:
|
||||
return None
|
||||
|
||||
|
||||
def audit_cluster_roles():
|
||||
"""Audit ClusterRoles for dangerous permissions."""
|
||||
findings = []
|
||||
data = run_kubectl(["get", "clusterroles"])
|
||||
if not data:
|
||||
return findings
|
||||
|
||||
dangerous_verbs = {"*", "create", "update", "patch", "delete"}
|
||||
dangerous_resources = {"secrets", "pods/exec", "clusterroles", "clusterrolebindings",
|
||||
"roles", "rolebindings", "serviceaccounts", "nodes"}
|
||||
escalation_resources = {"clusterroles", "clusterrolebindings", "roles", "rolebindings"}
|
||||
|
||||
for role in data.get("items", []):
|
||||
name = role.get("metadata", {}).get("name", "")
|
||||
if name.startswith("system:"):
|
||||
continue # Skip system roles
|
||||
|
||||
for rule in role.get("rules", []):
|
||||
verbs = set(rule.get("verbs", []))
|
||||
resources = set(rule.get("resources", []))
|
||||
api_groups = rule.get("apiGroups", [])
|
||||
|
||||
# Wildcard everything
|
||||
if "*" in verbs and "*" in resources:
|
||||
findings.append({
|
||||
"type": "wildcard_all", "role": name, "kind": "ClusterRole",
|
||||
"severity": "CRITICAL",
|
||||
"detail": "Full wildcard access (verbs: *, resources: *)",
|
||||
"recommendation": "Replace with specific verbs and resources",
|
||||
})
|
||||
|
||||
# Secrets access
|
||||
if resources & {"secrets", "*"} and verbs & {"get", "list", "watch", "*"}:
|
||||
findings.append({
|
||||
"type": "secrets_access", "role": name, "kind": "ClusterRole",
|
||||
"severity": "HIGH",
|
||||
"detail": f"Can read secrets (verbs: {verbs & {'get', 'list', 'watch', '*'}})",
|
||||
})
|
||||
|
||||
# Pod exec
|
||||
if "pods/exec" in resources or ("pods" in resources and "create" in verbs):
|
||||
findings.append({
|
||||
"type": "pod_exec", "role": name, "kind": "ClusterRole",
|
||||
"severity": "HIGH",
|
||||
"detail": "Can exec into pods (container escape risk)",
|
||||
})
|
||||
|
||||
# Privilege escalation via RBAC modification
|
||||
if resources & escalation_resources and verbs & {"create", "update", "patch", "*"}:
|
||||
findings.append({
|
||||
"type": "rbac_escalation", "role": name, "kind": "ClusterRole",
|
||||
"severity": "CRITICAL",
|
||||
"detail": f"Can modify RBAC resources: {resources & escalation_resources}",
|
||||
})
|
||||
|
||||
# Node access
|
||||
if "nodes" in resources and verbs & {"get", "list", "proxy", "*"}:
|
||||
findings.append({
|
||||
"type": "node_access", "role": name, "kind": "ClusterRole",
|
||||
"severity": "MEDIUM",
|
||||
"detail": "Can access node resources",
|
||||
})
|
||||
|
||||
return findings
|
||||
|
||||
def check_compliance(target, token):
|
||||
|
||||
def audit_cluster_role_bindings():
|
||||
"""Audit ClusterRoleBindings for overly broad subject assignments."""
|
||||
findings = []
|
||||
if not requests: return []
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
try:
|
||||
resp = requests.get(f"{target}/api/v1/compliance", headers=headers, timeout=10)
|
||||
if resp.status_code == 200:
|
||||
for item in resp.json().get("checks", []):
|
||||
if item.get("status") != "PASS":
|
||||
findings.append({"check": item.get("name"), "status": item.get("status"),
|
||||
"severity": item.get("severity", "MEDIUM")})
|
||||
except requests.RequestException:
|
||||
pass
|
||||
data = run_kubectl(["get", "clusterrolebindings"])
|
||||
if not data:
|
||||
return findings
|
||||
|
||||
for binding in data.get("items", []):
|
||||
name = binding.get("metadata", {}).get("name", "")
|
||||
if name.startswith("system:"):
|
||||
continue
|
||||
|
||||
role_ref = binding.get("roleRef", {})
|
||||
role_name = role_ref.get("name", "")
|
||||
subjects = binding.get("subjects", [])
|
||||
|
||||
for subject in subjects:
|
||||
kind = subject.get("kind", "")
|
||||
subj_name = subject.get("name", "")
|
||||
namespace = subject.get("namespace", "")
|
||||
|
||||
# Cluster-admin binding
|
||||
if role_name == "cluster-admin":
|
||||
findings.append({
|
||||
"type": "cluster_admin_binding",
|
||||
"binding": name, "subject": f"{kind}/{subj_name}",
|
||||
"severity": "CRITICAL",
|
||||
"detail": f"cluster-admin bound to {kind} '{subj_name}'",
|
||||
})
|
||||
|
||||
# Group bindings to all authenticated/unauthenticated
|
||||
if kind == "Group" and subj_name in ("system:authenticated", "system:unauthenticated"):
|
||||
findings.append({
|
||||
"type": "broad_group_binding",
|
||||
"binding": name, "subject": subj_name,
|
||||
"severity": "CRITICAL" if subj_name == "system:unauthenticated" else "HIGH",
|
||||
"detail": f"Role '{role_name}' bound to group '{subj_name}'",
|
||||
})
|
||||
|
||||
# Default service account bindings
|
||||
if kind == "ServiceAccount" and subj_name == "default":
|
||||
findings.append({
|
||||
"type": "default_sa_binding",
|
||||
"binding": name, "subject": f"default SA in {namespace}",
|
||||
"severity": "MEDIUM",
|
||||
"detail": f"Role '{role_name}' bound to default service account",
|
||||
})
|
||||
|
||||
return findings
|
||||
|
||||
|
||||
def audit_service_accounts(namespace=None):
|
||||
"""Audit service accounts for misconfigurations."""
|
||||
findings = []
|
||||
cmd = ["get", "serviceaccounts", "--all-namespaces"] if not namespace else ["get", "serviceaccounts", "-n", namespace]
|
||||
data = run_kubectl(cmd)
|
||||
if not data:
|
||||
return findings
|
||||
|
||||
for sa in data.get("items", []):
|
||||
name = sa.get("metadata", {}).get("name", "")
|
||||
ns = sa.get("metadata", {}).get("namespace", "")
|
||||
automount = sa.get("automountServiceAccountToken", None)
|
||||
|
||||
if name == "default" and automount is not False:
|
||||
findings.append({
|
||||
"type": "default_sa_automount",
|
||||
"namespace": ns, "service_account": name,
|
||||
"severity": "MEDIUM",
|
||||
"detail": f"Default SA in '{ns}' has automountServiceAccountToken enabled",
|
||||
"recommendation": "Set automountServiceAccountToken: false on default SA",
|
||||
})
|
||||
|
||||
secrets = sa.get("secrets", [])
|
||||
if len(secrets) > 1:
|
||||
findings.append({
|
||||
"type": "sa_multiple_secrets",
|
||||
"namespace": ns, "service_account": name,
|
||||
"severity": "LOW",
|
||||
"detail": f"SA has {len(secrets)} token secrets",
|
||||
})
|
||||
|
||||
return findings
|
||||
|
||||
|
||||
def format_summary(role_findings, binding_findings, sa_findings):
|
||||
"""Print RBAC audit summary."""
|
||||
all_findings = role_findings + binding_findings + sa_findings
|
||||
print(f"\n{'='*60}")
|
||||
print(f" Kubernetes RBAC Hardening Audit")
|
||||
print(f"{'='*60}")
|
||||
print(f" ClusterRole Issues : {len(role_findings)}")
|
||||
print(f" Binding Issues : {len(binding_findings)}")
|
||||
print(f" ServiceAccount Issues : {len(sa_findings)}")
|
||||
print(f" Total Findings : {len(all_findings)}")
|
||||
|
||||
severity_counts = {}
|
||||
for f in all_findings:
|
||||
sev = f.get("severity", "INFO")
|
||||
severity_counts[sev] = severity_counts.get(sev, 0) + 1
|
||||
|
||||
print(f"\n By Severity:")
|
||||
for sev in ["CRITICAL", "HIGH", "MEDIUM", "LOW"]:
|
||||
count = severity_counts.get(sev, 0)
|
||||
if count:
|
||||
print(f" {sev:10s}: {count}")
|
||||
|
||||
if all_findings:
|
||||
print(f"\n Top Findings:")
|
||||
for f in sorted(all_findings, key=lambda x: {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2}.get(x["severity"], 9))[:15]:
|
||||
print(f" [{f['severity']:8s}] {f['type']:25s} | {f.get('detail', '')[:50]}")
|
||||
|
||||
return severity_counts
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description="Kubernetes RBAC hardening audit")
|
||||
p.add_argument("--target", required=True, help="Target URL")
|
||||
p.add_argument("--token", required=True, help="API token")
|
||||
p.add_argument("--output", "-o", help="Output JSON report")
|
||||
p.add_argument("--verbose", "-v", action="store_true")
|
||||
a = p.parse_args()
|
||||
print("[*] Kubernetes RBAC hardening audit")
|
||||
report = {"timestamp": datetime.now(timezone.utc).isoformat(), "findings": []}
|
||||
report["findings"].extend(audit_config(a.target, a.token))
|
||||
report["findings"].extend(check_compliance(a.target, a.token))
|
||||
high = sum(1 for f in report["findings"] if f.get("severity") in ("HIGH", "CRITICAL"))
|
||||
report["risk_level"] = "HIGH" if high else "MEDIUM" if report["findings"] else "LOW"
|
||||
print(f"[*] {len(report['findings'])} findings, risk: {report['risk_level']}")
|
||||
if a.output:
|
||||
with open(a.output, "w") as f: json.dump(report, f, indent=2)
|
||||
else:
|
||||
parser = argparse.ArgumentParser(description="Kubernetes RBAC hardening audit agent")
|
||||
parser.add_argument("--namespace", "-n", help="Specific namespace to audit")
|
||||
parser.add_argument("--kubeconfig", help="Path to kubeconfig")
|
||||
parser.add_argument("--skip-roles", action="store_true")
|
||||
parser.add_argument("--skip-bindings", action="store_true")
|
||||
parser.add_argument("--skip-sa", action="store_true")
|
||||
parser.add_argument("--output", "-o", help="Output JSON report")
|
||||
parser.add_argument("--verbose", "-v", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.kubeconfig:
|
||||
os.environ["KUBECONFIG"] = args.kubeconfig
|
||||
|
||||
role_findings = [] if args.skip_roles else audit_cluster_roles()
|
||||
binding_findings = [] if args.skip_bindings else audit_cluster_role_bindings()
|
||||
sa_findings = [] if args.skip_sa else audit_service_accounts(args.namespace)
|
||||
|
||||
severity_counts = format_summary(role_findings, binding_findings, sa_findings)
|
||||
|
||||
report = {
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
"tool": "K8s RBAC Audit",
|
||||
"role_findings": role_findings,
|
||||
"binding_findings": binding_findings,
|
||||
"sa_findings": sa_findings,
|
||||
"severity_counts": severity_counts,
|
||||
"risk_level": (
|
||||
"CRITICAL" if severity_counts.get("CRITICAL", 0) > 0
|
||||
else "HIGH" if severity_counts.get("HIGH", 0) > 0
|
||||
else "MEDIUM" if severity_counts.get("MEDIUM", 0) > 0
|
||||
else "LOW"
|
||||
),
|
||||
}
|
||||
|
||||
if args.output:
|
||||
with open(args.output, "w") as f:
|
||||
json.dump(report, f, indent=2)
|
||||
print(f"\n[+] Report saved to {args.output}")
|
||||
elif args.verbose:
|
||||
print(json.dumps(report, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user