first commit
This commit is contained in:
+170
@@ -0,0 +1,170 @@
|
||||
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())
|
||||
Reference in New Issue
Block a user