feat: kit-busrouter v2.0 — complete Synology + GL fleet router platform
Includes: - package/ GL-XE3000 kit-busrouter (opkg) - scripts/provision.sh (GL) and provision-synology.sh (Synology) - syno-balance/ — new WAN balancer replacing aiwanbal (SmartWAN adapter) - kit-connect/ — unified connectivity SPK (Tailscale + reverse SSH) - docs/deployment/synology-rt2600ac-checklist.md — 62-point checklist - docs/provisioning/device-identity.md — fleet identity spec - docs/pilot/checklist.md — field pilot validation - x4078_20260721.dss — reference config backup Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Executable
+298
@@ -0,0 +1,298 @@
|
||||
#!/bin/sh
|
||||
# provision-synology.sh — Bootstrap a Synology RT2600ac Pioneer Bus Router
|
||||
#
|
||||
# Run once from router LAN or console on a fresh/factory Synology SRM unit.
|
||||
#
|
||||
# Usage:
|
||||
# provision-synology.sh <DEVICE_ID> [TAILSCALE_AUTH_KEY]
|
||||
#
|
||||
# DEVICE_ID : bus identifier, e.g. B0042
|
||||
# Becomes the WiFi SSID on both bands (plan spec).
|
||||
# TAILSCALE_AUTH_KEY : Tailscale pre-auth key (tskey-...).
|
||||
# Omit if Tailscale is already configured.
|
||||
#
|
||||
# Prerequisites:
|
||||
# - SRM 1.3.1+ with SSH enabled (Control Panel → Terminal & SNMP)
|
||||
# - SD card inserted (for persistent storage)
|
||||
# - aiwanbal SPK at /tmp/aiwanbal-latest.spk (or set AIWANBAL_SPK env)
|
||||
# - Tailscale SPK installed (or TAILSCALE_AUTH_KEY provided to configure)
|
||||
# - Eyeride Eyenet reachable at 192.168.10.1 (if GPS is used)
|
||||
# - Edit HUB_TAILSCALE_IP constant below before deploying
|
||||
#
|
||||
# Idempotent: safe to re-run after partial failure.
|
||||
|
||||
set -e
|
||||
|
||||
DEVICE_ID="${1:?Usage: provision-synology.sh <DEVICE_ID> [TAILSCALE_AUTH_KEY]}"
|
||||
TAILSCALE_AUTH_KEY="${2:-}"
|
||||
|
||||
AIWANBAL_SPK="${AIWANBAL_SPK:-/tmp/aiwanbal-latest.spk}"
|
||||
|
||||
# ── Fleet-wide constants (edit before deploying) ──────────────────────────────────
|
||||
HUB_TAILSCALE_IP="100.76.128.1" # hub's Tailscale IP
|
||||
HUB_TELEMETRY_PORT="8080" # telemetry API port on hub
|
||||
HUB_TELEMETRY_PATH="/api/telemetry" # telemetry endpoint path
|
||||
WIFI_PASS="Pioneer123"
|
||||
ADMIN_PASS="Pioneer321!"
|
||||
TIMEZONE="UTC"
|
||||
|
||||
# Guard: catch forgotten placeholder before any changes are made
|
||||
case "$HUB_TAILSCALE_IP" in
|
||||
REPLACE_*|CHANGE_ME*|"")
|
||||
echo "[provision] ERROR: edit HUB_TAILSCALE_IP in provision-synology.sh before running" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
TELEMETRY_HUB="http://${HUB_TAILSCALE_IP}:${HUB_TELEMETRY_PORT}${HUB_TELEMETRY_PATH}"
|
||||
|
||||
echo "[provision] === Pioneer Bus Router Provisioning (Synology SRM) ==="
|
||||
echo "[provision] DEVICE_ID : $DEVICE_ID"
|
||||
echo "[provision] HUB_TAILSCALE_IP : $HUB_TAILSCALE_IP"
|
||||
echo "[provision] TELEMETRY_HUB : $TELEMETRY_HUB"
|
||||
echo ""
|
||||
|
||||
# ── 1. Device identity ───────────────────────────────────────────────────────────
|
||||
echo "[provision] 1/11 Device identity"
|
||||
mkdir -p /etc/busrouter
|
||||
printf '%s\n' "$DEVICE_ID" > /etc/busrouter/device-id
|
||||
if [ ! -f /etc/busrouter/version ]; then
|
||||
printf '0.0\n' > /etc/busrouter/version
|
||||
fi
|
||||
echo "[provision] device-id: $(cat /etc/busrouter/device-id)"
|
||||
echo "[provision] version: $(cat /etc/busrouter/version)"
|
||||
|
||||
# ── 2. Hostname ──────────────────────────────────────────────────────────────────
|
||||
# SRM stores hostname in /etc/synoinfo.conf and /etc/hostname
|
||||
echo "[provision] 2/11 Hostname → ${DEVICE_ID}"
|
||||
if command -v synosetkeyvalue >/dev/null 2>&1; then
|
||||
synosetkeyvalue /etc/synoinfo.conf system hostname "$DEVICE_ID" 2>/dev/null || true
|
||||
fi
|
||||
hostname "$DEVICE_ID" 2>/dev/null || true
|
||||
printf '%s\n' "$DEVICE_ID" > /etc/hostname 2>/dev/null || true
|
||||
|
||||
# ── 3. Admin accounts ────────────────────────────────────────────────────────────
|
||||
# Synology SRM: the web admin user is typically "admin" (created at setup).
|
||||
# SSH uses root (set via SRM Control Panel → Terminal & SNMP).
|
||||
# We set the root password for fleet SSH access.
|
||||
echo "[provision] 3/11 Admin account (root password)"
|
||||
printf '%s\n%s\n' "$ADMIN_PASS" "$ADMIN_PASS" | passwd root 2>/dev/null || \
|
||||
echo "root:${ADMIN_PASS}" | chpasswd 2>/dev/null || \
|
||||
echo "[provision] WARNING: failed to set root password"
|
||||
|
||||
# ── 4. WiFi SSID ─────────────────────────────────────────────────────────────────
|
||||
# SRM WiFi is managed through the web UI (Wi-Fi Connect app).
|
||||
# We can configure it via synowifi if available, but the authoritative path
|
||||
# is the SRM Network Center. Log the expected values and attempt CLI config.
|
||||
echo "[provision] 4/11 WiFi SSID=${DEVICE_ID} (2.4G + 5G)"
|
||||
echo "[provision] Password: ${WIFI_PASS}"
|
||||
echo "[provision] NOTE: If WiFi doesn't change, configure manually in SRM:"
|
||||
echo "[provision] Wi-Fi Connect → Radio 1 (2.4G) → SSID: ${DEVICE_ID}"
|
||||
echo "[provision] Wi-Fi Connect → Radio 2 (5G) → SSID: ${DEVICE_ID}"
|
||||
|
||||
# Try synowifi if present (may not exist on all SRM versions)
|
||||
if command -v synowifi >/dev/null 2>&1; then
|
||||
synowifi --set-ssid "$DEVICE_ID" --set-security wpa2-personal --set-passphrase "$WIFI_PASS" 2>/dev/null || \
|
||||
echo "[provision] WARNING: synowifi command failed — configure WiFi via SRM web UI"
|
||||
elif command -v /usr/syno/bin/synowifi >/dev/null 2>&1; then
|
||||
/usr/syno/bin/synowifi --set-ssid "$DEVICE_ID" --set-security wpa2-personal --set-passphrase "$WIFI_PASS" 2>/dev/null || \
|
||||
echo "[provision] WARNING: synowifi command failed — configure WiFi via SRM web UI"
|
||||
fi
|
||||
|
||||
# ── 5. Install aiwanbal ──────────────────────────────────────────────────────────
|
||||
echo "[provision] 5/11 aiwanbal WAN balancer"
|
||||
if [ -f "$AIWANBAL_SPK" ]; then
|
||||
echo "[provision] Installing aiwanbal from $AIWANBAL_SPK"
|
||||
synopkg install "$AIWANBAL_SPK" 2>/dev/null || \
|
||||
echo "[provision] WARNING: synopkg install failed — install manually"
|
||||
else
|
||||
echo "[provision] WARNING: $AIWANBAL_SPK not found"
|
||||
echo "[provision] Build aiwanbal SPK first, then copy to router:"
|
||||
echo "[provision] cd /home/node/workspace/aiwanbal && bash build.sh"
|
||||
echo "[provision] scp -O aiwanbal-*.spk kitadmin@<router>:/tmp/aiwanbal-latest.spk"
|
||||
fi
|
||||
|
||||
# Ensure aiwanbal is running
|
||||
if synopkg status aiwanbal >/dev/null 2>&1; then
|
||||
synopkg start aiwanbal 2>/dev/null || true
|
||||
echo "[provision] aiwanbal started"
|
||||
else
|
||||
echo "[provision] WARNING: aiwanbal package not found — install before continuing"
|
||||
fi
|
||||
|
||||
# ── 6. Configure aiwanbal for busrouter (mobile environment) ─────────────────────
|
||||
echo "[provision] 6/11 aiwanbal busrouter tuning"
|
||||
AIWANBAL_CONF="/etc/aiwanbal/aiwanbal.conf"
|
||||
if [ -f "$AIWANBAL_CONF" ]; then
|
||||
# Set environment to mobile for faster scoring adaptations
|
||||
if grep -q '^ENVIRONMENT=' "$AIWANBAL_CONF" 2>/dev/null; then
|
||||
sed -i 's/^ENVIRONMENT=.*/ENVIRONMENT="mobile"/' "$AIWANBAL_CONF"
|
||||
else
|
||||
echo 'ENVIRONMENT="mobile"' >> "$AIWANBAL_CONF"
|
||||
fi
|
||||
# Set device name for fleet logging
|
||||
if grep -q '^DEVICE_NAME=' "$AIWANBAL_CONF" 2>/dev/null; then
|
||||
sed -i "s/^DEVICE_NAME=.*/DEVICE_NAME=\"$DEVICE_ID\"/" "$AIWANBAL_CONF"
|
||||
else
|
||||
echo "DEVICE_NAME=\"$DEVICE_ID\"" >> "$AIWANBAL_CONF"
|
||||
fi
|
||||
# Metered cellular: disable automatic speed tests to save data
|
||||
if grep -q '^SPEEDTEST_AUTO=' "$AIWANBAL_CONF" 2>/dev/null; then
|
||||
sed -i 's/^SPEEDTEST_AUTO=.*/SPEEDTEST_AUTO="0"/' "$AIWANBAL_CONF"
|
||||
else
|
||||
echo 'SPEEDTEST_AUTO="0"' >> "$AIWANBAL_CONF"
|
||||
fi
|
||||
# Log the config update
|
||||
logger -t provision "aiwanbal tuned for busrouter: mobile mode, auto-speedtest disabled"
|
||||
echo "[provision] ENVIRONMENT=mobile, SPEEDTEST_AUTO=0, DEVICE_NAME=${DEVICE_ID}"
|
||||
else
|
||||
echo "[provision] WARNING: aiwanbal.conf not found — tune manually"
|
||||
fi
|
||||
|
||||
# ── 7. Tailscale ─────────────────────────────────────────────────────────────────
|
||||
echo "[provision] 7/11 Tailscale"
|
||||
if command -v tailscale >/dev/null 2>&1; then
|
||||
if [ -n "$TAILSCALE_AUTH_KEY" ]; then
|
||||
echo "[provision] Authenticating with pre-auth key..."
|
||||
tailscale up --authkey="$TAILSCALE_AUTH_KEY" --hostname="$DEVICE_ID" 2>/dev/null || \
|
||||
echo "[provision] WARNING: tailscale up failed — check auth key"
|
||||
else
|
||||
ts_status=$(tailscale status --json 2>/dev/null || echo '{"BackendState":"NeedsLogin"}')
|
||||
ts_state=$(echo "$ts_status" | grep -o '"BackendState":"[^"]*"' | cut -d'"' -f4)
|
||||
if [ "$ts_state" = "Running" ]; then
|
||||
echo "[provision] Tailscale is running"
|
||||
else
|
||||
echo "[provision] WARNING: Tailscale not authenticated"
|
||||
echo "[provision] Run: tailscale up --hostname=${DEVICE_ID}"
|
||||
echo "[provision] Or re-run with: provision-synology.sh ${DEVICE_ID} <AUTH_KEY>"
|
||||
fi
|
||||
fi
|
||||
# Show Tailscale IP for fleet records
|
||||
ts_ip=$(tailscale ip -4 2>/dev/null || echo "unknown")
|
||||
echo "[provision] Tailscale IP: ${ts_ip}"
|
||||
else
|
||||
echo "[provision] WARNING: tailscale not found — install Tailscale SPK first"
|
||||
echo "[provision] Download from: https://pkgs.tailscale.com/stable/#synology"
|
||||
fi
|
||||
|
||||
# ── 8. Telemetry script + cron ───────────────────────────────────────────────────
|
||||
echo "[provision] 8/11 Telemetry agent"
|
||||
|
||||
# Determine where to install the telemetry script
|
||||
TELEM_SRC="/usr/lib/busrouter/telemetry-synology.sh"
|
||||
TELEM_DEST="${TELEM_DEST:-/usr/lib/busrouter/telemetry-synology.sh}"
|
||||
|
||||
# Check if this script is bundled with the repo (running from checkout) or
|
||||
# needs to be copied from a pre-staged location.
|
||||
if [ -f "$TELEM_SRC" ]; then
|
||||
echo "[provision] Using telemetry script from ${TELEM_SRC}"
|
||||
elif [ -f "/tmp/telemetry-synology.sh" ]; then
|
||||
TELEM_SRC="/tmp/telemetry-synology.sh"
|
||||
echo "[provision] Using telemetry script from /tmp/"
|
||||
else
|
||||
# The script should be copied alongside provision-synology.sh before running
|
||||
echo "[provision] WARNING: telemetry-synology.sh not found"
|
||||
echo "[provision] Copy it to /tmp/telemetry-synology.sh and re-run:"
|
||||
echo "[provision] scp package/kit-busrouter/files/usr/lib/busrouter/telemetry-synology.sh \\"
|
||||
echo "[provision] kitadmin@<router>:/tmp/"
|
||||
fi
|
||||
|
||||
if [ -f "$TELEM_SRC" ]; then
|
||||
mkdir -p "$(dirname "$TELEM_DEST")"
|
||||
cp "$TELEM_SRC" "$TELEM_DEST"
|
||||
chmod +x "$TELEM_DEST"
|
||||
|
||||
# Configure the hub endpoint in the script
|
||||
sed -i "s|TELEMETRY_HUB:=http://10.88.0.1:8080/api/telemetry|TELEMETRY_HUB:=${TELEMETRY_HUB}|" "$TELEM_DEST"
|
||||
|
||||
# Add cron job: run telemetry every 60 seconds
|
||||
# SRM uses /etc/crontab with user field (standard vixie-cron format)
|
||||
CRON_JOB="* * * * * root ${TELEM_DEST} 2>/dev/null"
|
||||
if [ -f /etc/crontab ]; then
|
||||
if ! grep -qF "$TELEM_DEST" /etc/crontab 2>/dev/null; then
|
||||
# Sleep offset to spread fleet load: use the last octet of device ID as stagger
|
||||
_stagger=0
|
||||
_dev_suffix=$(echo "$DEVICE_ID" | grep -o '[0-9]*$' || echo "0")
|
||||
_stagger=$(( _dev_suffix % 60 ))
|
||||
printf '%s\n' "* * * * * root sleep ${_stagger}; ${TELEM_DEST} 2>/dev/null" >> /etc/crontab
|
||||
echo "[provision] Cron job added (${_stagger}s offset)"
|
||||
else
|
||||
echo "[provision] Cron job already present"
|
||||
fi
|
||||
else
|
||||
# Fallback: use crontab -e style
|
||||
(crontab -l 2>/dev/null || true; echo "* * * * * ${TELEM_DEST} 2>/dev/null") | crontab - 2>/dev/null || \
|
||||
echo "[provision] WARNING: failed to add cron job — add manually"
|
||||
fi
|
||||
|
||||
echo "[provision] Telemetry installed at ${TELEM_DEST}"
|
||||
fi
|
||||
|
||||
# ── 9. Hub SSH jump key ──────────────────────────────────────────────────────────
|
||||
echo "[provision] 9/11 Hub SSH jump key"
|
||||
mkdir -p /root/.ssh
|
||||
JUMP_KEY='ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMS842ZbXPXHbVd9IjyVGubq7M/yQA/cn/vHgB5R0/ZE busfleet-hub-jump'
|
||||
if ! grep -qF 'busfleet-hub-jump' /root/.ssh/authorized_keys 2>/dev/null; then
|
||||
printf '%s\n' "$JUMP_KEY" >> /root/.ssh/authorized_keys
|
||||
chmod 600 /root/.ssh/authorized_keys
|
||||
echo "[provision] Hub jump key installed"
|
||||
else
|
||||
echo "[provision] Hub jump key already present"
|
||||
fi
|
||||
|
||||
# ── 10. Eyeride GPS config (optional) ────────────────────────────────────────────
|
||||
echo "[provision] 10/11 Eyeride GPS"
|
||||
EYERIDE_IP="${EYERIDE_IP:-192.168.10.1}"
|
||||
EYERIDE_PORT="${EYERIDE_PORT:-8080}"
|
||||
echo "[provision] Eyeride target: ${EYERIDE_IP}:${EYERIDE_PORT}"
|
||||
echo "[provision] Set EYERIDE_PASSWORD env before running telemetry for GPS"
|
||||
echo "[provision] To configure permanently, add to aiwanbal.conf:"
|
||||
echo "[provision] EYERIDE_IP=${EYERIDE_IP}"
|
||||
echo "[provision] EYERIDE_PORT=${EYERIDE_PORT}"
|
||||
echo "[provision] EYERIDE_PASSWORD=<your-eyenet-password>"
|
||||
|
||||
# ── 11. Verify + start services ──────────────────────────────────────────────────
|
||||
echo "[provision] 11/11 Final checks"
|
||||
|
||||
# Ensure aiwanbal is running
|
||||
if synopkg status aiwanbal >/dev/null 2>&1; then
|
||||
synopkg start aiwanbal 2>/dev/null || true
|
||||
echo "[provision] aiwanbal is running"
|
||||
fi
|
||||
|
||||
# Run one telemetry cycle to verify
|
||||
if [ -x "$TELEM_DEST" ]; then
|
||||
if "$TELEM_DEST" 2>/dev/null; then
|
||||
echo "[provision] Telemetry dry-run OK"
|
||||
else
|
||||
echo "[provision] Telemetry dry-run completed (check logs for POST result)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Done ──────────────────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "[provision] ============================================"
|
||||
echo "[provision] DONE: $DEVICE_ID provisioned (Synology SRM)."
|
||||
echo ""
|
||||
echo " Device ID : $DEVICE_ID"
|
||||
echo " Hostname : $DEVICE_ID"
|
||||
echo " WiFi SSID : $DEVICE_ID (2.4G + 5G) pw: Pioneer123"
|
||||
echo " Admin SSH : ssh root@<router-lan-ip> pw: Pioneer321!"
|
||||
if command -v tailscale >/dev/null 2>&1; then
|
||||
echo " Tailscale IP : $(tailscale ip -4 2>/dev/null || echo 'check `tailscale status`')"
|
||||
fi
|
||||
echo " Telemetry : ${TELEMETRY_HUB} (every 60s via cron)"
|
||||
echo ""
|
||||
echo " Verify:"
|
||||
echo " cat /etc/busrouter/device-id"
|
||||
echo " synopkg status aiwanbal"
|
||||
echo " tailscale status"
|
||||
echo " grep telemetry /etc/crontab"
|
||||
echo " tail -f /var/log/messages | grep busrouter"
|
||||
echo "[provision] ============================================"
|
||||
echo ""
|
||||
|
||||
# ── Reboot ───────────────────────────────────────────────────────────────────────
|
||||
echo "[provision] Rebooting in 5 s ..."
|
||||
sleep 5
|
||||
reboot
|
||||
Reference in New Issue
Block a user