#!/usr/bin/env bash # BigBrother setup -- Idempotent bootstrap for network surveillance implant # Targets: Orange Pi Zero 3 (primary), RPi Zero 2W (jumpbox), generic Debian set -euo pipefail # ── Colors ────────────────────────────────────────────────────────────────── RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' BOLD='\033[1m' NC='\033[0m' # ── Globals ───────────────────────────────────────────────────────────────── INSTALL_DIR="/opt/.cache/bb" TOOLS_DIR="/opt/tools" VENV_DIR="${INSTALL_DIR}/.venv" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" LOG_FILE="/tmp/bb_setup_$(date +%Y%m%d_%H%M%S).log" # Flags FLAG_REINSTALL=0 FLAG_JUMPBOX=0 FLAG_NO_TOOLS=0 FLAG_VERIFY_ONLY=0 # ── Output helpers ────────────────────────────────────────────────────────── info() { echo -e "${GREEN}[+]${NC} $*"; } warn() { echo -e "${YELLOW}[!]${NC} $*"; } error() { echo -e "${RED}[-]${NC} $*"; } step() { echo -e "${CYAN}[*]${NC} ${BOLD}$*${NC}"; } debug() { echo -e "${BLUE}[.]${NC} $*"; } die() { error "$*" exit 1 } log_cmd() { # Run command, log output, show status "$@" >> "$LOG_FILE" 2>&1 || { error "Command failed: $* (see $LOG_FILE)"; return 1; } } # ── Argument parsing ──────────────────────────────────────────────────────── usage() { echo "Usage: $0 [OPTIONS]" echo "" echo "Options:" echo " --reinstall Force reinstall all components" echo " --jumpbox Minimal install for RPi Zero 2W jumpbox mode" echo " --no-tools Skip GitHub tool installation" echo " --verify-only Check installation without changes" echo " -h, --help Show this help" exit 0 } while [[ $# -gt 0 ]]; do case "$1" in --reinstall) FLAG_REINSTALL=1; shift ;; --jumpbox) FLAG_JUMPBOX=1; shift ;; --no-tools) FLAG_NO_TOOLS=1; shift ;; --verify-only) FLAG_VERIFY_ONLY=1; shift ;; -h|--help) usage ;; *) die "Unknown option: $1" ;; esac done # ── Root check ────────────────────────────────────────────────────────────── [[ $EUID -eq 0 ]] || die "Must be run as root" # ── Platform detection ────────────────────────────────────────────────────── detect_platform() { if [ -f /proc/device-tree/model ]; then local model model=$(tr -d '\0' < /proc/device-tree/model) if echo "$model" | grep -qi "orange pi zero3"; then echo "opi_zero3" elif echo "$model" | grep -qi "orange pi zero 3"; then echo "opi_zero3" elif echo "$model" | grep -qi "raspberry pi zero 2"; then echo "pi_zero" elif echo "$model" | grep -qi "raspberry pi 4"; then echo "pi4" else echo "generic" fi else echo "generic" fi } detect_arch() { local arch arch=$(dpkg --print-architecture 2>/dev/null || uname -m) case "$arch" in arm64|aarch64) echo "arm64" ;; amd64|x86_64) echo "amd64" ;; armhf|armv7l) echo "armhf" ;; *) echo "$arch" ;; esac } PLATFORM=$(detect_platform) ARCH=$(detect_arch) step "Platform: ${PLATFORM} (${ARCH})" echo " Install dir: ${INSTALL_DIR}" echo " Tools dir: ${TOOLS_DIR}" echo " Log file: ${LOG_FILE}" echo "" # Force jumpbox mode on Pi Zero if [[ "$PLATFORM" == "pi_zero" ]]; then FLAG_JUMPBOX=1 info "Pi Zero 2W detected -- forcing jumpbox mode" fi # ════════════════════════════════════════════════════════════════════════════ # VERIFY-ONLY MODE # ════════════════════════════════════════════════════════════════════════════ if [[ $FLAG_VERIFY_ONLY -eq 1 ]]; then step "Running verification checks..." ERRORS=0 verify_bin() { if command -v "$1" &>/dev/null; then info " $1 ... found ($(command -v "$1"))" else error " $1 ... MISSING" ((ERRORS++)) fi } verify_file() { if [[ -f "$1" ]]; then info " $1 ... exists" else error " $1 ... MISSING" ((ERRORS++)) fi } verify_dir() { if [[ -d "$1" ]]; then info " $1 ... exists" else error " $1 ... MISSING" ((ERRORS++)) fi } verify_db() { if [[ -f "$1" ]] && sqlite3 "$1" "SELECT 1;" &>/dev/null; then local tables tables=$(sqlite3 "$1" ".tables" 2>/dev/null) info " $1 ... valid (tables: $tables)" else error " $1 ... MISSING or invalid" ((ERRORS++)) fi } echo "" step "System binaries" for bin in python3 tcpdump tshark nmap bettercap macchanger aircrack-ng \ zstd cryptsetup tmux jq sqlite3 curl wget git; do verify_bin "$bin" done echo "" step "Directory structure" for dir in "${INSTALL_DIR}" "${INSTALL_DIR}/core" "${INSTALL_DIR}/modules" \ "${INSTALL_DIR}/utils" "${INSTALL_DIR}/config" "${INSTALL_DIR}/data" \ "${INSTALL_DIR}/storage" "${INSTALL_DIR}/services" "${INSTALL_DIR}/scripts"; do verify_dir "$dir" done echo "" step "Python venv" verify_dir "${VENV_DIR}" if [[ -f "${VENV_DIR}/bin/python3" ]]; then info " Python venv functional" if "${VENV_DIR}/bin/python3" -c "import scapy, click, rich, yaml, cryptography, requests, dpkt, zstandard, dnslib, jinja2" 2>/dev/null; then info " Core Python imports OK" else error " Some Python imports FAILED" ((ERRORS++)) fi fi echo "" step "Data databases" verify_db "${INSTALL_DIR}/data/innocuous_macs.db" verify_db "${INSTALL_DIR}/data/oui.db" verify_db "${INSTALL_DIR}/data/os_sigs.db" verify_db "${INSTALL_DIR}/data/dhcp_fingerprints.db" verify_db "${INSTALL_DIR}/data/ja3_fingerprints.db" if [[ $FLAG_NO_TOOLS -eq 0 ]]; then echo "" step "External tools" verify_dir "${TOOLS_DIR}/Responder" verify_dir "${TOOLS_DIR}/enum4linux-ng" verify_dir "${TOOLS_DIR}/smbmap" verify_bin chisel verify_bin ligolo-agent verify_bin kerbrute fi echo "" if [[ $ERRORS -eq 0 ]]; then info "All verification checks passed" else error "${ERRORS} verification check(s) failed" fi exit $ERRORS fi # ════════════════════════════════════════════════════════════════════════════ # 1. SYSTEM PACKAGES # ════════════════════════════════════════════════════════════════════════════ step "Installing system packages..." export DEBIAN_FRONTEND=noninteractive # Core package list PACKAGES=( build-essential python3-dev python3-venv python3-pip python3-setuptools libpcap-dev libssl-dev libffi-dev libsqlite3-dev tcpdump tshark nmap masscan net-tools iproute2 bridge-utils wireless-tools wpasupplicant macchanger iptables nftables ebtables arptables cryptsetup zstd tmux screen curl wget git jq sqlite3 file dnsutils hostapd dnsmasq wireguard-tools autossh socat stunnel4 iodine ppp usb-modeswitch ) # Only install bluetooth packages on platforms that support it if [[ "$PLATFORM" != "pi_zero" ]]; then PACKAGES+=(bluez libbluetooth-dev) fi # Check if proxychains is available (package name varies) if apt-cache show proxychains4 &>/dev/null 2>&1; then PACKAGES+=(proxychains4) elif apt-cache show proxychains-ng &>/dev/null 2>&1; then PACKAGES+=(proxychains-ng) fi # Check if sshuttle is available as system package if apt-cache show sshuttle &>/dev/null 2>&1; then PACKAGES+=(sshuttle) fi # aircrack-ng not available on all Ubuntu arm64 mirrors — add only if a real candidate exists if apt-cache policy aircrack-ng 2>/dev/null | grep -q "Candidate: [^(none)]"; then PACKAGES+=(aircrack-ng) else warn "aircrack-ng has no install candidate -- skipping (install manually if needed)" fi info "Updating package cache..." log_cmd apt-get update -qq info "Installing ${#PACKAGES[@]} packages..." log_cmd apt-get install -y -qq "${PACKAGES[@]}" info "System packages installed" # ════════════════════════════════════════════════════════════════════════════ # 2. PI-SPECIFIC OPTIMIZATIONS # ════════════════════════════════════════════════════════════════════════════ if [[ "$PLATFORM" == "opi_zero3" || "$PLATFORM" == "pi_zero" || "$PLATFORM" == "pi4" ]]; then step "Applying Pi/SBC optimizations..." # RPi-specific: reduce GPU memory if [[ "$PLATFORM" == "pi_zero" || "$PLATFORM" == "pi4" ]]; then if [[ -f /boot/config.txt ]]; then if ! grep -q "^gpu_mem=16" /boot/config.txt; then echo "gpu_mem=16" >> /boot/config.txt info "Set gpu_mem=16 in /boot/config.txt" fi elif [[ -f /boot/firmware/config.txt ]]; then if ! grep -q "^gpu_mem=16" /boot/firmware/config.txt; then echo "gpu_mem=16" >> /boot/firmware/config.txt info "Set gpu_mem=16 in /boot/firmware/config.txt" fi fi fi # Disable unnecessary services for svc in bluetooth avahi-daemon triggerhappy ModemManager; do if systemctl is-enabled "$svc" &>/dev/null 2>&1; then systemctl disable --now "$svc" 2>/dev/null || true info "Disabled $svc" fi done # Disable swap swapoff -a 2>/dev/null || true systemctl disable dphys-swapfile 2>/dev/null || true systemctl disable zram-config 2>/dev/null || true # Remove swap entries from fstab if grep -q "swap" /etc/fstab 2>/dev/null; then sed -i '/swap/s/^/#/' /etc/fstab info "Commented out swap entries in /etc/fstab" fi info "Swap disabled" # Kernel parameters SYSCTL_CONF="/etc/sysctl.d/99-bigbrother.conf" cat > "$SYSCTL_CONF" << 'SYSCTL' # BigBrother network parameters net.ipv4.ip_forward=1 net.ipv6.conf.all.forwarding=1 net.ipv4.conf.all.send_redirects=0 net.ipv4.conf.default.send_redirects=0 net.ipv6.conf.all.disable_ipv6=1 net.ipv6.conf.default.disable_ipv6=1 # Performance tuning for packet capture net.core.rmem_max=16777216 net.core.rmem_default=1048576 net.core.netdev_max_backlog=5000 SYSCTL sysctl --system >> "$LOG_FILE" 2>&1 || true info "Kernel parameters applied" fi # ════════════════════════════════════════════════════════════════════════════ # 3. DIRECTORY STRUCTURE # ════════════════════════════════════════════════════════════════════════════ step "Creating directory structure..." mkdir -p "${INSTALL_DIR}"/{core,modules/{passive,active,stealth,intel,connectivity},utils} mkdir -p "${INSTALL_DIR}"/config/{caplets,mitmproxy_addons,templates} mkdir -p "${INSTALL_DIR}"/data/{bpf_filters,wordlists,ids_rules} mkdir -p "${INSTALL_DIR}"/storage/{pcaps,logs,baseline,intel,credentials,config,creds,dns_logs,sni_logs,audit,tool_logs} mkdir -p "${INSTALL_DIR}"/{services,scripts/operator,templates/{captive_portals,dns_zones,hostapd,responder,lkm},tests} chmod 700 "${INSTALL_DIR}/storage" chmod 700 "${INSTALL_DIR}/storage"/* # Tools directory mkdir -p "${TOOLS_DIR}" info "Directory structure created" # ════════════════════════════════════════════════════════════════════════════ # 4. PYTHON VIRTUAL ENVIRONMENT # ════════════════════════════════════════════════════════════════════════════ step "Setting up Python virtual environment..." if [[ ! -d "${VENV_DIR}" ]] || [[ $FLAG_REINSTALL -eq 1 ]]; then rm -rf "${VENV_DIR}" python3 -m venv "${VENV_DIR}" info "Created venv at ${VENV_DIR}" else info "Venv already exists at ${VENV_DIR}" fi # Upgrade pip "${VENV_DIR}/bin/pip" install --upgrade pip setuptools wheel >> "$LOG_FILE" 2>&1 # Install requirements from source tree if available, else from install dir REQ_FILE="" if [[ -f "${SCRIPT_DIR}/requirements.txt" ]]; then REQ_FILE="${SCRIPT_DIR}/requirements.txt" elif [[ -f "${INSTALL_DIR}/requirements.txt" ]]; then REQ_FILE="${INSTALL_DIR}/requirements.txt" fi if [[ -n "$REQ_FILE" ]]; then info "Installing Python requirements from ${REQ_FILE}..." "${VENV_DIR}/bin/pip" install -r "$REQ_FILE" >> "$LOG_FILE" 2>&1 fi # Additional pip packages (tools installed into venv) VENV_PIP_PACKAGES=( impacket certipy-ad bloodhound pyjwt graphviz numpy ) # Jumpbox mode: skip heavy packages if [[ $FLAG_JUMPBOX -eq 0 ]]; then VENV_PIP_PACKAGES+=( mitmproxy mitm6 coercer pypykatz ldapdomaindump ) fi info "Installing additional Python packages..." for pkg in "${VENV_PIP_PACKAGES[@]}"; do if "${VENV_DIR}/bin/pip" show "$pkg" &>/dev/null && [[ $FLAG_REINSTALL -eq 0 ]]; then debug " $pkg already installed" else info " Installing $pkg..." "${VENV_DIR}/bin/pip" install "$pkg" >> "$LOG_FILE" 2>&1 || warn " Failed to install $pkg (non-fatal)" fi done info "Python environment ready" # ════════════════════════════════════════════════════════════════════════════ # 5. INSTALL BETTERCAP # ════════════════════════════════════════════════════════════════════════════ step "Installing bettercap..." install_bettercap() { if command -v bettercap &>/dev/null && [[ $FLAG_REINSTALL -eq 0 ]]; then info "bettercap already installed: $(bettercap -version 2>/dev/null || echo 'unknown version')" return 0 fi info "Downloading bettercap binary..." local bc_arch case "$ARCH" in arm64) bc_arch="arm64" ;; amd64) bc_arch="amd64" ;; armhf) bc_arch="armhf" ;; *) warn "Unknown arch $ARCH for bettercap, trying amd64"; bc_arch="amd64" ;; esac # Get latest release URL from GitHub local release_url release_url=$(curl -fsSL "https://api.github.com/repos/bettercap/bettercap/releases/latest" \ | jq -r ".assets[] | select(.name | test(\"linux_${bc_arch}\")) | .browser_download_url" \ | head -1) if [[ -z "$release_url" || "$release_url" == "null" ]]; then # Fallback: try to install from apt if apt-cache show bettercap &>/dev/null 2>&1; then log_cmd apt-get install -y -qq bettercap info "bettercap installed from apt" return 0 fi warn "Could not find bettercap release for ${bc_arch} -- manual install required" return 1 fi local tmp_dir tmp_dir=$(mktemp -d) trap "rm -rf '$tmp_dir'" RETURN curl -fsSL "$release_url" -o "${tmp_dir}/bettercap.zip" # Extract if command -v unzip &>/dev/null; then unzip -o "${tmp_dir}/bettercap.zip" -d "${tmp_dir}/bc" >> "$LOG_FILE" 2>&1 else apt-get install -y -qq unzip >> "$LOG_FILE" 2>&1 unzip -o "${tmp_dir}/bettercap.zip" -d "${tmp_dir}/bc" >> "$LOG_FILE" 2>&1 fi # Find and install the binary local bc_bin bc_bin=$(find "${tmp_dir}/bc" -name "bettercap" -type f | head -1) if [[ -z "$bc_bin" ]]; then warn "Could not find bettercap binary in archive" return 1 fi cp "$bc_bin" /usr/local/bin/bettercap chmod 755 /usr/local/bin/bettercap setcap cap_net_raw,cap_net_admin=eip /usr/local/bin/bettercap 2>/dev/null || true info "bettercap installed to /usr/local/bin/bettercap" trap - RETURN rm -rf "$tmp_dir" } if [[ $FLAG_JUMPBOX -eq 0 ]]; then install_bettercap || warn "bettercap installation failed (non-fatal)" else info "Skipping bettercap (jumpbox mode)" fi # ════════════════════════════════════════════════════════════════════════════ # 6. INSTALL TOOLS FROM GITHUB # ════════════════════════════════════════════════════════════════════════════ if [[ $FLAG_NO_TOOLS -eq 0 ]]; then step "Installing external tools..." clone_or_pull() { local repo_url="$1" local dest="$2" local name name=$(basename "$dest") if [[ -d "$dest/.git" ]] && [[ $FLAG_REINSTALL -eq 0 ]]; then debug " $name already cloned, pulling updates..." git -C "$dest" pull --ff-only >> "$LOG_FILE" 2>&1 || true else rm -rf "$dest" info " Cloning $name..." git clone --depth 1 "$repo_url" "$dest" >> "$LOG_FILE" 2>&1 || { warn " Failed to clone $name" return 1 } fi } download_release_binary() { local repo="$1" # e.g. "jpillora/chisel" local bin_name="$2" # e.g. "chisel" local dest="$3" # e.g. "/usr/local/bin/chisel" local arch_pattern="$4" # e.g. "linux_arm64" if [[ -f "$dest" ]] && [[ $FLAG_REINSTALL -eq 0 ]]; then debug " $bin_name already installed at $dest" return 0 fi info " Downloading $bin_name..." local release_url release_url=$(curl -fsSL "https://api.github.com/repos/${repo}/releases/latest" \ | jq -r ".assets[] | select(.name | test(\"${arch_pattern}\")) | .browser_download_url" \ | head -1) if [[ -z "$release_url" || "$release_url" == "null" ]]; then warn " Could not find release for $bin_name (${arch_pattern})" return 1 fi local tmp_file tmp_file=$(mktemp) curl -fsSL "$release_url" -o "$tmp_file" # Detect archive type and extract local file_type file_type=$(file -b "$tmp_file") if echo "$file_type" | grep -qi "gzip"; then if echo "$release_url" | grep -qi "\.tar\.gz"; then local tmp_dir tmp_dir=$(mktemp -d) tar xzf "$tmp_file" -C "$tmp_dir" 2>/dev/null local found_bin found_bin=$(find "$tmp_dir" -name "$bin_name" -type f | head -1) if [[ -n "$found_bin" ]]; then cp "$found_bin" "$dest" else # Try the first executable file found_bin=$(find "$tmp_dir" -type f -executable | head -1) if [[ -n "$found_bin" ]]; then cp "$found_bin" "$dest" else warn " Could not find $bin_name in archive" rm -rf "$tmp_dir" "$tmp_file" return 1 fi fi rm -rf "$tmp_dir" else gunzip -c "$tmp_file" > "$dest" 2>/dev/null || cp "$tmp_file" "$dest" fi elif echo "$file_type" | grep -qi "zip"; then local tmp_dir tmp_dir=$(mktemp -d) unzip -o "$tmp_file" -d "$tmp_dir" >> "$LOG_FILE" 2>&1 local found_bin found_bin=$(find "$tmp_dir" -name "$bin_name" -type f | head -1) if [[ -n "$found_bin" ]]; then cp "$found_bin" "$dest" fi rm -rf "$tmp_dir" else # Assume raw binary cp "$tmp_file" "$dest" fi chmod 755 "$dest" rm -f "$tmp_file" info " $bin_name installed to $dest" } # Determine arch pattern for downloads case "$ARCH" in arm64) DL_ARCH="linux.*arm64\|linux.*aarch64" ;; amd64) DL_ARCH="linux.*amd64\|linux.*x86_64" ;; armhf) DL_ARCH="linux.*arm\|linux.*armv" ;; *) DL_ARCH="linux.*amd64" ;; esac # ── Git clone tools ───────────────────────────────────────────────── clone_or_pull "https://github.com/lgandx/Responder" "${TOOLS_DIR}/Responder" clone_or_pull "https://github.com/cddmp/enum4linux-ng" "${TOOLS_DIR}/enum4linux-ng" clone_or_pull "https://github.com/ShawnDEvans/smbmap" "${TOOLS_DIR}/smbmap" # ── Binary release tools ──────────────────────────────────────────── # Chisel case "$ARCH" in arm64) download_release_binary "jpillora/chisel" "chisel" "/usr/local/bin/chisel" "linux_arm64" ;; amd64) download_release_binary "jpillora/chisel" "chisel" "/usr/local/bin/chisel" "linux_amd64" ;; *) warn " Chisel: unsupported arch $ARCH" ;; esac # Ligolo-ng (agent binary) case "$ARCH" in arm64) download_release_binary "nicocha30/ligolo-ng" "ligolo-agent" "/usr/local/bin/ligolo-agent" "agent.*linux_arm64" ;; amd64) download_release_binary "nicocha30/ligolo-ng" "ligolo-agent" "/usr/local/bin/ligolo-agent" "agent.*linux_amd64" ;; *) warn " Ligolo-ng: unsupported arch $ARCH" ;; esac # Kerbrute (no arm64 binary — build from source) case "$ARCH" in arm64) if command -v go &>/dev/null; then log_cmd go install github.com/ropnop/kerbrute@latest cp "$(go env GOPATH)/bin/kerbrute" /usr/local/bin/kerbrute 2>/dev/null || true else GOTAR=$(mktemp -d) GOVERSION="1.22.3" curl -fsSL "https://go.dev/dl/go${GOVERSION}.linux-arm64.tar.gz" -o "$GOTAR/go.tar.gz" tar -C /usr/local -xzf "$GOTAR/go.tar.gz" export PATH=$PATH:/usr/local/go/bin log_cmd /usr/local/go/bin/go install github.com/ropnop/kerbrute@latest GOPATH=$(/usr/local/go/bin/go env GOPATH) cp "${GOPATH}/bin/kerbrute" /usr/local/bin/kerbrute 2>/dev/null || true rm -rf "$GOTAR" fi ;; amd64) download_release_binary "ropnop/kerbrute" "kerbrute" "/usr/local/bin/kerbrute" "linux_amd64" ;; *) warn " Kerbrute: unsupported arch $ARCH" ;; esac # ── NetExec (pip or git) ──────────────────────────────────────────── if ! "${VENV_DIR}/bin/pip" show netexec &>/dev/null || [[ $FLAG_REINSTALL -eq 1 ]]; then info " Installing NetExec via pip..." "${VENV_DIR}/bin/pip" install netexec >> "$LOG_FILE" 2>&1 || { warn " NetExec pip install failed, trying git clone..." clone_or_pull "https://github.com/Pennyw0rth/NetExec" "${TOOLS_DIR}/NetExec" } else debug " NetExec already installed" fi # ── Evil-WinRM (gem or skip) ──────────────────────────────────────── if command -v gem &>/dev/null; then if ! command -v evil-winrm &>/dev/null || [[ $FLAG_REINSTALL -eq 1 ]]; then info " Installing evil-winrm..." gem install evil-winrm >> "$LOG_FILE" 2>&1 || warn " evil-winrm install failed" fi else debug " Ruby not available, skipping evil-winrm" fi # ── OPi Zero 3 / Debian host only tools ───────────────────────────── if [[ $FLAG_JUMPBOX -eq 0 ]]; then # ROADtools (Azure/Entra recon) if ! "${VENV_DIR}/bin/pip" show roadlib &>/dev/null || [[ $FLAG_REINSTALL -eq 1 ]]; then info " Installing ROADtools..." "${VENV_DIR}/bin/pip" install roadlib roadrecon >> "$LOG_FILE" 2>&1 || warn " ROADtools install failed" fi # PetitPotam clone_or_pull "https://github.com/topotam/PetitPotam" "${TOOLS_DIR}/PetitPotam" fi info "External tools installation complete" else info "Skipping external tool installation (--no-tools)" fi # ════════════════════════════════════════════════════════════════════════════ # 7. CREATE DATA DATABASES # ════════════════════════════════════════════════════════════════════════════ step "Building data databases..." BUILD_DATA="${SCRIPT_DIR}/scripts/build_data.py" if [[ ! -f "$BUILD_DATA" ]]; then BUILD_DATA="${INSTALL_DIR}/scripts/build_data.py" fi if [[ -f "$BUILD_DATA" ]]; then "${VENV_DIR}/bin/python3" "$BUILD_DATA" --output-dir "${INSTALL_DIR}/data" >> "$LOG_FILE" 2>&1 info "Data databases created" else warn "build_data.py not found -- databases will need to be created manually" fi # ════════════════════════════════════════════════════════════════════════════ # 8. INSTALL SYSTEMD SERVICES # ════════════════════════════════════════════════════════════════════════════ step "Installing systemd services..." install_service() { local svc_file="$1" local svc_name svc_name=$(basename "$svc_file") if [[ -f "$svc_file" ]]; then cp "$svc_file" "/etc/systemd/system/${svc_name}" info " Installed $svc_name (not enabled)" fi } # Look for service files in source tree first, then install dir SVC_DIR="${SCRIPT_DIR}/services" if [[ ! -d "$SVC_DIR" ]]; then SVC_DIR="${INSTALL_DIR}/services" fi if [[ -d "$SVC_DIR" ]]; then for svc in "${SVC_DIR}"/*.service "${SVC_DIR}"/*.timer; do [[ -f "$svc" ]] && install_service "$svc" done systemctl daemon-reload info "Systemd services installed (none enabled by default)" else debug "No service files found in $SVC_DIR" fi # ════════════════════════════════════════════════════════════════════════════ # 9. COPY PROJECT FILES # ════════════════════════════════════════════════════════════════════════════ step "Deploying project files..." if [[ "$SCRIPT_DIR" != "$INSTALL_DIR" ]]; then # Copy from source tree to install directory # rsync preferred for idempotency; fall back to cp if command -v rsync &>/dev/null; then rsync -a --exclude='.git' --exclude='.venv' --exclude='storage/' \ --exclude='__pycache__' --exclude='*.pyc' \ "${SCRIPT_DIR}/" "${INSTALL_DIR}/" info "Project files synced to ${INSTALL_DIR}" else cp -a "${SCRIPT_DIR}"/*.py "${INSTALL_DIR}/" 2>/dev/null || true for subdir in core modules utils config data services scripts templates tests; do if [[ -d "${SCRIPT_DIR}/${subdir}" ]]; then cp -a "${SCRIPT_DIR}/${subdir}" "${INSTALL_DIR}/" fi done info "Project files copied to ${INSTALL_DIR}" fi else info "Already running from install directory" fi # ════════════════════════════════════════════════════════════════════════════ # 10. SET PERMISSIONS # ════════════════════════════════════════════════════════════════════════════ step "Setting permissions..." # Storage: restrictive chmod 700 "${INSTALL_DIR}/storage" find "${INSTALL_DIR}/storage" -mindepth 1 -type d -exec chmod 700 {} \; 2>/dev/null || true # Config files: restrictive find "${INSTALL_DIR}/config" -type f -exec chmod 600 {} \; 2>/dev/null || true # Scripts: executable find "${INSTALL_DIR}/scripts" -name "*.sh" -exec chmod 755 {} \; 2>/dev/null || true find "${INSTALL_DIR}/scripts" -name "*.py" -exec chmod 755 {} \; 2>/dev/null || true # Python source: read-only (owner rw) find "${INSTALL_DIR}" -name "*.py" -not -path "*/.venv/*" -exec chmod 644 {} \; 2>/dev/null || true # Main CLI executable [[ -f "${INSTALL_DIR}/bigbrother.py" ]] && chmod 755 "${INSTALL_DIR}/bigbrother.py" # Installed binaries for bin in /usr/local/bin/bettercap /usr/local/bin/chisel /usr/local/bin/ligolo-agent /usr/local/bin/kerbrute; do [[ -f "$bin" ]] && chmod 755 "$bin" done info "Permissions set" # ════════════════════════════════════════════════════════════════════════════ # 11. FINAL VERIFICATION # ════════════════════════════════════════════════════════════════════════════ step "Running final verification..." VERIFY_PASS=0 VERIFY_FAIL=0 check() { if "$@" &>/dev/null 2>&1; then ((VERIFY_PASS++)) || true else warn " FAIL: $*" ((VERIFY_FAIL++)) || true fi } # Core binaries check command -v python3 check command -v tcpdump check command -v nmap check command -v sqlite3 check command -v macchanger check command -v zstd check command -v tmux # Python venv check test -f "${VENV_DIR}/bin/python3" check "${VENV_DIR}/bin/python3" -c "import scapy" check "${VENV_DIR}/bin/python3" -c "import click" check "${VENV_DIR}/bin/python3" -c "import rich" check "${VENV_DIR}/bin/python3" -c "import cryptography" check "${VENV_DIR}/bin/python3" -c "import yaml" # Directories check test -d "${INSTALL_DIR}/core" check test -d "${INSTALL_DIR}/modules" check test -d "${INSTALL_DIR}/storage" check test -d "${INSTALL_DIR}/data" # Databases for db in innocuous_macs.db oui.db os_sigs.db dhcp_fingerprints.db ja3_fingerprints.db; do check test -f "${INSTALL_DIR}/data/${db}" done echo "" info "Verification: ${VERIFY_PASS} passed, ${VERIFY_FAIL} failed" if [[ $VERIFY_FAIL -gt 0 ]]; then warn "Some checks failed -- review $LOG_FILE for details" else info "Setup complete" fi echo "" step "BigBrother deployment summary" echo " Platform: ${PLATFORM} (${ARCH})" echo " Install dir: ${INSTALL_DIR}" echo " Venv: ${VENV_DIR}" echo " Mode: $(if [[ $FLAG_JUMPBOX -eq 1 ]]; then echo 'jumpbox (minimal)'; else echo 'full'; fi)" echo " Log: ${LOG_FILE}" echo "" info "Run '${INSTALL_DIR}/bigbrother.py' to start"