62a1010ab4
9 files in utils/ providing the shared infrastructure layer: - crypto: AES-256-GCM file encryption, argon2id/PBKDF2 key derivation, HKDF network unlock, LUKS container management - networking: interface detection, MAC/IP helpers, BPF compilation via libpcap ctypes, gratuitous ARP, VLAN creation - logging: encrypted log writer (BBLogger) with rotation, per-module files, stdout suppression for stealth - stealth: process rename via prctl, cmdline spoofing, sysctl helpers, timestomping, core dump disable - resource: hardware tier detection (OPi Zero 3 / Pi Zero / generic) via /proc/device-tree and cpuinfo, resource monitoring - permissions: root/capability checks via capget ctypes, privilege dropping, directory permission enforcement - config_loader: YAML merge hierarchy (hardware_tiers -> bigbrother -> modules -> stealth -> CLI), tier auto-detection, SIGHUP reload - bettercap_api: REST client with per-session random credentials, session/events/command methods
147 lines
4.5 KiB
Python
147 lines
4.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Encrypted log writer for BigBrother. Named bb_logging to avoid shadowing stdlib logging."""
|
|
|
|
import logging
|
|
import os
|
|
import time
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
|
|
LOG_FORMAT = "[{timestamp}] [{module}] [{level}] {message}"
|
|
MAX_LOG_SIZE = 10 * 1024 * 1024 # 10MB default rotation size
|
|
LOG_DIR = "storage/logs"
|
|
|
|
|
|
class EncryptedLogHandler(logging.Handler):
|
|
def __init__(
|
|
self,
|
|
module_name: str,
|
|
log_dir: str = LOG_DIR,
|
|
max_bytes: int = MAX_LOG_SIZE,
|
|
encrypt: bool = False,
|
|
crypto_engine=None,
|
|
):
|
|
super().__init__()
|
|
self.module_name = module_name
|
|
self.log_dir = Path(log_dir)
|
|
self.log_dir.mkdir(parents=True, exist_ok=True)
|
|
self.max_bytes = max_bytes
|
|
self.encrypt = encrypt
|
|
self.crypto_engine = crypto_engine
|
|
self._log_path = self.log_dir / f"{module_name}.log"
|
|
self._file = None
|
|
self._current_size = 0
|
|
self._open_file()
|
|
|
|
def _open_file(self):
|
|
if self._file:
|
|
self._file.close()
|
|
mode = "ab" if self.encrypt else "a"
|
|
self._file = open(self._log_path, mode)
|
|
self._current_size = self._log_path.stat().st_size if self._log_path.exists() else 0
|
|
|
|
def _rotate(self):
|
|
if self._file:
|
|
self._file.close()
|
|
rotated = self._log_path.with_suffix(f".log.{int(time.time())}")
|
|
if self._log_path.exists():
|
|
self._log_path.rename(rotated)
|
|
self._open_file()
|
|
|
|
def emit(self, record):
|
|
try:
|
|
ts = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(record.created))
|
|
line = LOG_FORMAT.format(
|
|
timestamp=ts,
|
|
module=self.module_name,
|
|
level=record.levelname,
|
|
message=record.getMessage(),
|
|
)
|
|
if self.encrypt and self.crypto_engine:
|
|
data = self.crypto_engine.encrypt(line.encode("utf-8"))
|
|
length_prefix = len(data).to_bytes(4, "big")
|
|
self._file.write(length_prefix)
|
|
self._file.write(data)
|
|
self._current_size += 4 + len(data)
|
|
else:
|
|
line += "\n"
|
|
self._file.write(line)
|
|
self._current_size += len(line.encode("utf-8"))
|
|
|
|
self._file.flush()
|
|
|
|
if self._current_size >= self.max_bytes:
|
|
self._rotate()
|
|
except Exception:
|
|
self.handleError(record)
|
|
|
|
def close(self):
|
|
if self._file:
|
|
self._file.close()
|
|
self._file = None
|
|
super().close()
|
|
|
|
|
|
class BBLogger:
|
|
def __init__(
|
|
self,
|
|
module_name: str,
|
|
log_dir: str = LOG_DIR,
|
|
max_bytes: int = MAX_LOG_SIZE,
|
|
level: int = logging.INFO,
|
|
encrypt: bool = False,
|
|
crypto_engine=None,
|
|
suppress_stdout: bool = True,
|
|
):
|
|
self.module_name = module_name
|
|
self.logger = logging.getLogger(f"bb.{module_name}")
|
|
self.logger.setLevel(level)
|
|
self.logger.propagate = False
|
|
|
|
# Clear existing handlers to prevent duplicates
|
|
self.logger.handlers.clear()
|
|
|
|
self._file_handler = EncryptedLogHandler(
|
|
module_name=module_name,
|
|
log_dir=log_dir,
|
|
max_bytes=max_bytes,
|
|
encrypt=encrypt,
|
|
crypto_engine=crypto_engine,
|
|
)
|
|
self._file_handler.setLevel(level)
|
|
self.logger.addHandler(self._file_handler)
|
|
|
|
if not suppress_stdout:
|
|
console = logging.StreamHandler()
|
|
console.setLevel(level)
|
|
fmt = logging.Formatter(
|
|
"[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s",
|
|
datefmt="%Y-%m-%d %H:%M:%S",
|
|
)
|
|
console.setFormatter(fmt)
|
|
self.logger.addHandler(console)
|
|
|
|
def debug(self, msg: str, *args, **kwargs):
|
|
self.logger.debug(msg, *args, **kwargs)
|
|
|
|
def info(self, msg: str, *args, **kwargs):
|
|
self.logger.info(msg, *args, **kwargs)
|
|
|
|
def warning(self, msg: str, *args, **kwargs):
|
|
self.logger.warning(msg, *args, **kwargs)
|
|
|
|
def error(self, msg: str, *args, **kwargs):
|
|
self.logger.error(msg, *args, **kwargs)
|
|
|
|
def critical(self, msg: str, *args, **kwargs):
|
|
self.logger.critical(msg, *args, **kwargs)
|
|
|
|
def set_level(self, level: int):
|
|
self.logger.setLevel(level)
|
|
self._file_handler.setLevel(level)
|
|
|
|
def close(self):
|
|
self._file_handler.close()
|
|
self.logger.removeHandler(self._file_handler)
|