mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-20 14:30:59 +03:00
Production hardening: security fixes, code quality, 724 skills complete
- Fix 25 shell=True subprocess calls with list-based commands - Fix 49 verify=False in defensive skills (env-var override) - Add timeout to 231 HTTP/subprocess/socket calls - Fix 6 SQL injection patterns with whitelist validation - Replace 8 __import__() with standard imports - Remove 701 unused imports across 442 files - Add authorized-testing disclaimers to all offensive skills - Complete 11 incomplete skill directories - Expand 10 stub SKILL.md files with full content - Fix 2 YAML parse errors in frontmatter - Fix 5 pre-existing syntax errors - Convert 22 hardcoded paths/ports to environment variables - Back up 21 redundant skill pairs to .bak - Fix 2 global declaration errors - 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE) - 0 compile errors across all 724 agent.py files
This commit is contained in:
+157
-15
@@ -1,27 +1,169 @@
|
||||
# API Reference: Kubernetes Pod Security Admission audit
|
||||
# API Reference: Kubernetes Pod Security Admission Controller
|
||||
|
||||
## API Endpoints
|
||||
CoreV1Api: list_namespace(), labels: pod-security.kubernetes.io/enforce, baseline/restricted
|
||||
## Libraries Used
|
||||
|
||||
| Library | Purpose |
|
||||
|---------|---------|
|
||||
| `kubernetes` | Official Kubernetes Python client for cluster API access |
|
||||
| `json` | Parse and format admission review payloads |
|
||||
| `yaml` | Read and write Pod Security Standard label configurations |
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install kubernetes
|
||||
pip install kubernetes pyyaml
|
||||
```
|
||||
|
||||
## Libraries
|
||||
|
||||
| Library | Use |
|
||||
|---------|-----|
|
||||
| `kubernetes` | kubernetes SDK/client |
|
||||
|
||||
## Authentication
|
||||
|
||||
| Method | Header |
|
||||
|--------|--------|
|
||||
| Bearer Token | `Authorization: Bearer <token>` |
|
||||
| API Key | `X-API-Key: <key>` |
|
||||
```python
|
||||
from kubernetes import client, config
|
||||
|
||||
# In-cluster (running inside a pod)
|
||||
config.load_incluster_config()
|
||||
|
||||
# Local kubeconfig
|
||||
config.load_kube_config(context="my-cluster")
|
||||
|
||||
v1 = client.CoreV1Api()
|
||||
```
|
||||
|
||||
## Pod Security Standards Levels
|
||||
|
||||
| Level | Description |
|
||||
|-------|-------------|
|
||||
| `privileged` | Unrestricted — no restrictions applied |
|
||||
| `baseline` | Minimally restrictive — prevents known privilege escalation |
|
||||
| `restricted` | Heavily restricted — follows hardening best practices |
|
||||
|
||||
## Namespace Label API
|
||||
|
||||
Pod Security Admission is configured via namespace labels:
|
||||
|
||||
| Label | Purpose |
|
||||
|-------|---------|
|
||||
| `pod-security.kubernetes.io/enforce` | Reject pods that violate the policy |
|
||||
| `pod-security.kubernetes.io/enforce-version` | Pin policy to specific k8s version |
|
||||
| `pod-security.kubernetes.io/audit` | Log violations in audit log |
|
||||
| `pod-security.kubernetes.io/audit-version` | Pin audit policy version |
|
||||
| `pod-security.kubernetes.io/warn` | Show warnings to kubectl users |
|
||||
| `pod-security.kubernetes.io/warn-version` | Pin warning policy version |
|
||||
|
||||
## Core Operations
|
||||
|
||||
### List Namespaces with PSA Labels
|
||||
```python
|
||||
namespaces = v1.list_namespace()
|
||||
for ns in namespaces.items:
|
||||
labels = ns.metadata.labels or {}
|
||||
enforce = labels.get("pod-security.kubernetes.io/enforce", "none")
|
||||
audit = labels.get("pod-security.kubernetes.io/audit", "none")
|
||||
warn = labels.get("pod-security.kubernetes.io/warn", "none")
|
||||
print(f"{ns.metadata.name}: enforce={enforce} audit={audit} warn={warn}")
|
||||
```
|
||||
|
||||
### Apply PSA Labels to a Namespace
|
||||
```python
|
||||
body = {
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"pod-security.kubernetes.io/enforce": "restricted",
|
||||
"pod-security.kubernetes.io/enforce-version": "latest",
|
||||
"pod-security.kubernetes.io/audit": "restricted",
|
||||
"pod-security.kubernetes.io/warn": "restricted",
|
||||
}
|
||||
}
|
||||
}
|
||||
v1.patch_namespace(name="production", body=body)
|
||||
```
|
||||
|
||||
### Audit All Namespaces for Missing PSA Labels
|
||||
```python
|
||||
def audit_psa_labels():
|
||||
findings = []
|
||||
namespaces = v1.list_namespace()
|
||||
for ns in namespaces.items:
|
||||
name = ns.metadata.name
|
||||
labels = ns.metadata.labels or {}
|
||||
if name in ("kube-system", "kube-public", "kube-node-lease"):
|
||||
continue
|
||||
enforce = labels.get("pod-security.kubernetes.io/enforce")
|
||||
if not enforce:
|
||||
findings.append({"namespace": name, "issue": "no enforce label"})
|
||||
elif enforce == "privileged":
|
||||
findings.append({"namespace": name, "issue": "enforce=privileged"})
|
||||
return findings
|
||||
```
|
||||
|
||||
### Check Pod Violations Against a Level
|
||||
```python
|
||||
def check_pod_security(namespace, level="restricted"):
|
||||
pods = v1.list_namespaced_pod(namespace=namespace)
|
||||
violations = []
|
||||
for pod in pods.items:
|
||||
for container in pod.spec.containers:
|
||||
sc = container.security_context
|
||||
if not sc:
|
||||
violations.append({
|
||||
"pod": pod.metadata.name,
|
||||
"container": container.name,
|
||||
"issue": "no securityContext defined",
|
||||
})
|
||||
continue
|
||||
if sc.privileged:
|
||||
violations.append({
|
||||
"pod": pod.metadata.name,
|
||||
"container": container.name,
|
||||
"issue": "privileged=true",
|
||||
})
|
||||
if sc.run_as_non_root is not True:
|
||||
violations.append({
|
||||
"pod": pod.metadata.name,
|
||||
"container": container.name,
|
||||
"issue": "runAsNonRoot not set",
|
||||
})
|
||||
caps = sc.capabilities
|
||||
if level == "restricted" and (not caps or not caps.drop or "ALL" not in caps.drop):
|
||||
violations.append({
|
||||
"pod": pod.metadata.name,
|
||||
"container": container.name,
|
||||
"issue": "capabilities.drop does not include ALL",
|
||||
})
|
||||
return violations
|
||||
```
|
||||
|
||||
## kubectl Equivalents
|
||||
|
||||
```bash
|
||||
# Label a namespace with restricted enforcement
|
||||
kubectl label namespace production \
|
||||
pod-security.kubernetes.io/enforce=restricted \
|
||||
pod-security.kubernetes.io/warn=restricted \
|
||||
--overwrite
|
||||
|
||||
# Dry-run to test impact before enforcing
|
||||
kubectl label --dry-run=server --overwrite namespace production \
|
||||
pod-security.kubernetes.io/enforce=restricted
|
||||
|
||||
# Check which namespaces have PSA labels
|
||||
kubectl get namespaces -L pod-security.kubernetes.io/enforce
|
||||
```
|
||||
|
||||
## Output Format
|
||||
|
||||
```json
|
||||
{"timestamp": "ISO-8601", "findings": [], "risk_level": "HIGH"}
|
||||
{
|
||||
"namespace": "production",
|
||||
"enforce_level": "restricted",
|
||||
"audit_level": "restricted",
|
||||
"warn_level": "restricted",
|
||||
"pod_violations": [
|
||||
{
|
||||
"pod": "legacy-app-7f8b9c",
|
||||
"container": "app",
|
||||
"issue": "privileged=true"
|
||||
}
|
||||
],
|
||||
"compliant": false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,61 +1,305 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Kubernetes Pod Security Admission audit."""
|
||||
import argparse, json, sys
|
||||
"""Kubernetes Pod Security Admission audit agent.
|
||||
|
||||
Audits Kubernetes namespaces and workloads for Pod Security Standards (PSS)
|
||||
compliance using kubectl. Checks namespace labels for enforce/audit/warn
|
||||
modes, analyzes pod specs against Baseline and Restricted profiles, and
|
||||
reports violations.
|
||||
"""
|
||||
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 a kubectl command and return parsed JSON output."""
|
||||
cmd = ["kubectl"] + args_list + ["-o", "json"]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
|
||||
if result.returncode != 0:
|
||||
print(f"[!] kubectl error: {result.stderr[:200]}", file=sys.stderr)
|
||||
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 get_namespaces():
|
||||
"""Get all namespaces with their PSA labels."""
|
||||
print("[*] Fetching namespaces...")
|
||||
data = run_kubectl(["get", "namespaces"])
|
||||
if not data:
|
||||
return []
|
||||
namespaces = []
|
||||
for ns in data.get("items", []):
|
||||
name = ns.get("metadata", {}).get("name", "")
|
||||
labels = ns.get("metadata", {}).get("labels", {})
|
||||
psa_enforce = labels.get("pod-security.kubernetes.io/enforce", "")
|
||||
psa_audit = labels.get("pod-security.kubernetes.io/audit", "")
|
||||
psa_warn = labels.get("pod-security.kubernetes.io/warn", "")
|
||||
psa_enforce_version = labels.get("pod-security.kubernetes.io/enforce-version", "")
|
||||
namespaces.append({
|
||||
"name": name,
|
||||
"enforce": psa_enforce,
|
||||
"audit": psa_audit,
|
||||
"warn": psa_warn,
|
||||
"enforce_version": psa_enforce_version,
|
||||
"has_psa": bool(psa_enforce or psa_audit or psa_warn),
|
||||
})
|
||||
print(f"[+] Found {len(namespaces)} namespaces")
|
||||
return namespaces
|
||||
|
||||
|
||||
def audit_namespace_psa(namespaces):
|
||||
"""Audit PSA label configuration across namespaces."""
|
||||
findings = []
|
||||
system_ns = {"kube-system", "kube-public", "kube-node-lease", "default"}
|
||||
|
||||
for ns in namespaces:
|
||||
name = ns["name"]
|
||||
if name in system_ns:
|
||||
continue
|
||||
|
||||
if not ns["has_psa"]:
|
||||
findings.append({
|
||||
"namespace": name,
|
||||
"check": "No PSA labels configured",
|
||||
"severity": "HIGH",
|
||||
"recommendation": (
|
||||
f"Apply PSA labels: kubectl label namespace {name} "
|
||||
f"pod-security.kubernetes.io/enforce=baseline "
|
||||
f"pod-security.kubernetes.io/warn=restricted"
|
||||
),
|
||||
})
|
||||
elif ns["enforce"] == "privileged":
|
||||
findings.append({
|
||||
"namespace": name,
|
||||
"check": "PSA enforce set to privileged (permissive)",
|
||||
"severity": "HIGH",
|
||||
"recommendation": "Upgrade to baseline or restricted enforcement",
|
||||
})
|
||||
elif ns["enforce"] == "baseline" and not ns["warn"]:
|
||||
findings.append({
|
||||
"namespace": name,
|
||||
"check": "Enforce baseline but no warn=restricted",
|
||||
"severity": "MEDIUM",
|
||||
"recommendation": (
|
||||
f"Add warn label: kubectl label namespace {name} "
|
||||
f"pod-security.kubernetes.io/warn=restricted"
|
||||
),
|
||||
})
|
||||
elif ns["enforce"] == "restricted":
|
||||
findings.append({
|
||||
"namespace": name,
|
||||
"check": "PSA enforce=restricted (most secure)",
|
||||
"severity": "INFO",
|
||||
})
|
||||
|
||||
return findings
|
||||
|
||||
def check_compliance(target, token):
|
||||
|
||||
def audit_pod_security(namespace=None):
|
||||
"""Audit pods for security spec violations against PSS profiles."""
|
||||
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
|
||||
cmd = ["get", "pods", "--all-namespaces"] if not namespace else ["get", "pods", "-n", namespace]
|
||||
data = run_kubectl(cmd)
|
||||
if not data:
|
||||
return findings
|
||||
|
||||
for pod in data.get("items", []):
|
||||
pod_name = pod.get("metadata", {}).get("name", "")
|
||||
pod_ns = pod.get("metadata", {}).get("namespace", "")
|
||||
spec = pod.get("spec", {})
|
||||
|
||||
# Check host namespaces
|
||||
if spec.get("hostNetwork"):
|
||||
findings.append({
|
||||
"namespace": pod_ns, "pod": pod_name,
|
||||
"check": "hostNetwork enabled",
|
||||
"severity": "CRITICAL", "profile": "baseline",
|
||||
})
|
||||
if spec.get("hostPID"):
|
||||
findings.append({
|
||||
"namespace": pod_ns, "pod": pod_name,
|
||||
"check": "hostPID enabled",
|
||||
"severity": "CRITICAL", "profile": "baseline",
|
||||
})
|
||||
if spec.get("hostIPC"):
|
||||
findings.append({
|
||||
"namespace": pod_ns, "pod": pod_name,
|
||||
"check": "hostIPC enabled",
|
||||
"severity": "HIGH", "profile": "baseline",
|
||||
})
|
||||
|
||||
containers = spec.get("containers", []) + spec.get("initContainers", [])
|
||||
for container in containers:
|
||||
c_name = container.get("name", "")
|
||||
sec_ctx = container.get("securityContext", {})
|
||||
|
||||
# Privileged container
|
||||
if sec_ctx.get("privileged"):
|
||||
findings.append({
|
||||
"namespace": pod_ns, "pod": pod_name, "container": c_name,
|
||||
"check": "Privileged container",
|
||||
"severity": "CRITICAL", "profile": "baseline",
|
||||
})
|
||||
|
||||
# Run as root
|
||||
if sec_ctx.get("runAsUser") == 0:
|
||||
findings.append({
|
||||
"namespace": pod_ns, "pod": pod_name, "container": c_name,
|
||||
"check": "Running as root (UID 0)",
|
||||
"severity": "HIGH", "profile": "restricted",
|
||||
})
|
||||
|
||||
# Missing runAsNonRoot
|
||||
if not sec_ctx.get("runAsNonRoot"):
|
||||
findings.append({
|
||||
"namespace": pod_ns, "pod": pod_name, "container": c_name,
|
||||
"check": "runAsNonRoot not set",
|
||||
"severity": "MEDIUM", "profile": "restricted",
|
||||
})
|
||||
|
||||
# Writable root filesystem
|
||||
if not sec_ctx.get("readOnlyRootFilesystem"):
|
||||
findings.append({
|
||||
"namespace": pod_ns, "pod": pod_name, "container": c_name,
|
||||
"check": "Root filesystem not read-only",
|
||||
"severity": "MEDIUM", "profile": "restricted",
|
||||
})
|
||||
|
||||
# Privilege escalation
|
||||
if sec_ctx.get("allowPrivilegeEscalation", True):
|
||||
findings.append({
|
||||
"namespace": pod_ns, "pod": pod_name, "container": c_name,
|
||||
"check": "allowPrivilegeEscalation not disabled",
|
||||
"severity": "MEDIUM", "profile": "restricted",
|
||||
})
|
||||
|
||||
# Dangerous capabilities
|
||||
caps = sec_ctx.get("capabilities", {})
|
||||
added = caps.get("add", [])
|
||||
dangerous_caps = {"SYS_ADMIN", "NET_ADMIN", "SYS_PTRACE", "ALL",
|
||||
"NET_RAW", "SYS_RAWIO", "SYS_MODULE"}
|
||||
for cap in added:
|
||||
if cap.upper() in dangerous_caps:
|
||||
findings.append({
|
||||
"namespace": pod_ns, "pod": pod_name, "container": c_name,
|
||||
"check": f"Dangerous capability added: {cap}",
|
||||
"severity": "CRITICAL" if cap.upper() in ("SYS_ADMIN", "ALL") else "HIGH",
|
||||
"profile": "baseline",
|
||||
})
|
||||
|
||||
# Drop ALL capabilities check (restricted)
|
||||
dropped = [c.upper() for c in caps.get("drop", [])]
|
||||
if "ALL" not in dropped:
|
||||
findings.append({
|
||||
"namespace": pod_ns, "pod": pod_name, "container": c_name,
|
||||
"check": "Capabilities not dropping ALL",
|
||||
"severity": "LOW", "profile": "restricted",
|
||||
})
|
||||
|
||||
# Host ports
|
||||
for port in container.get("ports", []):
|
||||
if port.get("hostPort"):
|
||||
findings.append({
|
||||
"namespace": pod_ns, "pod": pod_name, "container": c_name,
|
||||
"check": f"hostPort exposed: {port['hostPort']}",
|
||||
"severity": "HIGH", "profile": "baseline",
|
||||
})
|
||||
|
||||
return findings
|
||||
|
||||
|
||||
def format_summary(ns_findings, pod_findings, namespaces):
|
||||
"""Print audit summary."""
|
||||
print(f"\n{'='*60}")
|
||||
print(f" Pod Security Admission Audit Report")
|
||||
print(f"{'='*60}")
|
||||
print(f" Namespaces : {len(namespaces)}")
|
||||
psa_configured = sum(1 for ns in namespaces if ns["has_psa"])
|
||||
print(f" PSA Configured : {psa_configured}/{len(namespaces)}")
|
||||
print(f" NS Findings : {len(ns_findings)}")
|
||||
print(f" Pod Findings : {len(pod_findings)}")
|
||||
|
||||
all_findings = ns_findings + pod_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", "INFO"]:
|
||||
count = severity_counts.get(sev, 0)
|
||||
if count > 0:
|
||||
print(f" {sev:10s}: {count}")
|
||||
|
||||
print(f"\n Namespace PSA Status:")
|
||||
for ns in namespaces:
|
||||
if ns["name"] in ("kube-system", "kube-public", "kube-node-lease"):
|
||||
continue
|
||||
status = ns["enforce"] or "none"
|
||||
print(f" {ns['name']:30s}: enforce={status:12s} audit={ns['audit'] or 'none':12s}")
|
||||
|
||||
if pod_findings:
|
||||
print(f"\n Top Pod Violations:")
|
||||
for f in pod_findings[:15]:
|
||||
if f["severity"] in ("CRITICAL", "HIGH"):
|
||||
print(f" [{f['severity']:8s}] {f.get('namespace', '')}/"
|
||||
f"{f.get('pod', '')}: {f['check']}")
|
||||
|
||||
return severity_counts
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description="Kubernetes Pod Security Admission 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 Pod Security Admission 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 Pod Security Admission audit agent"
|
||||
)
|
||||
parser.add_argument("--namespace", "-n", help="Specific namespace to audit (default: all)")
|
||||
parser.add_argument("--skip-pods", action="store_true", help="Only audit namespace labels")
|
||||
parser.add_argument("--kubeconfig", help="Path to kubeconfig file")
|
||||
parser.add_argument("--context", help="Kubernetes context to use")
|
||||
parser.add_argument("--output", "-o", help="Output JSON report path")
|
||||
parser.add_argument("--verbose", "-v", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.kubeconfig:
|
||||
os.environ["KUBECONFIG"] = args.kubeconfig
|
||||
|
||||
namespaces = get_namespaces()
|
||||
ns_findings = audit_namespace_psa(namespaces)
|
||||
|
||||
pod_findings = []
|
||||
if not args.skip_pods:
|
||||
pod_findings = audit_pod_security(args.namespace)
|
||||
|
||||
severity_counts = format_summary(ns_findings, pod_findings, namespaces)
|
||||
|
||||
report = {
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
"tool": "PSA Audit",
|
||||
"namespaces": namespaces,
|
||||
"namespace_findings": ns_findings,
|
||||
"pod_findings": pod_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