Files
routeros-suricata-tzsp/app/dev_web.py
T
2026-08-13 15:58:52 +02:00

171 lines
5.1 KiB
Python

from __future__ import annotations
import os
import signal
import threading
import time
from datetime import datetime, timezone
from urllib.parse import urlparse
from .config import Config
from .state import RuntimeStats
from .store import AlertStore
from .webui import WebServer
def _bool_env(name: str, default: bool = False) -> bool:
value = os.getenv(name)
if value is None:
return default
return value.strip().lower() in {"1", "true", "yes", "on"}
def _seed_demo_alert(store: AlertStore) -> None:
if store.summary()["total_alerts"]:
return
event = {
"timestamp": datetime.now(timezone.utc).isoformat(),
"flow_id": "dev-demo",
"src_ip": "192.168.100.10",
"src_port": 51515,
"dest_ip": "203.0.113.10",
"dest_port": 443,
"proto": "TCP",
"alert": {
"signature_id": 1000001,
"signature": "DEV MODE SAMPLE ALERT",
"category": "Development/Test",
"severity": 2,
"action": "allowed",
},
}
store.insert_alert(event, False, None, "development sample")
def _routeros_target(cfg: Config) -> tuple[str, int]:
parsed = urlparse(cfg.routeros_url)
host = parsed.hostname or cfg.routeros_url
port = parsed.port or (443 if parsed.scheme == "https" else 80)
return host, port
def main() -> int:
cfg = Config.from_env()
stop_event = threading.Event()
stats = RuntimeStats()
started_at = datetime.now(timezone.utc)
started_monotonic = time.monotonic()
os.makedirs(os.path.dirname(cfg.db_path) or ".", exist_ok=True)
store = AlertStore(cfg.db_path)
if _bool_env("DEV_SEED_DATA", False):
_seed_demo_alert(store)
routeros_host, routeros_port = _routeros_target(cfg)
def health() -> dict:
return {
"status": "development",
"mode": "web-only-development",
"dev_mode": True,
"operational": True,
"started_at": started_at.isoformat(),
"uptime_seconds": round(time.monotonic() - started_monotonic, 1),
"suricata_running": False,
"suricata_pid": None,
"auto_block": False,
"routeros_configured": False,
"services": {
"web": {
"name": "Web UI / API",
"status": "up",
"details": f"Development server listening on {cfg.web_bind}:{cfg.web_port}",
},
"tzsp": {
"name": "TZSP receiver",
"status": "disabled",
"details": "Disabled in web-only development mode",
},
"tap": {
"name": "TAP interface",
"status": "disabled",
"details": f"{cfg.tap_name} is not created in development mode",
},
"suricata": {
"name": "Suricata IDS",
"status": "disabled",
"details": "Suricata is not started in web-only development mode",
},
"eve": {
"name": "EVE JSON watcher",
"status": "disabled",
"details": "EVE watcher is not started in web-only development mode",
},
"routeros": {
"name": "RouterOS REST integration",
"status": "disabled",
"details": "RouterOS integration and auto-blocking are disabled in development mode",
},
},
"ports": [
{
"name": "Web UI / API",
"direction": "listen",
"protocol": "TCP",
"address": cfg.web_bind,
"port": cfg.web_port,
"status": "up",
},
{
"name": "TZSP receiver",
"direction": "listen",
"protocol": "UDP",
"address": cfg.tzsp_bind,
"port": cfg.tzsp_port,
"status": "disabled",
},
{
"name": "RouterOS REST API",
"direction": "outbound",
"protocol": "TCP",
"address": routeros_host,
"port": routeros_port,
"status": "disabled",
},
],
"runtime": stats.snapshot(),
}
web = WebServer(cfg, store, health)
def request_stop(_signum=None, _frame=None) -> None:
stop_event.set()
signal.signal(signal.SIGTERM, request_stop)
signal.signal(signal.SIGINT, request_stop)
web.start()
print(
f"[dev] web-only mode active at http://{cfg.web_bind}:{cfg.web_port}",
flush=True,
)
try:
while not stop_event.is_set():
time.sleep(0.25)
except KeyboardInterrupt:
stop_event.set()
finally:
try:
web.stop()
finally:
store.close()
return 0
if __name__ == "__main__":
raise SystemExit(main())