#!/bin/bash # File: /var/www/nebuleair_pro_4g/services/tailscale_bootstrap.sh # Purpose: Enroll the device into the AirCarto Headscale tailnet at boot. # Idempotent: exits cleanly if already enrolled or if authkey is missing. # # Invoked by nebuleair-tailscale-bootstrap.service (one-shot at boot). # Also safe to call manually from update_firmware.sh. set -u LOGIN_SERVER="https://headscale.aircarto.fr" AUTHKEY_FILE="/etc/tailscale/authkey" DB_FILE="/var/www/nebuleair_pro_4g/sqlite/sensors.db" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] tailscale-bootstrap: $1"; } if ! command -v tailscale >/dev/null 2>&1; then log "tailscale binary not found, skipping (install via update_firmware.sh)" exit 0 fi if tailscale ip -4 >/dev/null 2>&1; then log "already enrolled (IP $(tailscale ip -4)), nothing to do" exit 0 fi if [[ ! -s "$AUTHKEY_FILE" ]]; then log "no authkey at $AUTHKEY_FILE, skipping (will be fetched by update_firmware.sh)" exit 0 fi if [[ ! -f "$DB_FILE" ]]; then log "database $DB_FILE not found, cannot derive hostname" exit 1 fi deviceID=$(sqlite3 "$DB_FILE" "SELECT value FROM config_table WHERE key='deviceID'" 2>/dev/null) if [[ -z "$deviceID" ]]; then log "deviceID empty in config_table, cannot enroll" exit 1 fi # Tailscale hostnames must be RFC 1123 (lowercase letters, digits, hyphens). deviceID_clean=$(echo "$deviceID" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9-' '-' | sed 's/-\+/-/g; s/^-//; s/-$//') hostname="nebuleair-pro-${deviceID_clean}" authkey=$(cat "$AUTHKEY_FILE") log "enrolling as $hostname..." if tailscale up \ --login-server="$LOGIN_SERVER" \ --authkey="$authkey" \ --hostname="$hostname"; then log "enrollment OK, IP $(tailscale ip -4)" else log "enrollment FAILED (will retry at next boot or update)" exit 1 fi