chore: fix license, add disclaimer, quick start, GitHub topics, issue templates

This commit is contained in:
mukul975
2026-03-11 01:42:50 +01:00
parent e09b84a7da
commit 4ed6f49151
1468 changed files with 152537 additions and 13659 deletions
@@ -1,173 +1,99 @@
#!/usr/bin/env python3
"""
Kubernetes Pod Privilege Escalation Detection Agent
Audits Kubernetes pods for privilege escalation risks including privileged
containers, host namespace access, and dangerous capabilities.
Authorized security monitoring use only.
"""Kubernetes pod privilege escalation detection agent.
Audits pod security contexts, capabilities, and service account permissions
to detect misconfigurations enabling container privilege escalation.
"""
import argparse
import json
import subprocess
import sys
from datetime import datetime, timezone
from datetime import datetime
from kubernetes import client, config
DANGEROUS_CAPABILITIES = {
"SYS_ADMIN", "SYS_PTRACE", "SYS_MODULE", "NET_ADMIN",
"NET_RAW", "DAC_READ_SEARCH", "SYS_RAWIO",
}
DANGEROUS_CAPABILITIES = [
"SYS_ADMIN", "SYS_PTRACE", "NET_ADMIN", "NET_RAW",
"DAC_OVERRIDE", "SETUID", "SETGID", "SYS_RAWIO",
]
def get_pods(namespace="--all-namespaces"):
try:
cmd = ["kubectl", "get", "pods", "-o", "json"]
if namespace == "--all-namespaces":
cmd.append("--all-namespaces")
else:
cmd.extend(["-n", namespace])
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
if result.returncode == 0:
return json.loads(result.stdout).get("items", [])
except (subprocess.TimeoutExpired, FileNotFoundError, json.JSONDecodeError):
pass
return []
def load_k8s_config(kubeconfig=None, in_cluster=False):
"""Load Kubernetes configuration."""
if in_cluster:
config.load_incluster_config()
else:
config.load_kube_config(config_file=kubeconfig)
def audit_pod_security(namespace=None):
"""Audit pods for privilege escalation vectors."""
v1 = client.CoreV1Api()
if namespace:
pods = v1.list_namespaced_pod(namespace)
else:
pods = v1.list_pod_for_all_namespaces()
def audit_pod(pod):
findings = []
for pod in pods.items:
pod_name = pod.metadata.name
pod_ns = pod.metadata.namespace
for container in (pod.spec.containers or []):
sc = container.security_context
if not sc:
findings.append({
"pod": pod_name, "namespace": pod_ns,
"container": container.name,
"issue": "no_security_context",
"severity": "medium",
"detail": "Container has no security context defined",
})
continue
if sc.privileged:
findings.append({
"pod": pod_name, "namespace": pod_ns,
"container": container.name,
"issue": "privileged_container",
"severity": "critical",
"detail": "Container runs in privileged mode",
})
if sc.allow_privilege_escalation is not False:
findings.append({
"pod": pod_name, "namespace": pod_ns,
"container": container.name,
"issue": "allow_privilege_escalation",
"severity": "high",
"detail": "allowPrivilegeEscalation is not explicitly set to false",
})
if sc.run_as_user == 0 or (sc.run_as_non_root is not True and not sc.run_as_user):
findings.append({
"pod": pod_name, "namespace": pod_ns,
"container": container.name,
"issue": "runs_as_root",
"severity": "high",
"detail": "Container may run as root",
})
if sc.capabilities and sc.capabilities.add:
dangerous = [c for c in sc.capabilities.add if c in DANGEROUS_CAPABILITIES]
if dangerous:
findings.append({
"pod": pod_name, "namespace": pod_ns,
"container": container.name,
"issue": "dangerous_capabilities",
"severity": "high",
"detail": f"Dangerous capabilities: {dangerous}",
})
spec = pod.spec
if spec.host_pid:
findings.append({
"pod": pod_name, "namespace": pod_ns,
"issue": "host_pid_namespace", "severity": "critical",
"detail": "Pod shares host PID namespace",
})
if spec.host_network:
findings.append({
"pod": pod_name, "namespace": pod_ns,
"issue": "host_network", "severity": "high",
"detail": "Pod shares host network namespace",
})
if spec.host_ipc:
findings.append({
"pod": pod_name, "namespace": pod_ns,
"issue": "host_ipc", "severity": "high",
"detail": "Pod shares host IPC namespace",
})
for vol in (spec.volumes or []):
if vol.host_path:
findings.append({
"pod": pod_name, "namespace": pod_ns,
"issue": "host_path_volume",
"severity": "high",
"detail": f"hostPath volume: {vol.host_path.path}",
})
return findings
pod_name = pod.get("metadata", {}).get("name", "")
namespace = pod.get("metadata", {}).get("namespace", "")
spec = pod.get("spec", {})
if spec.get("hostPID"):
findings.append({"check": "hostPID", "severity": "CRITICAL", "desc": "Host PID namespace"})
if spec.get("hostNetwork"):
findings.append({"check": "hostNetwork", "severity": "HIGH", "desc": "Host network"})
if spec.get("hostIPC"):
findings.append({"check": "hostIPC", "severity": "MEDIUM", "desc": "Host IPC"})
for container in spec.get("containers", []) + spec.get("initContainers", []):
ctx = container.get("securityContext", {})
c_name = container.get("name", "")
if ctx.get("privileged"):
findings.append({"container": c_name, "check": "privileged",
"severity": "CRITICAL", "desc": "Privileged container"})
if ctx.get("allowPrivilegeEscalation", True):
findings.append({"container": c_name, "check": "allowPrivilegeEscalation",
"severity": "HIGH", "desc": "Privilege escalation allowed"})
run_as = ctx.get("runAsUser")
if run_as == 0 or (run_as is None and not ctx.get("runAsNonRoot")):
findings.append({"container": c_name, "check": "runAsRoot",
"severity": "HIGH", "desc": "May run as root"})
if not ctx.get("readOnlyRootFilesystem"):
findings.append({"container": c_name, "check": "mutableFS",
"severity": "MEDIUM", "desc": "Writable root FS"})
caps = ctx.get("capabilities", {})
for cap in set(caps.get("add", [])) & DANGEROUS_CAPABILITIES:
findings.append({"container": c_name, "check": "dangerous_cap",
"capability": cap, "severity": "CRITICAL"})
for vm in container.get("volumeMounts", []):
if vm.get("mountPath") in ("/var/run/docker.sock", "/run/containerd/containerd.sock"):
findings.append({"container": c_name, "check": "runtime_socket",
"severity": "CRITICAL", "desc": f"Socket: {vm['mountPath']}"})
risk = "CRITICAL" if any(f["severity"] == "CRITICAL" for f in findings) else \
"HIGH" if any(f["severity"] == "HIGH" for f in findings) else "MEDIUM"
return {"pod": pod_name, "namespace": namespace, "findings": findings, "risk": risk}
def audit_rbac_escalation(namespace=None):
"""Check RBAC for privilege escalation paths."""
rbac = client.RbacAuthorizationV1Api()
findings = []
if namespace:
bindings = rbac.list_namespaced_role_binding(namespace)
def main():
parser = argparse.ArgumentParser(description="K8s Pod PrivEsc Detector")
parser.add_argument("--namespace", default="--all-namespaces")
parser.add_argument("--json-file", help="Pod JSON file instead of kubectl")
args = parser.parse_args()
if args.json_file:
with open(args.json_file) as f:
data = json.load(f)
pods = data.get("items", [data]) if "items" in data else [data]
else:
bindings = rbac.list_cluster_role_binding()
for binding in bindings.items:
role_ref = binding.role_ref
if role_ref.name in ("cluster-admin", "admin", "edit"):
for subject in (binding.subjects or []):
if subject.kind == "ServiceAccount":
findings.append({
"binding": binding.metadata.name,
"role": role_ref.name,
"subject": f"{subject.namespace}/{subject.name}",
"issue": "overprivileged_service_account",
"severity": "high",
"detail": f"ServiceAccount bound to {role_ref.name}",
})
return findings
def generate_report(pod_findings, rbac_findings):
"""Generate Kubernetes privilege escalation report."""
all_findings = pod_findings + rbac_findings
return {
"report_title": "Kubernetes Privilege Escalation Detection",
"generated_at": datetime.now(timezone.utc).isoformat(),
"total_findings": len(all_findings),
"critical": len([f for f in all_findings if f.get("severity") == "critical"]),
"high": len([f for f in all_findings if f.get("severity") == "high"]),
"pod_findings": pod_findings,
"rbac_findings": rbac_findings,
}
pods = get_pods(args.namespace)
results = {"timestamp": datetime.utcnow().isoformat() + "Z", "pod_audits": []}
for pod in pods:
audit = audit_pod(pod)
if audit["findings"]:
results["pod_audits"].append(audit)
results["total_pods_with_issues"] = len(results["pod_audits"])
print(json.dumps(results, indent=2))
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Detect K8s pod privilege escalation")
parser.add_argument("--namespace", "-n", help="Kubernetes namespace to audit")
parser.add_argument("--kubeconfig", help="Path to kubeconfig file")
parser.add_argument("--in-cluster", action="store_true", help="Use in-cluster config")
parser.add_argument("--output", default="k8s_privesc_audit.json", help="Output file")
args = parser.parse_args()
load_k8s_config(args.kubeconfig, args.in_cluster)
print("[*] Auditing pod security contexts...")
pod_findings = audit_pod_security(args.namespace)
print("[*] Auditing RBAC bindings...")
rbac_findings = audit_rbac_escalation(args.namespace)
report = generate_report(pod_findings, rbac_findings)
with open(args.output, "w") as f:
json.dump(report, f, indent=2)
print(json.dumps(report, indent=2))
main()