Files
bigbrother/utils/config_loader.py
T
n0mad1k ccc6b729de Initial public release
Full BigBrother network implant - passive SOC + active exploitation.
Personal identifiers removed; all capabilities intact.
See README.md for setup and docs/deployment.md for detailed deployment.
2026-06-26 09:52:50 -04:00

180 lines
6.1 KiB
Python

#!/usr/bin/env python3
"""YAML config loader with merge hierarchy, hardware tier detection, and SIGHUP reload."""
import copy
import os
import signal
import threading
from pathlib import Path
from typing import Any, Callable, Dict, List, Optional
import yaml
from utils.resource import get_hardware_tier
CONFIG_DIR = "config"
MERGE_ORDER = [
"hardware_tiers.yaml",
"bigbrother.yaml",
"modules.yaml",
"stealth.yaml",
]
def deep_merge(base: dict, override: dict) -> dict:
result = copy.deepcopy(base)
for key, value in override.items():
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
result[key] = deep_merge(result[key], value)
else:
result[key] = copy.deepcopy(value)
return result
class ConfigLoader:
def __init__(
self,
config_dir: str = CONFIG_DIR,
cli_overrides: Optional[Dict[str, Any]] = None,
auto_detect_tier: bool = True,
):
self.config_dir = Path(config_dir)
self.cli_overrides = cli_overrides or {}
self.auto_detect_tier = auto_detect_tier
self._config: Dict[str, Any] = {}
self._lock = threading.Lock()
self._reload_callbacks: List[Callable] = []
self._loaded = False
def load(self) -> Dict[str, Any]:
with self._lock:
self._config = self._build_config()
self._loaded = True
return copy.deepcopy(self._config)
def _build_config(self) -> Dict[str, Any]:
merged: Dict[str, Any] = {}
for filename in MERGE_ORDER:
filepath = self.config_dir / filename
if filepath.exists():
try:
with open(filepath, "r") as f:
data = yaml.safe_load(f) or {}
merged = deep_merge(merged, data)
except (yaml.YAMLError, IOError):
continue
# Apply hardware tier defaults
if self.auto_detect_tier:
merged = self._apply_tier_limits(merged)
# Apply CLI overrides last (highest priority)
if self.cli_overrides:
merged = deep_merge(merged, self.cli_overrides)
# Validate
self._validate(merged)
return merged
def _apply_tier_limits(self, config: dict) -> dict:
tier = config.get("device", {}).get("platform", "auto")
if tier == "auto":
tier = get_hardware_tier()
config.setdefault("device", {})["platform"] = tier
# Extract tier-specific limits from hardware_tiers section
tiers = config.pop("hardware_tiers", {})
tier_config = tiers.get(tier, tiers.get("generic", {}))
if tier_config:
config = deep_merge(config, tier_config)
# Apply well-known tier constraints
if tier == "pi_zero":
config.setdefault("capture", {})["pcap_compression_level"] = 3
config.setdefault("security", {})["encryption_key_derive"] = "pbkdf2"
# Limit passive modules
limits = config.setdefault("limits", {})
limits.setdefault("max_passive_modules", 4)
limits.setdefault("max_active_modules", 0)
elif tier == "opi_zero3":
config.setdefault("capture", {})["pcap_compression_level"] = 19
config.setdefault("security", {})["encryption_key_derive"] = "argon2id"
limits = config.setdefault("limits", {})
limits.setdefault("max_passive_modules", 16)
limits.setdefault("max_active_modules", 6)
else:
config.setdefault("capture", {})["pcap_compression_level"] = 19
config.setdefault("security", {})["encryption_key_derive"] = "argon2id"
limits = config.setdefault("limits", {})
limits.setdefault("max_passive_modules", 16)
limits.setdefault("max_active_modules", -1) # unlimited
return config
def _validate(self, config: dict) -> None:
device = config.get("device", {})
platform = device.get("platform", "")
if platform and platform not in ("auto", "opi_zero3", "pi_zero", "generic"):
raise ValueError(f"Invalid platform: {platform}")
capture = config.get("capture", {})
comp_level = capture.get("pcap_compression_level")
if comp_level is not None and not (1 <= comp_level <= 22):
raise ValueError(f"Invalid compression level: {comp_level}")
bettercap = config.get("bettercap", {})
port = bettercap.get("api_port")
if port is not None and not (1 <= port <= 65535):
raise ValueError(f"Invalid bettercap API port: {port}")
security = config.get("security", {})
kdf = security.get("encryption_key_derive")
if kdf and kdf not in ("argon2id", "pbkdf2"):
raise ValueError(f"Invalid key derivation method: {kdf}")
def get(self, key_path: str, default: Any = None) -> Any:
with self._lock:
keys = key_path.split(".")
current = self._config
for k in keys:
if isinstance(current, dict) and k in current:
current = current[k]
else:
return default
return copy.deepcopy(current)
def reload(self) -> Dict[str, Any]:
config = self.load()
for cb in self._reload_callbacks:
try:
cb(config)
except Exception:
pass
return config
def on_reload(self, callback: Callable):
self._reload_callbacks.append(callback)
def register_sighup(self):
def _handler(signum, frame):
self.reload()
signal.signal(signal.SIGHUP, _handler)
@property
def config(self) -> Dict[str, Any]:
with self._lock:
if not self._loaded:
return self.load()
return copy.deepcopy(self._config)
def load_config(
config_dir: str = CONFIG_DIR,
cli_overrides: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
loader = ConfigLoader(config_dir=config_dir, cli_overrides=cli_overrides)
return loader.load()