#!/usr/bin/env python3 """Central configuration constants for the intrusion-detection system. All tunable values (paths, router credentials, whitelist sources, scan thresholds) live here so they can be maintained and overridden from a single place. Values that make sense per-deployment are read from environment variables; see ``.env.example`` for the supported keys. """ from __future__ import annotations import os # --------------------------------------------------------------------------- # Project root + .env loading # --------------------------------------------------------------------------- PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Load `.env` for local development (python-dotenv, optional dependency). # `load_dotenv` does NOT override variables already present in the system # environment, so production deployments that set these via the OS keep # working without a `.env` file (or even without python-dotenv installed). try: from dotenv import load_dotenv load_dotenv(os.path.join(PROJECT_ROOT, ".env")) except ImportError: pass # --------------------------------------------------------------------------- # Project paths # --------------------------------------------------------------------------- DATA_DIR = os.path.join(PROJECT_ROOT, "data") REPORT_DIR = os.path.join(PROJECT_ROOT, "reports") # Per-analyzer report directories COPYPARTY_REPORT_DIR = os.path.join(REPORT_DIR, "copyparty") NPM_REPORT_DIR = os.path.join(REPORT_DIR, "npm") # Consolidated application outputs (blocklists, shared data) APP_REPORT_DIR = os.path.join(REPORT_DIR, "app") APP_REPORT_DATA_DIR = os.path.join(APP_REPORT_DIR, "data") # MikroTik reports + structured snapshots from each run MIKROTIK_REPORT_DIR = os.path.join(REPORT_DIR, "mikrotik") MIKROTIK_REPORT_DATA_DIR = os.path.join(MIKROTIK_REPORT_DIR, "data") # Common MikroTik state (latest snapshot; overwritten each run, used for # git diffing / auditing changes to firewall rules and address lists). MIKROTIK_DATA_DIR = os.path.join(DATA_DIR, "mikrotik") WHOIS_CACHE_PATH = os.path.join(DATA_DIR, "whois_cache.json") # Incremental-scan state: absolute log path -> last processed byte offset SCAN_STATE_PATH = os.path.join(DATA_DIR, "scan_state.json") # Web UI scan-job history (web_scan.py): persisted so the dashboard keeps # showing recent scans across server restarts. Oldest entries are pruned # once SCAN_JOBS_MAX is exceeded. SCAN_JOBS_PATH = os.path.join(DATA_DIR, "scan_jobs.json") SCAN_JOBS_MAX = 50 # Report retention: per-directory cleanup of generated files under /reports/. # Each directory keeps at most REPORT_RETENTION_MAX_FILES files, all newer # than REPORT_RETENTION_DAYS days (see app/retention.py). REPORT_RETENTION_DAYS = int(os.environ.get("REPORT_RETENTION_DAYS", "30")) REPORT_RETENTION_MAX_FILES = int( os.environ.get("REPORT_RETENTION_MAX_FILES", "10")) # --------------------------------------------------------------------------- # Log sources (configurable via environment) # --------------------------------------------------------------------------- COPYPARTY_LOG_PATH = os.environ.get("COPYPARTY_LOG_PATH", "/srv/projects/copyparty") NPM_LOG_PATH = os.environ.get("NPM_LOG_PATH", "/mnt/backups/npm/logs") # --------------------------------------------------------------------------- # MikroTik router connection (configurable via environment) # --------------------------------------------------------------------------- SSH_PORT = 22 SSH_TIMEOUT = 10 # seconds ROUTER_IP = os.environ.get("ROUTER_IP", "192.168.88.1") ROUTER_USER = os.environ.get("ROUTER_USER", "admin") ROUTER_PASSWORD = os.environ.get("ROUTER_PASSWORD", "M11ffpgm") BLOCKLIST_NAME = os.environ.get("BLOCKLIST_NAME", "Blocked") WHITELIST_NAMES = [ name.strip() for name in os.environ.get( "WHITELIST_NAMES", "Cloudflare,Openfactory").split(",") if name.strip() ] # --------------------------------------------------------------------------- # WHOIS subnet consolidation # --------------------------------------------------------------------------- WHOIS_SERVER = os.environ.get("WHOIS_SERVER", "whois.radb.net") WHOIS_PORT = int(os.environ.get("WHOIS_PORT", "43")) WHOIS_TIMEOUT = float(os.environ.get("WHOIS_TIMEOUT", "3.0")) # seconds # Minimum number of same-subnet IPs before blocking the whole subnet SUBNET_THRESHOLD = int(os.environ.get("SUBNET_THRESHOLD", "3")) # --------------------------------------------------------------------------- # Scan / flagging defaults # --------------------------------------------------------------------------- DEFAULT_TOP = int(os.environ.get("DEFAULT_TOP", "30") ) # top-IP table rows DEFAULT_MIN_DENIED = int(os.environ.get( "DEFAULT_MIN_DENIED", "1")) # copyparty DEFAULT_MIN_ERRORS = int(os.environ.get("DEFAULT_MIN_ERRORS", "5")) # npm DEFAULT_ERROR_RATIO = float(os.environ.get( "DEFAULT_ERROR_RATIO", "0.4")) # npm DEFAULT_TOP_PATHS = int(os.environ.get("DEFAULT_TOP_PATHS", "20")) # --------------------------------------------------------------------------- # Web server (localhost only) # --------------------------------------------------------------------------- # The Flask web UI binds to localhost on a very high port so it is only # reachable from this machine. Do not expose WEB_HOST beyond 127.0.0.1. WEB_HOST = os.environ.get("WEB_HOST", "127.0.0.1") WEB_PORT = int(os.environ.get("WEB_PORT", "54666")) # --------------------------------------------------------------------------- # Whitelist sources (configurable via environment) # --------------------------------------------------------------------------- OPENFACTORY_URL = os.environ.get( "OPENFACTORY_URL", "https://openfactory.net/geofeed.csv") CLOUDFLARE_URL_V4 = os.environ.get( "CLOUDFLARE_URL_V4", "https://www.cloudflare.com/ips-v4") CLOUDFLARE_URL_V6 = os.environ.get( "CLOUDFLARE_URL_V6", "https://www.cloudflare.com/ips-v6") PALOALTO_URL = os.environ.get( "PALOALTO_URL", "https://docs-cortex.paloaltonetworks.com/r/1/Cortex-Xpanse/Scanning-activity", ) # Local whitelist files (checked before remote download) OPENFACTORY_FILE = os.path.join(DATA_DIR, "openfactory", "geofeed.csv") CLOUDFLARE_V4_FILE = os.path.join(DATA_DIR, "cloudflare", "ips-v4.txt") CLOUDFLARE_V6_FILE = os.path.join(DATA_DIR, "cloudflare", "ips-v6.txt") PALOALTO_FILE = os.path.join(DATA_DIR, "paloalto", "scanner-ips.txt")