adacam/liberate.sh

277 lines
10 KiB
Bash

#!/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"