"""Configuration loader for camera server module - reads from .env file.""" import os import sys import warnings from pathlib import Path from dotenv import load_dotenv # Load .env file from c2 root directory C2_ROOT = Path(__file__).parent.parent ENV_FILE = C2_ROOT / ".env" _ENV_LOADED = False if ENV_FILE.exists(): load_dotenv(ENV_FILE) _ENV_LOADED = True else: # Try .env.example as fallback for development example_env = C2_ROOT / ".env.example" if example_env.exists(): load_dotenv(example_env) warnings.warn( "No .env file found — loaded .env.example defaults. " "Copy .env.example to .env and set secure values before production use.", stacklevel=2, ) def _get_bool(key: str, default: bool = False) -> bool: """Get boolean value from environment.""" val = os.getenv(key, str(default)).lower() return val in ("true", "1", "yes", "on") def _get_int(key: str, default: int) -> int: """Get integer value from environment.""" try: return int(os.getenv(key, default)) except ValueError: return default # C2 Server C2_HOST = os.getenv("C2_HOST", "0.0.0.0") C2_PORT = _get_int("C2_PORT", 2626) # UDP Server configuration UDP_HOST = os.getenv("UDP_HOST", "0.0.0.0") UDP_PORT = _get_int("UDP_PORT", 5000) UDP_BUFFER_SIZE = _get_int("UDP_BUFFER_SIZE", 65535) # Flask Web Server configuration WEB_HOST = os.getenv("WEB_HOST", "0.0.0.0") WEB_PORT = _get_int("WEB_PORT", 8000) # Security SECRET_TOKEN = os.getenv("CAMERA_SECRET_TOKEN", "Sup3rS3cretT0k3n").encode() FLASK_SECRET_KEY = os.getenv("FLASK_SECRET_KEY", "change_this_for_prod") # Credentials DEFAULT_USERNAME = os.getenv("WEB_USERNAME", "admin") DEFAULT_PASSWORD = os.getenv("WEB_PASSWORD", "admin") # Storage paths IMAGE_DIR = os.getenv("IMAGE_DIR", str(C2_ROOT / "static" / "streams")) # Video recording VIDEO_ENABLED = _get_bool("VIDEO_ENABLED", True) VIDEO_PATH = os.getenv("VIDEO_PATH", str(C2_ROOT / "static" / "streams" / "record.avi")) VIDEO_FPS = _get_int("VIDEO_FPS", 10) VIDEO_CODEC = os.getenv("VIDEO_CODEC", "MJPG") # Multilateration MULTILAT_AUTH_TOKEN = os.getenv("MULTILAT_AUTH_TOKEN", "multilat_secret_token") # Rate Limiting RATE_LIMIT_DEFAULT = os.getenv("RATE_LIMIT_DEFAULT", "200 per minute") RATE_LIMIT_LOGIN = os.getenv("RATE_LIMIT_LOGIN", "5 per minute") # CORS CORS_ALLOWED_ORIGINS = [ o.strip() for o in os.getenv("CORS_ALLOWED_ORIGINS", "").split(",") if o.strip() ] # Tunnel / SOCKS5 Proxy TUNNEL_SOCKS_HOST = os.getenv("TUNNEL_SOCKS_HOST", "127.0.0.1") TUNNEL_SOCKS_PORT = _get_int("TUNNEL_SOCKS_PORT", 1080) TUNNEL_LISTEN_PORT = _get_int("TUNNEL_LISTEN_PORT", 2627) # Insecure default values that must be changed for production _INSECURE_DEFAULTS = { "FLASK_SECRET_KEY": "change_this_for_prod", "CAMERA_SECRET_TOKEN": "Sup3rS3cretT0k3n", "MULTILAT_AUTH_TOKEN": "multilat_secret_token", } def validate_config(): """Check that no insecure default values are still in use. Prints warnings for each insecure default found. Returns True if configuration is safe, False otherwise. """ insecure = [] if FLASK_SECRET_KEY == _INSECURE_DEFAULTS["FLASK_SECRET_KEY"]: insecure.append("FLASK_SECRET_KEY") if SECRET_TOKEN == _INSECURE_DEFAULTS["CAMERA_SECRET_TOKEN"].encode(): insecure.append("CAMERA_SECRET_TOKEN") if MULTILAT_AUTH_TOKEN == _INSECURE_DEFAULTS["MULTILAT_AUTH_TOKEN"]: insecure.append("MULTILAT_AUTH_TOKEN") if DEFAULT_USERNAME == "admin" and DEFAULT_PASSWORD == "admin": insecure.append("WEB_USERNAME/WEB_PASSWORD (admin/admin)") if insecure: print("\n" + "=" * 60) print(" WARNING: Insecure default values detected!") print("=" * 60) for name in insecure: print(f" - {name}") print() print(" Set these in your .env file before production use.") print(" See .env.example for reference.") print("=" * 60 + "\n") return False return True