Files
Anthropic-Cybersecurity-Skills/skills/implementing-zero-trust-dns-with-nextdns/scripts/process.py
T

282 lines
9.4 KiB
Python

#!/usr/bin/env python3
"""
NextDNS Zero Trust DNS Configuration and Monitoring.
Manages NextDNS profiles, analyzes DNS logs for threats,
and generates compliance reports for DNS security posture.
"""
import json
import datetime
from dataclasses import dataclass, field
from pathlib import Path
from typing import Optional
import math
@dataclass
class DNSPolicy:
name: str
security_features: list = field(default_factory=list)
privacy_blocklists: list = field(default_factory=list)
native_tracking: list = field(default_factory=list)
allowlist: list = field(default_factory=list)
denylist: list = field(default_factory=list)
log_retention_days: int = 90
@dataclass
class DNSQueryLog:
timestamp: str
domain: str
query_type: str
status: str # "allowed", "blocked"
blocked_reason: str = ""
device: str = ""
client_ip: str = ""
class NextDNSPolicyGenerator:
"""Generate NextDNS configuration profiles."""
SECURITY_FEATURES = [
"threat_intelligence_feeds",
"ai_driven_threat_detection",
"google_safe_browsing",
"cryptojacking_protection",
"dns_rebinding_protection",
"idn_homograph_attacks",
"typosquatting_protection",
"dga_protection",
"nrd_protection",
"ddns_blocking",
"parked_domains",
"csam_blocking",
]
PRIVACY_BLOCKLISTS = [
"nextdns_recommended",
"oisd_full",
"easyprivacy",
"adguard_dns_filter",
"hagezi_pro",
"steven_black_unified",
]
NATIVE_TRACKING = [
"windows", "apple", "samsung", "xiaomi",
"huawei", "roku", "sonos", "alexa",
]
def create_enterprise_policy(self, name: str) -> DNSPolicy:
return DNSPolicy(
name=name,
security_features=self.SECURITY_FEATURES.copy(),
privacy_blocklists=["nextdns_recommended", "oisd_full", "easyprivacy"],
native_tracking=["windows", "apple"],
log_retention_days=90,
)
def create_strict_policy(self, name: str) -> DNSPolicy:
return DNSPolicy(
name=name,
security_features=self.SECURITY_FEATURES.copy(),
privacy_blocklists=self.PRIVACY_BLOCKLISTS.copy(),
native_tracking=self.NATIVE_TRACKING.copy(),
log_retention_days=365,
)
def create_minimal_policy(self, name: str) -> DNSPolicy:
return DNSPolicy(
name=name,
security_features=[
"threat_intelligence_feeds",
"cryptojacking_protection",
"dns_rebinding_protection",
"dga_protection",
"nrd_protection",
],
privacy_blocklists=["nextdns_recommended"],
native_tracking=[],
log_retention_days=30,
)
def export_policy(self, policy: DNSPolicy, output_path: str):
config = {
"name": policy.name,
"security": {
"features_enabled": policy.security_features,
},
"privacy": {
"blocklists": policy.privacy_blocklists,
"native_tracking_protection": policy.native_tracking,
"block_cname_cloaking": True,
},
"allowlist": policy.allowlist,
"denylist": policy.denylist,
"logging": {
"enabled": True,
"retention_days": policy.log_retention_days,
"storage_location": "US",
},
}
path = Path(output_path)
path.parent.mkdir(parents=True, exist_ok=True)
with open(path, "w") as f:
json.dump(config, f, indent=2)
return config
class DNSLogAnalyzer:
"""Analyze DNS query logs for security threats."""
def __init__(self):
self.logs: list[DNSQueryLog] = []
self.findings: list[dict] = []
def add_log(self, log: DNSQueryLog):
self.logs.append(log)
def calculate_entropy(self, domain: str) -> float:
"""Calculate Shannon entropy of a domain name (DNS tunneling indicator)."""
label = domain.split(".")[0]
if not label:
return 0.0
freq = {}
for c in label:
freq[c] = freq.get(c, 0) + 1
entropy = 0.0
for count in freq.values():
p = count / len(label)
if p > 0:
entropy -= p * math.log2(p)
return entropy
def detect_dns_tunneling(self, threshold: float = 4.0) -> list[dict]:
"""Detect potential DNS tunneling based on subdomain entropy and length."""
suspects = []
for log in self.logs:
if log.status == "blocked":
continue
subdomain = log.domain.split(".")[0]
entropy = self.calculate_entropy(log.domain)
if entropy > threshold and len(subdomain) > 20:
suspects.append({
"domain": log.domain,
"entropy": round(entropy, 2),
"subdomain_length": len(subdomain),
"device": log.device,
"timestamp": log.timestamp,
})
if suspects:
self.findings.append({
"type": "dns_tunneling_suspect",
"severity": "HIGH",
"count": len(suspects),
"details": suspects[:20],
})
return suspects
def detect_dga_domains(self) -> list[dict]:
"""Detect domain generation algorithm patterns."""
suspects = []
for log in self.logs:
parts = log.domain.split(".")
if len(parts) >= 2:
sld = parts[-2]
entropy = self.calculate_entropy(sld + "." + parts[-1])
if entropy > 3.5 and len(sld) > 8 and not any(
c in sld for c in ["-", "_"]
):
digits = sum(1 for c in sld if c.isdigit())
if digits / len(sld) > 0.3:
suspects.append({
"domain": log.domain,
"entropy": round(entropy, 2),
"digit_ratio": round(digits / len(sld), 2),
})
if suspects:
self.findings.append({
"type": "dga_domain_suspect",
"severity": "HIGH",
"count": len(suspects),
"details": suspects[:20],
})
return suspects
def get_blocked_summary(self) -> dict:
"""Summarize blocked queries by reason."""
blocked = [l for l in self.logs if l.status == "blocked"]
by_reason = {}
for log in blocked:
reason = log.blocked_reason or "unknown"
by_reason[reason] = by_reason.get(reason, 0) + 1
by_domain = {}
for log in blocked:
by_domain[log.domain] = by_domain.get(log.domain, 0) + 1
top_blocked = sorted(by_domain.items(), key=lambda x: x[1], reverse=True)[:20]
return {
"total_blocked": len(blocked),
"total_allowed": len(self.logs) - len(blocked),
"block_rate": round(len(blocked) / len(self.logs) * 100, 2) if self.logs else 0,
"by_reason": by_reason,
"top_blocked_domains": dict(top_blocked),
}
def generate_report(self) -> dict:
return {
"report_date": datetime.datetime.now().isoformat(),
"total_queries": len(self.logs),
"blocked_summary": self.get_blocked_summary(),
"security_findings": self.findings,
"unique_devices": len(set(l.device for l in self.logs if l.device)),
"unique_domains": len(set(l.domain for l in self.logs)),
}
def main():
"""Demonstrate NextDNS policy generation and DNS log analysis."""
# Generate policies
gen = NextDNSPolicyGenerator()
enterprise = gen.create_enterprise_policy("Enterprise Standard")
enterprise.allowlist = ["login.microsoftonline.com", "graph.microsoft.com", "*.company.com"]
enterprise.denylist = ["malware-c2.example.com", "data-exfil.example.com"]
config = gen.export_policy(enterprise, "nextdns_enterprise_policy.json")
print("Enterprise DNS Policy:")
print(json.dumps(config, indent=2))
# Analyze sample DNS logs
analyzer = DNSLogAnalyzer()
sample_logs = [
DNSQueryLog("2024-01-15T10:00:00Z", "google.com", "A", "allowed", device="laptop-1"),
DNSQueryLog("2024-01-15T10:00:01Z", "malware.example.com", "A", "blocked",
"threat_intelligence", "laptop-2"),
DNSQueryLog("2024-01-15T10:00:02Z", "aHR0cHM6Ly9leGFtcGxlLmNvbQ.tunnel.evil.com",
"TXT", "allowed", device="server-1"),
DNSQueryLog("2024-01-15T10:00:03Z", "x8k3m9p2q5.botnet.com", "A", "blocked",
"dga_protection", "laptop-3"),
DNSQueryLog("2024-01-15T10:00:04Z", "office.com", "A", "allowed", device="laptop-1"),
DNSQueryLog("2024-01-15T10:00:05Z", "ads.tracker.com", "A", "blocked",
"ad_blocker", "laptop-1"),
]
for log in sample_logs:
analyzer.add_log(log)
analyzer.detect_dns_tunneling()
analyzer.detect_dga_domains()
report = analyzer.generate_report()
print("\n" + "=" * 60)
print("DNS Security Analysis Report")
print("=" * 60)
print(json.dumps(report, indent=2))
if __name__ == "__main__":
main()