mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-10 21:24:56 +03:00
284 lines
10 KiB
Python
284 lines
10 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Software-Defined Perimeter Deployment Validator
|
|
|
|
Validates SDP deployment readiness, tests SPA mechanisms,
|
|
verifies gateway invisibility, and generates deployment reports.
|
|
"""
|
|
|
|
import json
|
|
import socket
|
|
import hashlib
|
|
import hmac
|
|
import struct
|
|
import time
|
|
import ssl
|
|
import subprocess
|
|
import sys
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
|
|
def check_gateway_invisibility(host: str, port_range: tuple = (1, 1024), timeout: float = 0.5) -> dict:
|
|
"""Scan gateway ports to verify SDP invisibility (all ports should appear closed/filtered)."""
|
|
result = {
|
|
"host": host,
|
|
"scanned_range": f"{port_range[0]}-{port_range[1]}",
|
|
"open_ports": [],
|
|
"closed_ports": 0,
|
|
"filtered_ports": 0,
|
|
"invisible": True,
|
|
"timestamp": datetime.now().isoformat(),
|
|
}
|
|
|
|
for port in range(port_range[0], port_range[1] + 1):
|
|
try:
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
sock.settimeout(timeout)
|
|
conn_result = sock.connect_ex((host, port))
|
|
if conn_result == 0:
|
|
result["open_ports"].append(port)
|
|
result["invisible"] = False
|
|
else:
|
|
result["filtered_ports"] += 1
|
|
sock.close()
|
|
except socket.timeout:
|
|
result["filtered_ports"] += 1
|
|
except OSError:
|
|
result["closed_ports"] += 1
|
|
|
|
return result
|
|
|
|
|
|
def generate_spa_packet(
|
|
source_ip: str,
|
|
destination_service: str,
|
|
shared_key: str,
|
|
timestamp: Optional[float] = None
|
|
) -> bytes:
|
|
"""Generate a Single Packet Authorization payload (demonstration)."""
|
|
if timestamp is None:
|
|
timestamp = time.time()
|
|
|
|
payload = json.dumps({
|
|
"version": 2,
|
|
"source_ip": source_ip,
|
|
"service": destination_service,
|
|
"timestamp": timestamp,
|
|
"nonce": hashlib.sha256(f"{time.time()}{source_ip}".encode()).hexdigest()[:16],
|
|
}).encode()
|
|
|
|
mac = hmac.new(shared_key.encode(), payload, hashlib.sha256).digest()
|
|
packet = struct.pack("!I", len(payload)) + payload + mac
|
|
|
|
return packet
|
|
|
|
|
|
def validate_spa_packet(packet: bytes, shared_key: str, max_age_seconds: int = 60) -> dict:
|
|
"""Validate a received SPA packet."""
|
|
result = {"valid": False, "errors": [], "payload": None}
|
|
|
|
try:
|
|
payload_len = struct.unpack("!I", packet[:4])[0]
|
|
payload = packet[4:4 + payload_len]
|
|
received_mac = packet[4 + payload_len:]
|
|
|
|
expected_mac = hmac.new(shared_key.encode(), payload, hashlib.sha256).digest()
|
|
if not hmac.compare_digest(received_mac, expected_mac):
|
|
result["errors"].append("HMAC verification failed")
|
|
return result
|
|
|
|
data = json.loads(payload.decode())
|
|
result["payload"] = data
|
|
|
|
age = time.time() - data.get("timestamp", 0)
|
|
if age > max_age_seconds:
|
|
result["errors"].append(f"Packet expired ({age:.0f}s old, max {max_age_seconds}s)")
|
|
return result
|
|
|
|
if age < -5:
|
|
result["errors"].append("Packet timestamp is in the future")
|
|
return result
|
|
|
|
result["valid"] = True
|
|
|
|
except (struct.error, json.JSONDecodeError, KeyError) as e:
|
|
result["errors"].append(f"Packet parse error: {str(e)}")
|
|
|
|
return result
|
|
|
|
|
|
def validate_mtls_certificate(host: str, port: int, ca_cert_path: Optional[str] = None) -> dict:
|
|
"""Validate mTLS certificate configuration on SDP gateway."""
|
|
result = {
|
|
"host": host,
|
|
"port": port,
|
|
"tls_configured": False,
|
|
"certificate": None,
|
|
"errors": [],
|
|
}
|
|
|
|
try:
|
|
context = ssl.create_default_context()
|
|
if ca_cert_path:
|
|
context.load_verify_locations(ca_cert_path)
|
|
|
|
with socket.create_connection((host, port), timeout=10) as sock:
|
|
with context.wrap_socket(sock, server_hostname=host) as ssock:
|
|
cert = ssock.getpeercert()
|
|
result["tls_configured"] = True
|
|
result["certificate"] = {
|
|
"subject": dict(x[0] for x in cert.get("subject", [])),
|
|
"issuer": dict(x[0] for x in cert.get("issuer", [])),
|
|
"version": cert.get("version"),
|
|
"not_before": cert.get("notBefore"),
|
|
"not_after": cert.get("notAfter"),
|
|
"serial": cert.get("serialNumber"),
|
|
}
|
|
|
|
not_after = cert.get("notAfter", "")
|
|
if not_after:
|
|
expiry = datetime.strptime(not_after, "%b %d %H:%M:%S %Y %Z")
|
|
days_remaining = (expiry - datetime.utcnow()).days
|
|
result["certificate"]["days_remaining"] = days_remaining
|
|
if days_remaining < 30:
|
|
result["errors"].append(f"Certificate expires in {days_remaining} days")
|
|
|
|
except ssl.SSLError as e:
|
|
result["errors"].append(f"SSL error: {str(e)}")
|
|
except Exception as e:
|
|
result["errors"].append(f"Connection error: {str(e)}")
|
|
|
|
return result
|
|
|
|
|
|
def validate_sdp_config(config: dict) -> dict:
|
|
"""Validate SDP deployment configuration."""
|
|
findings = []
|
|
score = 100
|
|
|
|
controller = config.get("controller", {})
|
|
if not controller.get("ha_enabled"):
|
|
findings.append({"severity": "high", "finding": "Controller HA not enabled"})
|
|
score -= 15
|
|
|
|
if not controller.get("idp_integration"):
|
|
findings.append({"severity": "critical", "finding": "No IdP integration configured"})
|
|
score -= 25
|
|
|
|
if not controller.get("audit_logging"):
|
|
findings.append({"severity": "high", "finding": "Audit logging not enabled on controller"})
|
|
score -= 10
|
|
|
|
gateways = config.get("gateways", [])
|
|
if not gateways:
|
|
findings.append({"severity": "critical", "finding": "No SDP gateways deployed"})
|
|
score -= 25
|
|
for gw in gateways:
|
|
if not gw.get("default_drop"):
|
|
findings.append({"severity": "critical", "finding": f"Gateway {gw.get('name')}: default-drop not enabled"})
|
|
score -= 20
|
|
if not gw.get("spa_enabled"):
|
|
findings.append({"severity": "critical", "finding": f"Gateway {gw.get('name')}: SPA not enabled"})
|
|
score -= 15
|
|
if not gw.get("mtls_enabled"):
|
|
findings.append({"severity": "high", "finding": f"Gateway {gw.get('name')}: mTLS not configured"})
|
|
score -= 10
|
|
|
|
pki = config.get("pki", {})
|
|
cert_lifetime_hours = pki.get("client_cert_lifetime_hours", 8760)
|
|
if cert_lifetime_hours > 72:
|
|
findings.append({
|
|
"severity": "warning",
|
|
"finding": f"Client certificate lifetime is {cert_lifetime_hours}h (recommend <=72h for zero trust)"
|
|
})
|
|
score -= 5
|
|
|
|
if not pki.get("ocsp_enabled") and not pki.get("crl_enabled"):
|
|
findings.append({"severity": "high", "finding": "No certificate revocation checking enabled"})
|
|
score -= 10
|
|
|
|
monitoring = config.get("monitoring", {})
|
|
if not monitoring.get("siem_integration"):
|
|
findings.append({"severity": "warning", "finding": "No SIEM integration for SDP events"})
|
|
score -= 5
|
|
|
|
return {
|
|
"score": max(score, 0),
|
|
"findings": findings,
|
|
"status": "ready" if score >= 80 else "needs_work" if score >= 50 else "not_ready",
|
|
"timestamp": datetime.now().isoformat(),
|
|
}
|
|
|
|
|
|
def generate_sdp_deployment_report(config: dict) -> dict:
|
|
"""Generate comprehensive SDP deployment report."""
|
|
validation = validate_sdp_config(config)
|
|
|
|
applications = config.get("applications", [])
|
|
users = config.get("authorized_users", [])
|
|
|
|
return {
|
|
"generated": datetime.now().isoformat(),
|
|
"deployment_status": validation["status"],
|
|
"security_score": validation["score"],
|
|
"findings": validation["findings"],
|
|
"summary": {
|
|
"controller_ha": config.get("controller", {}).get("ha_enabled", False),
|
|
"gateways_deployed": len(config.get("gateways", [])),
|
|
"applications_protected": len(applications),
|
|
"authorized_users": len(users),
|
|
"spa_enabled": all(g.get("spa_enabled") for g in config.get("gateways", [])),
|
|
"mtls_enabled": all(g.get("mtls_enabled") for g in config.get("gateways", [])),
|
|
},
|
|
"recommendations": [f["finding"] for f in validation["findings"] if f["severity"] in ("critical", "high")],
|
|
}
|
|
|
|
|
|
def main():
|
|
import argparse
|
|
parser = argparse.ArgumentParser(description="SDP Deployment Validator")
|
|
parser.add_argument("--config", type=str, help="Path to SDP configuration JSON")
|
|
parser.add_argument("--scan", type=str, help="Gateway host to scan for invisibility")
|
|
parser.add_argument("--scan-ports", type=str, default="1-1024", help="Port range to scan")
|
|
parser.add_argument("--check-tls", type=str, help="Host:port to check TLS certificate")
|
|
parser.add_argument("--output", type=str, default="sdp_report.json")
|
|
args = parser.parse_args()
|
|
|
|
if args.config:
|
|
with open(args.config) as f:
|
|
config = json.load(f)
|
|
report = generate_sdp_deployment_report(config)
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2)
|
|
print(f"SDP Status: {report['deployment_status']} (Score: {report['security_score']})")
|
|
for r in report["recommendations"]:
|
|
print(f" - {r}")
|
|
|
|
elif args.scan:
|
|
start, end = args.scan_ports.split("-")
|
|
result = check_gateway_invisibility(args.scan, (int(start), int(end)))
|
|
with open(args.output, "w") as f:
|
|
json.dump(result, f, indent=2)
|
|
status = "INVISIBLE" if result["invisible"] else "EXPOSED"
|
|
print(f"Gateway {args.scan}: {status}")
|
|
if result["open_ports"]:
|
|
print(f" Open ports: {result['open_ports']}")
|
|
|
|
elif args.check_tls:
|
|
parts = args.check_tls.split(":")
|
|
host = parts[0]
|
|
port = int(parts[1]) if len(parts) > 1 else 443
|
|
result = validate_mtls_certificate(host, port)
|
|
with open(args.output, "w") as f:
|
|
json.dump(result, f, indent=2)
|
|
print(f"TLS on {host}:{port}: {'configured' if result['tls_configured'] else 'not configured'}")
|
|
|
|
else:
|
|
parser.print_help()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|