mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-12 22:24:56 +03:00
c47eed6a64
- 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
211 lines
7.7 KiB
Python
211 lines
7.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Agent for auditing and generating Calico Kubernetes network policies."""
|
|
|
|
import json
|
|
import argparse
|
|
import subprocess
|
|
from datetime import datetime
|
|
|
|
|
|
def kubectl_get(resource, namespace=None, label_selector=None):
|
|
"""Get Kubernetes resources via kubectl."""
|
|
cmd = ["kubectl", "get", resource, "-o", "json"]
|
|
if namespace:
|
|
cmd.extend(["-n", namespace])
|
|
else:
|
|
cmd.append("--all-namespaces")
|
|
if label_selector:
|
|
cmd.extend(["-l", label_selector])
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
|
|
if result.returncode != 0:
|
|
return {"error": result.stderr.strip()}
|
|
return json.loads(result.stdout) if result.stdout.strip() else {}
|
|
|
|
|
|
def list_network_policies():
|
|
"""List all NetworkPolicy and Calico GlobalNetworkPolicy resources."""
|
|
k8s_np = kubectl_get("networkpolicy")
|
|
calico_gnp = kubectl_get("globalnetworkpolicy")
|
|
return {
|
|
"k8s_network_policies": k8s_np.get("items", []) if isinstance(k8s_np, dict) else [],
|
|
"calico_global_policies": calico_gnp.get("items", []) if isinstance(calico_gnp, dict) else [],
|
|
}
|
|
|
|
|
|
def audit_network_policies(policies_path):
|
|
"""Audit network policies for security gaps."""
|
|
with open(policies_path) as f:
|
|
data = json.load(f)
|
|
policies = data if isinstance(data, list) else data.get("items", data.get("policies", []))
|
|
findings = []
|
|
namespaces_covered = set()
|
|
|
|
for policy in policies:
|
|
metadata = policy.get("metadata", {})
|
|
spec = policy.get("spec", {})
|
|
ns = metadata.get("namespace", "default")
|
|
namespaces_covered.add(ns)
|
|
|
|
policy_types = spec.get("policyTypes", [])
|
|
if "Ingress" not in policy_types and "Egress" not in policy_types:
|
|
findings.append({
|
|
"policy": metadata.get("name", ""),
|
|
"namespace": ns,
|
|
"issue": "No policyTypes defined (defaults to ingress-only)",
|
|
"severity": "MEDIUM",
|
|
})
|
|
|
|
if "Egress" not in policy_types:
|
|
findings.append({
|
|
"policy": metadata.get("name", ""),
|
|
"namespace": ns,
|
|
"issue": "No egress policy - all outbound traffic allowed",
|
|
"severity": "HIGH",
|
|
})
|
|
|
|
ingress_rules = spec.get("ingress", [])
|
|
for rule in ingress_rules:
|
|
if not rule.get("from"):
|
|
findings.append({
|
|
"policy": metadata.get("name", ""),
|
|
"issue": "Ingress rule allows from all sources",
|
|
"severity": "HIGH",
|
|
})
|
|
|
|
egress_rules = spec.get("egress", [])
|
|
for rule in egress_rules:
|
|
if not rule.get("to"):
|
|
findings.append({
|
|
"policy": metadata.get("name", ""),
|
|
"issue": "Egress rule allows to all destinations",
|
|
"severity": "MEDIUM",
|
|
})
|
|
|
|
return {"findings": findings, "namespaces_covered": list(namespaces_covered),
|
|
"total_policies": len(policies)}
|
|
|
|
|
|
def generate_default_deny(namespace):
|
|
"""Generate a default deny-all NetworkPolicy for a namespace."""
|
|
return {
|
|
"apiVersion": "networking.k8s.io/v1",
|
|
"kind": "NetworkPolicy",
|
|
"metadata": {"name": "default-deny-all", "namespace": namespace},
|
|
"spec": {
|
|
"podSelector": {},
|
|
"policyTypes": ["Ingress", "Egress"],
|
|
},
|
|
}
|
|
|
|
|
|
def generate_allow_dns_egress(namespace):
|
|
"""Generate a policy allowing DNS egress to kube-dns."""
|
|
return {
|
|
"apiVersion": "networking.k8s.io/v1",
|
|
"kind": "NetworkPolicy",
|
|
"metadata": {"name": "allow-dns-egress", "namespace": namespace},
|
|
"spec": {
|
|
"podSelector": {},
|
|
"policyTypes": ["Egress"],
|
|
"egress": [{
|
|
"to": [{"namespaceSelector": {"matchLabels": {
|
|
"kubernetes.io/metadata.name": "kube-system"}}}],
|
|
"ports": [{"protocol": "UDP", "port": 53},
|
|
{"protocol": "TCP", "port": 53}],
|
|
}],
|
|
},
|
|
}
|
|
|
|
|
|
def generate_app_policy(namespace, app_label, allowed_ingress_labels=None,
|
|
allowed_egress_ports=None):
|
|
"""Generate a Calico-style network policy for an application."""
|
|
policy = {
|
|
"apiVersion": "networking.k8s.io/v1",
|
|
"kind": "NetworkPolicy",
|
|
"metadata": {"name": f"allow-{app_label}", "namespace": namespace},
|
|
"spec": {
|
|
"podSelector": {"matchLabels": {"app": app_label}},
|
|
"policyTypes": ["Ingress", "Egress"],
|
|
"ingress": [],
|
|
"egress": [],
|
|
},
|
|
}
|
|
if allowed_ingress_labels:
|
|
for label in allowed_ingress_labels:
|
|
policy["spec"]["ingress"].append({
|
|
"from": [{"podSelector": {"matchLabels": {"app": label}}}],
|
|
})
|
|
if allowed_egress_ports:
|
|
for port_info in allowed_egress_ports:
|
|
policy["spec"]["egress"].append({
|
|
"ports": [{"protocol": port_info.get("protocol", "TCP"),
|
|
"port": port_info["port"]}],
|
|
})
|
|
return policy
|
|
|
|
|
|
def check_unprotected_namespaces():
|
|
"""Find namespaces without any network policies."""
|
|
namespaces = kubectl_get("namespaces")
|
|
policies = kubectl_get("networkpolicy")
|
|
if isinstance(namespaces, dict) and "error" not in namespaces:
|
|
all_ns = {item["metadata"]["name"] for item in namespaces.get("items", [])}
|
|
else:
|
|
return {"error": "Cannot list namespaces"}
|
|
|
|
protected_ns = set()
|
|
if isinstance(policies, dict) and "error" not in policies:
|
|
for item in policies.get("items", []):
|
|
protected_ns.add(item.get("metadata", {}).get("namespace", "default"))
|
|
|
|
system_ns = {"kube-system", "kube-public", "kube-node-lease"}
|
|
unprotected = all_ns - protected_ns - system_ns
|
|
return {
|
|
"total_namespaces": len(all_ns),
|
|
"protected": len(protected_ns),
|
|
"unprotected": list(unprotected),
|
|
"severity": "HIGH" if unprotected else "INFO",
|
|
}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Calico Network Policy Agent")
|
|
parser.add_argument("--audit", help="Network policies JSON to audit")
|
|
parser.add_argument("--namespace", help="Namespace for policy generation")
|
|
parser.add_argument("--app", help="App label for policy generation")
|
|
parser.add_argument("--action", choices=["audit", "generate", "check", "full"],
|
|
default="full")
|
|
parser.add_argument("--output", default="calico_netpol_report.json")
|
|
args = parser.parse_args()
|
|
|
|
report = {"generated_at": datetime.utcnow().isoformat(), "results": {}}
|
|
|
|
if args.action in ("audit", "full") and args.audit:
|
|
result = audit_network_policies(args.audit)
|
|
report["results"]["audit"] = result
|
|
print(f"[+] Audit: {len(result['findings'])} findings across {result['total_policies']} policies")
|
|
|
|
if args.action in ("generate", "full") and args.namespace:
|
|
policies = [
|
|
generate_default_deny(args.namespace),
|
|
generate_allow_dns_egress(args.namespace),
|
|
]
|
|
if args.app:
|
|
policies.append(generate_app_policy(args.namespace, args.app))
|
|
report["results"]["generated"] = policies
|
|
print(f"[+] Generated {len(policies)} policies for {args.namespace}")
|
|
|
|
if args.action in ("check", "full"):
|
|
result = check_unprotected_namespaces()
|
|
report["results"]["unprotected"] = result
|
|
print(f"[+] Unprotected namespaces: {result.get('unprotected', [])}")
|
|
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2, default=str)
|
|
print(f"[+] Report saved to {args.output}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|