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
187 lines
8.1 KiB
Python
187 lines
8.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Agent for performing Kubernetes etcd security assessment."""
|
|
|
|
import json
|
|
import argparse
|
|
import os
|
|
import subprocess
|
|
import re
|
|
from datetime import datetime
|
|
|
|
|
|
def check_etcd_encryption(kubeconfig=None):
|
|
"""Check if etcd encryption at rest is configured."""
|
|
cmd = ["kubectl", "get", "apiserver", "-o", "json"]
|
|
if kubeconfig:
|
|
cmd += ["--kubeconfig", kubeconfig]
|
|
try:
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
|
enc_cmd = ["kubectl", "get", "pods", "-n", "kube-system", "-l", "component=kube-apiserver", "-o", "json"]
|
|
if kubeconfig:
|
|
enc_cmd += ["--kubeconfig", kubeconfig]
|
|
result = subprocess.run(enc_cmd, capture_output=True, text=True, timeout=30)
|
|
data = json.loads(result.stdout)
|
|
findings = []
|
|
for pod in data.get("items", []):
|
|
containers = pod.get("spec", {}).get("containers", [])
|
|
for c in containers:
|
|
args_list = c.get("command", []) + c.get("args", [])
|
|
args_str = " ".join(args_list)
|
|
has_encryption = "--encryption-provider-config" in args_str
|
|
has_audit = "--audit-log-path" in args_str
|
|
etcd_servers = re.findall(r"--etcd-servers=([^\s]+)", args_str)
|
|
uses_tls = all("https" in s for s in etcd_servers) if etcd_servers else False
|
|
findings.append({
|
|
"pod": pod.get("metadata", {}).get("name"),
|
|
"encryption_at_rest": has_encryption,
|
|
"audit_logging": has_audit,
|
|
"etcd_tls": uses_tls,
|
|
"etcd_servers": etcd_servers,
|
|
})
|
|
return {"checks": findings, "timestamp": datetime.utcnow().isoformat()}
|
|
except Exception as e:
|
|
return {"error": str(e)}
|
|
|
|
|
|
def check_etcd_access(etcd_endpoint=None, cert=None, key=None, cacert=None):
|
|
etcd_endpoint = etcd_endpoint or os.environ.get("ETCD_ENDPOINT", "https://127.0.0.1:2379")
|
|
"""Test etcd access and check for unauthenticated access."""
|
|
cmd = ["etcdctl", "endpoint", "health", "--endpoints", etcd_endpoint]
|
|
if cert:
|
|
cmd += ["--cert", cert, "--key", key, "--cacert", cacert]
|
|
try:
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=15)
|
|
healthy = "healthy" in result.stdout.lower()
|
|
unauth_cmd = ["etcdctl", "get", "/", "--prefix", "--limit", "1", "--endpoints", etcd_endpoint]
|
|
unauth_result = subprocess.run(unauth_cmd, capture_output=True, text=True, timeout=10)
|
|
unauth_access = unauth_result.returncode == 0 and unauth_result.stdout.strip()
|
|
return {
|
|
"endpoint": etcd_endpoint,
|
|
"healthy": healthy,
|
|
"unauthenticated_access": unauth_access,
|
|
"severity": "CRITICAL" if unauth_access else "INFO",
|
|
"finding": "ETCD_UNAUTHENTICATED_ACCESS" if unauth_access else "ETCD_AUTH_REQUIRED",
|
|
}
|
|
except FileNotFoundError:
|
|
return {"error": "etcdctl not found"}
|
|
except Exception as e:
|
|
return {"error": str(e)}
|
|
|
|
|
|
def dump_secrets_check(kubeconfig=None):
|
|
"""Check if secrets are stored unencrypted in etcd."""
|
|
cmd = ["kubectl", "get", "secrets", "--all-namespaces", "-o", "json"]
|
|
if kubeconfig:
|
|
cmd += ["--kubeconfig", kubeconfig]
|
|
try:
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
|
data = json.loads(result.stdout)
|
|
secrets = data.get("items", [])
|
|
secret_types = {}
|
|
sensitive = []
|
|
for s in secrets:
|
|
stype = s.get("type", "Opaque")
|
|
secret_types[stype] = secret_types.get(stype, 0) + 1
|
|
name = s.get("metadata", {}).get("name", "")
|
|
ns = s.get("metadata", {}).get("namespace", "")
|
|
if any(kw in name.lower() for kw in ["password", "token", "key", "cert", "credential", "tls"]):
|
|
sensitive.append({"name": name, "namespace": ns, "type": stype})
|
|
return {
|
|
"total_secrets": len(secrets),
|
|
"by_type": secret_types,
|
|
"sensitive_secrets": sensitive[:20],
|
|
"recommendation": "Enable EncryptionConfiguration for secrets at rest",
|
|
}
|
|
except Exception as e:
|
|
return {"error": str(e)}
|
|
|
|
|
|
def check_etcd_tls_config():
|
|
"""Verify etcd TLS certificate configuration."""
|
|
cmd = ["kubectl", "get", "pods", "-n", "kube-system", "-l", "component=etcd", "-o", "json"]
|
|
try:
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
|
|
data = json.loads(result.stdout)
|
|
findings = []
|
|
for pod in data.get("items", []):
|
|
for c in pod.get("spec", {}).get("containers", []):
|
|
args_str = " ".join(c.get("command", []) + c.get("args", []))
|
|
peer_tls = "--peer-cert-file" in args_str and "--peer-key-file" in args_str
|
|
client_tls = "--cert-file" in args_str and "--key-file" in args_str
|
|
client_auth = "--client-cert-auth=true" in args_str or "--client-cert-auth true" in args_str
|
|
findings.append({
|
|
"pod": pod.get("metadata", {}).get("name"),
|
|
"peer_tls_enabled": peer_tls,
|
|
"client_tls_enabled": client_tls,
|
|
"client_cert_auth": client_auth,
|
|
"issues": [
|
|
i for i in [
|
|
"NO_PEER_TLS" if not peer_tls else None,
|
|
"NO_CLIENT_TLS" if not client_tls else None,
|
|
"NO_CLIENT_CERT_AUTH" if not client_auth else None,
|
|
] if i
|
|
],
|
|
})
|
|
return {"etcd_tls_checks": findings}
|
|
except Exception as e:
|
|
return {"error": str(e)}
|
|
|
|
|
|
def full_assessment(kubeconfig=None, etcd_endpoint=None):
|
|
"""Run comprehensive etcd security assessment."""
|
|
results = {
|
|
"timestamp": datetime.utcnow().isoformat(),
|
|
"encryption": check_etcd_encryption(kubeconfig),
|
|
"secrets": dump_secrets_check(kubeconfig),
|
|
"tls": check_etcd_tls_config(),
|
|
}
|
|
if etcd_endpoint:
|
|
results["access"] = check_etcd_access(etcd_endpoint)
|
|
issues = []
|
|
enc = results["encryption"]
|
|
if isinstance(enc, dict) and enc.get("checks"):
|
|
for c in enc["checks"]:
|
|
if not c.get("encryption_at_rest"):
|
|
issues.append("ENCRYPTION_AT_REST_DISABLED")
|
|
if not c.get("etcd_tls"):
|
|
issues.append("ETCD_COMMUNICATION_NOT_TLS")
|
|
results["critical_issues"] = list(set(issues))
|
|
results["risk_level"] = "CRITICAL" if issues else "LOW"
|
|
return results
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Kubernetes etcd Security Assessment Agent")
|
|
parser.add_argument("--kubeconfig", help="Path to kubeconfig file")
|
|
sub = parser.add_subparsers(dest="command")
|
|
sub.add_parser("encrypt", help="Check encryption at rest")
|
|
a = sub.add_parser("access", help="Test etcd access")
|
|
a.add_argument("--endpoint", default=os.environ.get("ETCD_ENDPOINT", "https://127.0.0.1:2379"))
|
|
a.add_argument("--cert", help="Client certificate")
|
|
a.add_argument("--key", help="Client key")
|
|
a.add_argument("--cacert", help="CA certificate")
|
|
sub.add_parser("secrets", help="Check secrets storage")
|
|
sub.add_parser("tls", help="Check TLS configuration")
|
|
f = sub.add_parser("full", help="Full assessment")
|
|
f.add_argument("--endpoint", help="etcd endpoint URL")
|
|
args = parser.parse_args()
|
|
kc = args.kubeconfig if hasattr(args, "kubeconfig") else None
|
|
if args.command == "encrypt":
|
|
result = check_etcd_encryption(kc)
|
|
elif args.command == "access":
|
|
result = check_etcd_access(args.endpoint, args.cert, args.key, args.cacert)
|
|
elif args.command == "secrets":
|
|
result = dump_secrets_check(kc)
|
|
elif args.command == "tls":
|
|
result = check_etcd_tls_config()
|
|
elif args.command == "full":
|
|
result = full_assessment(kc, args.endpoint if hasattr(args, "endpoint") else None)
|
|
else:
|
|
parser.print_help()
|
|
return
|
|
print(json.dumps(result, indent=2, default=str))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|