#!/bin/bash # liberate.sh — AdaCam Liberation Script v0.4 # Converts a factory Hivemapper Bee (HDC-S) into an AdaCam. # # Usage: ssh root@192.168.0.10 'bash -s' < liberate.sh # # CRITICAL: Only /data is reliably writable on this device. # /etc may be writable. /root, /usr, /var — assume read-only. # # This version: # - Does NOT touch SSH config (password auth stays enabled) # - Does NOT remove usb-updater (our recovery path) # - Does NOT write to /usr/local/bin, /etc/udev, /etc/systemd # - Only writes to /data/adacam/ and appends to /etc/hosts set -euo pipefail # ── Constants ──────────────────────────────────────────────────────────────── ADACAM_DATA="/data/adacam" ADACAM_AP_IP="10.77.0.1" ADACAM_AP_DHCP_START="10.77.0.100" ADACAM_AP_DHCP_END="10.77.0.200" # ── Logging ────────────────────────────────────────────────────────────────── log() { echo "[liberate] $1"; } ok() { echo "[liberate] ✓ $1"; } warn() { echo "[liberate] ⚠ $1"; } die() { echo "[liberate] ✗ FATAL: $1"; exit 1; } log "" log "╔══════════════════════════════════════════╗" log "║ AdaCam Liberation Script v0.4 ║" log "║ Liberating bees from the hive ║" log "╚══════════════════════════════════════════╝" log "" # ── ROOT CHECK ─────────────────────────────────────────────────────────────── [ "$(id -u)" = "0" ] || die "must run as root" # ── ALREADY LIBERATED CHECK ────────────────────────────────────────────────── if [ -f "$ADACAM_DATA/liberated" ]; then die "device already liberated (marker exists at $ADACAM_DATA/liberated)" fi # ── WRITABILITY CHECK ──────────────────────────────────────────────────────── log "=== Checking filesystem writability ===" check_writable() { local path="$1" if [ -d "$path" ] && touch "${path}/.adacam_test" 2>/dev/null; then rm -f "${path}/.adacam_test" ok "Writable: $path" return 0 else warn "Read-only or missing: $path" return 1 fi } DATA_WRITABLE=false ETC_WRITABLE=false VAR_WRITABLE=false check_writable /data && DATA_WRITABLE=true check_writable /etc && ETC_WRITABLE=true check_writable /var && VAR_WRITABLE=true # /data MUST be writable — that's our only safe zone [ "$DATA_WRITABLE" = "true" ] || die "/data is not writable — cannot proceed" # Confirm this looks like a Bee [ -f /opt/odc-api/odc-api-bee.js ] || warn "odc-api not found — may not be a factory device" ok "running on: $(uname -n)" # ── DERIVE DEVICE SERIAL ───────────────────────────────────────────────────── log "" log "=== Detecting device serial ===" SERIAL="" SERIAL=$(cat /proc/device-tree/serial-number 2>/dev/null | tr -d '\0') || true [ -z "$SERIAL" ] && SERIAL=$(ip link show wlp1s0f0 2>/dev/null | grep link/ether | awk '{print $2}' | tr -d ':') || true [ -z "$SERIAL" ] && SERIAL=$(cat /sys/class/net/wlan0/address 2>/dev/null | tr -d ':') || true if [ -z "$SERIAL" ]; then die "Cannot determine device serial — check /proc/device-tree/serial-number" fi ok "device serial: $SERIAL" # ── GENERATE PER-DEVICE CREDENTIALS ────────────────────────────────────────── log "" log "=== Generating per-device credentials ===" WIFI_PASS=$(echo -n "adacam-${SERIAL}-2026" | sha256sum | cut -c1-12) API_TOKEN=$(echo -n "adacam-api-${SERIAL}-token" | sha256sum | cut -c1-32) ADACAM_AP_SSID="adacam-${SERIAL: -6}" ok "AP SSID: $ADACAM_AP_SSID" ok "WiFi password: $WIFI_PASS" ok "API token: $API_TOKEN" # ── CREATE ADACAM DATA DIRECTORY ───────────────────────────────────────────── log "" log "=== Setting up /data/adacam ===" mkdir -p "$ADACAM_DATA" mkdir -p "$ADACAM_DATA/logs" mkdir -p "$ADACAM_DATA/cache" echo "$SERIAL" > "$ADACAM_DATA/device_serial" echo "$WIFI_PASS" > "$ADACAM_DATA/wifi_password" echo "$API_TOKEN" > "$ADACAM_DATA/api_token" chmod 600 "$ADACAM_DATA/device_serial" "$ADACAM_DATA/wifi_password" "$ADACAM_DATA/api_token" cat > "$ADACAM_DATA/config.json" << CONFIG { "device_id": "$SERIAL", "ap_ssid": "$ADACAM_AP_SSID", "ap_ip": "$ADACAM_AP_IP", "liberated_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)", "liberate_version": "0.4" } CONFIG chmod 644 "$ADACAM_DATA/config.json" ok "config written to $ADACAM_DATA/config.json" # ── PHASE 1: KILL HIVEMAPPER SERVICES ──────────────────────────────────────── log "" log "=== Phase 1: Stopping Hivemapper services ===" # NOTE: We do NOT touch usb-updater — it's our recovery path KILL_SERVICES=" odc-api mitmproxy mender-client here-plugin model-zoo collectd hivemapper-data-logger hivemapper-folder-purger beekeeper-plugin video-processor cpu-mem-logger vnstat vnstatd rm_vpu_daemon dnf-automatic dnf-automatic-download dnf-automatic-install dnf-makecache " for svc in $KILL_SERVICES; do if systemctl list-unit-files "${svc}.service" 2>/dev/null | grep -q "$svc"; then systemctl stop "${svc}.service" 2>/dev/null || true systemctl disable "${svc}.service" 2>/dev/null || true systemctl mask "${svc}.service" 2>/dev/null || true ok "killed: $svc" fi done # ── PHASE 2: BLOCK HIVEMAPPER DOMAINS ──────────────────────────────────────── log "" log "=== Phase 2: Blocking Hivemapper endpoints ===" if [ "$ETC_WRITABLE" = "true" ]; then BLOCK_HOSTS=" hivemapper.com api.hivemapper.com beemaps.com api.trybeekeeper.ai docker.mender.io s3.mender.io direct.data.api.platform.here.com api-lookup.data.api.platform.here.com account.api.here.com edge.hereapi.com olp.here.com dashcam-firmware.s3.us-west-2.amazonaws.com cfapi.cloudflare.com " for host in $BLOCK_HOSTS; do if ! grep -q "$host" /etc/hosts 2>/dev/null; then echo "0.0.0.0 $host" >> /etc/hosts && ok "blocked: $host" || warn "failed to block: $host" fi done else warn "/etc not writable — skipping host blocking" fi # ── PHASE 3: AP CONFIGURATION ──────────────────────────────────────────────── log "" log "=== Phase 3: Configuring WiFi AP ===" # Find and update hostapd config HOSTAPD_CONF="" for f in /var/hostapd-2g.conf /var/hostapd.conf /etc/hostapd/hostapd.conf; do [ -f "$f" ] && HOSTAPD_CONF="$f" && break done if [ -n "$HOSTAPD_CONF" ]; then sed -i "s/^ssid=.*/ssid=$ADACAM_AP_SSID/" "$HOSTAPD_CONF" && \ sed -i "s/^wpa_passphrase=.*/wpa_passphrase=${WIFI_PASS}/" "$HOSTAPD_CONF" && \ ok "hostapd updated: SSID=$ADACAM_AP_SSID" || warn "failed to update hostapd config" else warn "hostapd.conf not found — AP SSID not changed" fi # Update dnsmasq config DNSMASQ_WRITTEN=false if [ "$VAR_WRITABLE" = "true" ] && [ -d /etc/dnsmasq.d ]; then cat > /etc/dnsmasq.d/adacam.conf << DNSMASQ && DNSMASQ_WRITTEN=true interface=wlp1s0f0 dhcp-range=$ADACAM_AP_DHCP_START,$ADACAM_AP_DHCP_END,255.255.255.0,12h dhcp-option=3,$ADACAM_AP_IP dhcp-option=6,$ADACAM_AP_IP no-resolv server=1.1.1.1 server=8.8.8.8 DNSMASQ fi if [ "$DNSMASQ_WRITTEN" = "true" ]; then ok "dnsmasq config written" else warn "dnsmasq config not written — DHCP may not work as expected" fi ok "AP config prepared (changes apply after reboot)" # ── PHASE 4: REMOVE REVERSE TUNNELS ────────────────────────────────────────── log "" log "=== Phase 4: Disabling reverse tunnel services ===" for tunnel in bee-tunnel adacam-tunnel; do systemctl stop "$tunnel" 2>/dev/null || true systemctl mask "$tunnel" 2>/dev/null || true done ok "reverse tunnel services disabled" # ── MARK LIBERATED ─────────────────────────────────────────────────────────── log "" log "=== Marking device as liberated ===" cat > "$ADACAM_DATA/liberated" << MARKER liberated_at=$(date -u +%Y-%m-%dT%H:%M:%SZ) serial=$SERIAL version=0.4 MARKER ok "liberation marker written to $ADACAM_DATA/liberated" # ── SUMMARY ────────────────────────────────────────────────────────────────── log "" echo "════════════════════════════════════════════════════════" echo " AdaCam Liberation Complete (v0.4)" echo "════════════════════════════════════════════════════════" echo "" echo " Device Serial: $SERIAL" echo " AP SSID: $ADACAM_AP_SSID" echo " AP Password: $WIFI_PASS" echo " API Token: $API_TOKEN" echo "" echo " Data stored at: $ADACAM_DATA/" echo "" echo " AFTER REBOOT:" echo " 1. Connect to WiFi: $ADACAM_AP_SSID" echo " 2. SSH: ssh root@$ADACAM_AP_IP" echo " (password auth still works — factory default)" echo "" echo " NOTE: usb-updater is still running (recovery path)" echo "" echo "════════════════════════════════════════════════════════" echo "" log "Reboot the device to apply network changes." log "Run: reboot"