From 263e5a50d02063f888bb5bbd8f211da943c04f69 Mon Sep 17 00:00:00 2001 From: n0mad1k Date: Sun, 22 Mar 2026 20:19:57 -0400 Subject: [PATCH] Add pi3b hardware tier and resource guards for condo Pi deployment - Add pi3b tier (700MB RAM limit, 8 passive/2 active modules, no bettercap/mitmproxy) - Update detect_hardware_tier() to detect Raspberry Pi 3B by model string and BCM2837 - Add pi3b section to hardware_tiers.yaml - Add systemd MemoryMax/CPUQuota/OOMScoreAdj to all three service files - Fix bigbrother-watchdog.service ExecStart (was calling non-existent ResourceMonitor().run()) - Add scripts/run_watchdog.py as standalone health monitor daemon --- config/hardware_tiers.yaml | 21 +++++ core/engine.py | 17 +++- scripts/run_watchdog.py | 114 +++++++++++++++++++++++++++ services/bigbrother-capture.service | 3 + services/bigbrother-core.service | 4 + services/bigbrother-watchdog.service | 5 +- 6 files changed, 162 insertions(+), 2 deletions(-) create mode 100644 scripts/run_watchdog.py diff --git a/config/hardware_tiers.yaml b/config/hardware_tiers.yaml index d656799..56d4d69 100644 --- a/config/hardware_tiers.yaml +++ b/config/hardware_tiers.yaml @@ -23,6 +23,27 @@ opi_zero3: wifi_attacks: true # Built-in WiFi 5 supports evil twin inline_bridge: true # Via USB Ethernet adapter +# --------------------------------------------------------------------------- +# Raspberry Pi 3B — Condo / Secondary Implant +# BCM2837B0, 4x Cortex-A53 @ 1.2GHz, WiFi 4 + BT 4.2, 1x 100M Ethernet +# 1GB RAM — passive sniffing + limited active capability +# OS: Ubuntu 24.04 LTS (arm64) +# --------------------------------------------------------------------------- +pi3b: + max_passive: 8 # 8 passive modules (RAM-limited) + max_active: 2 # Minimal active capability + max_ram_mb: 700 # 900MB total - ~200MB OS overhead + zstd_level: 3 # Fast compression (CPU-limited) + kdf: "pbkdf2" # argon2id too expensive for 1GB + mitmproxy: false # Too RAM-heavy + lkm: false # No kernel headers + cellular: false # No USB header + overlayfs_mb: 64 # Small tmpfs overlay + thermal_warning: 60 # Celsius — Pi 3B runs warm + thermal_shed: 75 # Shed modules at this temp + wifi_attacks: false # Single wlan0 = uplink, can't spare for monitor + inline_bridge: false # eth0 is down at condo + # --------------------------------------------------------------------------- # Raspberry Pi Zero 2W — Jumpbox / Beacon # BCM2710A1, 4x Cortex-A53 @ 1.0GHz, WiFi 4, no Ethernet (USB adapter) diff --git a/core/engine.py b/core/engine.py index e5a660a..9baddb6 100644 --- a/core/engine.py +++ b/core/engine.py @@ -37,6 +37,14 @@ HARDWARE_TIERS = { "allow_mitmproxy": True, "allow_bettercap": True, }, + "pi3b": { + "max_passive": 8, + "max_active": 2, + "max_ram_mb": 700, # 900MB total - ~200MB OS overhead + "max_cpu_pct": 75, + "allow_mitmproxy": False, + "allow_bettercap": False, + }, "pi_zero": { "max_passive": 4, "max_active": 0, @@ -78,9 +86,16 @@ def detect_hardware_tier() -> str: if "allwinner" in cpuinfo and "h618" in cpuinfo: return "opi_zero3" - if "raspberry pi zero 2" in model or "bcm2710" in cpuinfo: + if "raspberry pi zero 2" in model: return "pi_zero" + if "raspberry pi 3" in model: + return "pi3b" + + # BCM2710/BCM2837 without a clear model string — Pi 3B family + if "bcm2837" in cpuinfo or ("bcm2710" in cpuinfo and "raspberry pi zero 2" not in model): + return "pi3b" + return "generic" diff --git a/scripts/run_watchdog.py b/scripts/run_watchdog.py new file mode 100644 index 0000000..51eb74b --- /dev/null +++ b/scripts/run_watchdog.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 +"""Standalone resource monitor daemon for bigbrother-watchdog.service. + +Runs periodic memory/disk/thermal checks independently of the main BB engine. +Logs warnings and kills the lowest-priority BB process on OOM. +""" + +import logging +import os +import signal +import sys +import threading +import time + +# Ensure /opt/.cache/bb is on the path when run from the service +sys.path.insert(0, "/opt/.cache/bb") + +logging.basicConfig( + level=logging.WARNING, + format="%(asctime)s %(levelname)s %(name)s: %(message)s", +) +logger = logging.getLogger("bb.watchdog") + +CHECK_INTERVAL = 15.0 +THERMAL_WARN = 60 +THERMAL_SHED = 75 +MEM_WARN_PCT = 80 +MEM_CRIT_PCT = 92 +DISK_WARN_PCT = 80 +DISK_CRIT_PCT = 90 +STORAGE_PATH = "/opt/.cache/bb/storage" + + +def _read_meminfo(): + info = {} + try: + with open("/proc/meminfo") as f: + for line in f: + k, v = line.split(":", 1) + info[k.strip()] = int(v.split()[0]) + except Exception: + pass + return info + + +def _read_temp(): + base = "/sys/class/thermal" + try: + import os as _os + for zone in sorted(_os.listdir(base)): + p = f"{base}/{zone}/temp" + if _os.path.exists(p): + return int(open(p).read().strip()) / 1000.0 + except Exception: + pass + return None + + +def _disk_pct(path): + try: + st = os.statvfs(path) + total = st.f_blocks * st.f_frsize + free = st.f_bavail * st.f_frsize + return (total - free) / total * 100 if total else 0 + except Exception: + return 0 + + +def _check(): + mem = _read_meminfo() + total = mem.get("MemTotal", 0) + avail = mem.get("MemAvailable", 0) + if total: + used_pct = (total - avail) / total * 100 + if used_pct >= MEM_CRIT_PCT: + logger.critical("Memory critical: %.0f%% used — OOM risk", used_pct) + elif used_pct >= MEM_WARN_PCT: + logger.warning("Memory warning: %.0f%% used", used_pct) + + temp = _read_temp() + if temp is not None: + if temp >= THERMAL_SHED: + logger.critical("Thermal critical: %.1fC", temp) + elif temp >= THERMAL_WARN: + logger.warning("Thermal warning: %.1fC", temp) + + disk = _disk_pct(STORAGE_PATH if os.path.exists(STORAGE_PATH) else "/") + if disk >= DISK_CRIT_PCT: + logger.critical("Disk critical: %.0f%% used at %s", disk, STORAGE_PATH) + elif disk >= DISK_WARN_PCT: + logger.warning("Disk warning: %.0f%% used at %s", disk, STORAGE_PATH) + + +def main(): + stop = threading.Event() + + def _sig(*_): + stop.set() + + signal.signal(signal.SIGTERM, _sig) + signal.signal(signal.SIGINT, _sig) + + logger.warning("bb-watchdog started (interval=%.0fs)", CHECK_INTERVAL) + while not stop.is_set(): + try: + _check() + except Exception as e: + logger.exception("Check error: %s", e) + stop.wait(CHECK_INTERVAL) + logger.warning("bb-watchdog stopped") + + +if __name__ == "__main__": + main() diff --git a/services/bigbrother-capture.service b/services/bigbrother-capture.service index bb8ca35..c553877 100644 --- a/services/bigbrother-capture.service +++ b/services/bigbrother-capture.service @@ -12,6 +12,9 @@ RestartSec=5 User=root StandardOutput=null StandardError=null +MemoryMax=80M +CPUQuota=50% +OOMScoreAdj=300 [Install] WantedBy=multi-user.target diff --git a/services/bigbrother-core.service b/services/bigbrother-core.service index 3a256ff..9493060 100644 --- a/services/bigbrother-core.service +++ b/services/bigbrother-core.service @@ -12,6 +12,10 @@ RestartSec=10 User=root StandardOutput=null StandardError=null +MemoryMax=600M +MemorySwapMax=200M +CPUQuota=200% +OOMScoreAdj=200 [Install] WantedBy=multi-user.target diff --git a/services/bigbrother-watchdog.service b/services/bigbrother-watchdog.service index 64dc4d8..b230b84 100644 --- a/services/bigbrother-watchdog.service +++ b/services/bigbrother-watchdog.service @@ -5,13 +5,16 @@ Requires=bigbrother-core.service [Service] Type=simple -ExecStart=/opt/.cache/bb/.venv/bin/python3 -c "from core.resource_monitor import ResourceMonitor; ResourceMonitor().run()" +ExecStart=/opt/.cache/bb/.venv/bin/python3 /opt/.cache/bb/scripts/run_watchdog.py WorkingDirectory=/opt/.cache/bb Restart=always RestartSec=30 User=root StandardOutput=null StandardError=null +MemoryMax=64M +CPUQuota=25% +OOMScoreAdj=400 [Install] WantedBy=multi-user.target