#!/usr/bin/env python3 """Nozomi Networks OT Traffic Analysis Agent - monitors ICS protocols and detects anomalies.""" import json import argparse import logging import subprocess from collections import defaultdict from datetime import datetime logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") logger = logging.getLogger(__name__) def nozomi_api(base_url, token, endpoint): cmd = ["curl", "-s", "-k", "-H", f"Authorization: Bearer {token}", f"{base_url}/api/v1{endpoint}"] result = subprocess.run(cmd, capture_output=True, text=True, timeout=120) return json.loads(result.stdout) if result.stdout else {} def get_alerts(base_url, token): return nozomi_api(base_url, token, "/alerts?limit=500") def get_assets(base_url, token): return nozomi_api(base_url, token, "/assets?limit=500") def get_sessions(base_url, token): return nozomi_api(base_url, token, "/sessions?limit=500") def analyze_ot_protocols(sessions): protocol_counts = defaultdict(int) ot_protocols = {"modbus", "s7comm", "dnp3", "opcua", "ethernet/ip", "bacnet", "profinet"} ot_sessions = [] for session in sessions: proto = session.get("protocol", "").lower() protocol_counts[proto] += 1 if proto in ot_protocols: ot_sessions.append({"source": session.get("source_ip", ""), "destination": session.get("destination_ip", ""), "protocol": proto, "bytes": session.get("bytes_total", 0)}) return {"protocol_distribution": dict(protocol_counts), "ot_sessions": len(ot_sessions), "total_sessions": len(sessions)} def detect_anomalies(alerts): anomaly_types = defaultdict(list) for alert in alerts: anomaly_types[alert.get("type_id", "unknown")].append({ "description": alert.get("description", ""), "risk": alert.get("risk", ""), "source": alert.get("source_ip", ""), "timestamp": alert.get("created_at", ""), }) return {cat: {"count": len(items), "samples": items[:3]} for cat, items in anomaly_types.items()} def audit_asset_inventory(assets): by_type = defaultdict(int) by_vendor = defaultdict(int) for asset in assets: by_type[asset.get("type", "unknown")] += 1 by_vendor[asset.get("vendor", "unknown")] += 1 return {"total_assets": len(assets), "by_type": dict(by_type), "by_vendor": dict(sorted(by_vendor.items(), key=lambda x: x[1], reverse=True)[:10])} def generate_report(alerts, sessions, assets, base_url): return { "timestamp": datetime.utcnow().isoformat(), "nozomi_url": base_url, "alert_summary": {"total": len(alerts), "critical": sum(1 for a in alerts if a.get("risk") == "critical")}, "protocol_analysis": analyze_ot_protocols(sessions), "anomalies": detect_anomalies(alerts), "asset_inventory": audit_asset_inventory(assets), } def main(): parser = argparse.ArgumentParser(description="Nozomi Networks OT Traffic Analysis Agent") parser.add_argument("--nozomi-url", required=True, help="Nozomi Guardian URL") parser.add_argument("--token", required=True, help="API bearer token") parser.add_argument("--output", default="nozomi_ot_report.json") args = parser.parse_args() alerts = get_alerts(args.nozomi_url, args.token) sessions = get_sessions(args.nozomi_url, args.token) assets = get_assets(args.nozomi_url, args.token) report = generate_report(alerts, sessions, assets, args.nozomi_url) with open(args.output, "w") as f: json.dump(report, f, indent=2, default=str) logger.info("Nozomi: %d alerts, %d sessions, %d assets", len(alerts), len(sessions), len(assets)) print(json.dumps(report, indent=2, default=str)) if __name__ == "__main__": main()