#!/usr/bin/env python3 """Agent for auditing and hardening Docker daemon configuration.""" import argparse import json import os from datetime import datetime, timezone DAEMON_JSON_PATH = os.environ.get("DOCKER_DAEMON_JSON", "/etc/docker/daemon.json") RECOMMENDED_SETTINGS = { "icc": False, "live-restore": True, "userland-proxy": False, "no-new-privileges": True, "userns-remap": "default", "log-driver": "json-file", "log-opts": {"max-size": "10m", "max-file": "3"}, } def read_daemon_config(): """Read Docker daemon.json configuration.""" if not os.path.isfile(DAEMON_JSON_PATH): return {"error": f"{DAEMON_JSON_PATH} not found"} try: with open(DAEMON_JSON_PATH, "r") as f: return json.load(f) except (json.JSONDecodeError, PermissionError) as e: return {"error": str(e)} def audit_daemon_config(config): """Audit daemon.json against CIS benchmarks.""" findings = [] if "error" in config: findings.append({"check": "daemon.json", "issue": config["error"], "severity": "HIGH"}) return findings if config.get("icc", True): findings.append({"check": "CIS 2.1", "issue": "Inter-container communication enabled", "severity": "MEDIUM"}) if not config.get("live-restore"): findings.append({"check": "CIS 2.2", "issue": "Live restore not enabled", "severity": "LOW"}) if config.get("userland-proxy", True): findings.append({"check": "CIS 2.3", "issue": "Userland proxy enabled (use iptables)", "severity": "LOW"}) if not config.get("no-new-privileges"): findings.append({"check": "CIS 2.4", "issue": "no-new-privileges not set", "severity": "MEDIUM"}) if "userns-remap" not in config: findings.append({"check": "CIS 2.8", "issue": "User namespace remapping not configured", "severity": "MEDIUM"}) if not config.get("tls"): if not config.get("tlsverify"): findings.append({"check": "CIS 2.6", "issue": "TLS not configured for Docker daemon", "severity": "HIGH"}) log_driver = config.get("log-driver", "") if not log_driver: findings.append({"check": "CIS 2.12", "issue": "No log driver configured", "severity": "MEDIUM"}) if config.get("insecure-registries"): findings.append({"check": "CIS 2.4", "issue": f"Insecure registries: {config['insecure-registries']}", "severity": "HIGH"}) return findings def check_docker_socket(): """Check Docker socket permissions.""" findings = [] socket_path = "/var/run/docker.sock" if os.path.exists(socket_path): stat = os.stat(socket_path) mode = oct(stat.st_mode)[-3:] if mode != "660": findings.append({ "check": "CIS 3.3", "issue": f"Docker socket permissions: {mode} (should be 660)", "severity": "HIGH", }) return findings def check_docker_files(): """Audit Docker configuration file permissions.""" findings = [] files_to_check = { "/etc/docker/daemon.json": "644", "/etc/default/docker": "644", "/etc/docker/certs.d": "444", } for fpath, expected in files_to_check.items(): if os.path.exists(fpath): stat = os.stat(fpath) mode = oct(stat.st_mode)[-3:] if mode > expected: findings.append({ "check": "CIS 3.x", "issue": f"{fpath}: permissions {mode} (should be {expected})", "severity": "MEDIUM", }) return findings def main(): global DAEMON_JSON_PATH parser = argparse.ArgumentParser( description="Audit Docker daemon configuration against CIS benchmarks" ) parser.add_argument("--config", default=DAEMON_JSON_PATH, help="Path to daemon.json") parser.add_argument("--output", "-o", help="Output JSON report") args = parser.parse_args() print("[*] Docker Daemon Configuration Audit Agent") DAEMON_JSON_PATH = args.config config = read_daemon_config() findings = audit_daemon_config(config) findings.extend(check_docker_socket()) findings.extend(check_docker_files()) report = { "timestamp": datetime.now(timezone.utc).isoformat(), "daemon_config": config if "error" not in config else {}, "findings": findings, "finding_count": len(findings), } critical = sum(1 for f in findings if f["severity"] == "HIGH") print(f"[*] Findings: {len(findings)} (HIGH: {critical})") if args.output: with open(args.output, "w") as f: json.dump(report, f, indent=2) print(f"[*] Report saved to {args.output}") else: print(json.dumps(report, indent=2)) if __name__ == "__main__": main()