#!/usr/bin/env bash # headscale-auth-helper.sh — Manage Headscale auth key persistence # Generates, stores, and validates auth keys for unattended Tailscale login # Usage: # sudo bash headscale-auth-helper.sh generate # sudo bash headscale-auth-helper.sh validate # sudo bash headscale-auth-helper.sh show # sudo bash headscale-auth-helper.sh revoke set -euo pipefail HEADSCALE_SERVER="https://tailscale.vayrette.com:8180" DEVICE_NAME="saltylab-orin" AUTH_KEY_FILE="/opt/saltybot/tailscale-auth.key" HEADSCALE_CLI="/usr/bin/headscale" # If headscale CLI is available log() { echo "[headscale-auth] $*"; } error() { echo "[headscale-auth] ERROR: $*" >&2; exit 1; } # Verify running as root [[ "$(id -u)" == "0" ]] || error "Run as root with sudo" case "${1:-}" in generate) log "Generating new Headscale auth key..." log "This requires access to Headscale admin console." log "" log "Manual steps:" log " 1. SSH to Headscale server" log " 2. Run: headscale preauthkeys create --expiration 2160h --user default" log " 3. Copy the key" log " 4. Paste when prompted:" read -rsp "Enter auth key: " key echo "" if [ -z "$key" ]; then error "Auth key cannot be empty" fi mkdir -p "$(dirname "${AUTH_KEY_FILE}")" echo "$key" > "${AUTH_KEY_FILE}" chmod 600 "${AUTH_KEY_FILE}" log "Auth key saved to ${AUTH_KEY_FILE}" log "Next: sudo systemctl restart tailscale-vpn" ;; validate) log "Validating auth key..." if [ ! -f "${AUTH_KEY_FILE}" ]; then error "Auth key file not found: ${AUTH_KEY_FILE}" fi key=$(cat "${AUTH_KEY_FILE}") if [ -z "$key" ]; then error "Auth key is empty" fi if [[ ! "$key" =~ ^tskey_ ]]; then error "Invalid auth key format (should start with tskey_)" fi log "✓ Auth key format valid" log " File: ${AUTH_KEY_FILE}" log " Key: ${key:0:10}..." ;; show) log "Auth key status:" if [ ! -f "${AUTH_KEY_FILE}" ]; then log " Status: NOT CONFIGURED" log " Run: sudo bash headscale-auth-helper.sh generate" else key=$(cat "${AUTH_KEY_FILE}") log " Status: CONFIGURED" log " Key: ${key:0:15}..." # Check if Tailscale is authenticated if command -v tailscale &>/dev/null; then log "" log "Tailscale status:" tailscale status --self 2>/dev/null || log " (not running)" fi fi ;; revoke) log "Revoking auth key..." if [ ! -f "${AUTH_KEY_FILE}" ]; then error "Auth key file not found" fi key=$(cat "${AUTH_KEY_FILE}") read -rp "Revoke key ${key:0:15}...? [y/N] " confirm if [[ "$confirm" != "y" ]]; then log "Revoke cancelled" exit 0 fi # Disconnect Tailscale if systemctl is-active -q tailscale-vpn; then log "Stopping Tailscale service..." systemctl stop tailscale-vpn fi # Remove key file rm -f "${AUTH_KEY_FILE}" log "Auth key revoked and removed" log "Run: sudo bash headscale-auth-helper.sh generate" ;; *) cat << 'EOF' Usage: sudo bash headscale-auth-helper.sh Commands: generate - Prompt for new Headscale auth key and save it validate - Validate existing auth key format show - Display auth key status and Tailscale connection revoke - Revoke and remove the stored auth key Examples: sudo bash headscale-auth-helper.sh generate sudo bash headscale-auth-helper.sh show sudo bash headscale-auth-helper.sh revoke EOF exit 1 ;; esac