saltylab-firmware/jetson/docs/headscale-vpn-setup.md
sl-jetson 062c05cac0 feat: Add Issue #502 - Headscale VPN auto-connect on Orin
Configure Jetson Orin with Tailscale client connecting to Headscale
coordination server at tailscale.vayrette.com:8180. Device registers
as 'saltylab-orin' with persistent auth key for unattended login.

Features:
- systemd auto-start and restart on WiFi drops
- Persistent auth key storage at /opt/saltybot/tailscale-auth.key
- SSH + HTTP access over Tailscale tailnet (encrypted WireGuard)
- IP forwarding enabled for relay/exit node capability
- WiFi resilience with aggressive restart policy
- MQTT reporting of VPN status, IP, and connection type

Components added:
- jetson/scripts/setup-tailscale.sh: Tailscale package installation
- jetson/scripts/headscale-auth-helper.sh: Auth key management utility
- jetson/systemd/tailscale-vpn.service: systemd service unit
- jetson/docs/headscale-vpn-setup.md: Comprehensive setup documentation
- saltybot_cellular/vpn_status_node.py: ROS2 node for MQTT reporting

Updated:
- jetson/systemd/install_systemd.sh: Include tailscale-vpn.service
- jetson/scripts/setup-jetson.sh: Add Tailscale setup steps

Access patterns:
- SSH: ssh user@saltylab-orin.tail12345.ts.net
- HTTP: http://saltylab-orin.tail12345.ts.net:port
- Direct IP: 100.x.x.x (Tailscale allocated address)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-06 10:25:04 -05:00

3.1 KiB

Headscale VPN Auto-Connect Setup — Jetson Orin

This document describes the auto-connect VPN setup for the Jetson Orin using Tailscale client connecting to the Headscale server at tailscale.vayrette.com:8180.

Overview

Device Name: saltylab-orin Headscale Server: https://tailscale.vayrette.com:8180 Primary Features:

  • Auto-connect on system boot
  • Persistent auth key for unattended login
  • SSH + HTTP over Tailscale (tailnet)
  • WiFi resilience and fallback
  • systemd auto-restart on failure

Architecture

Components

  1. Tailscale Client (/usr/sbin/tailscaled)

    • VPN daemon running on Jetson
    • Manages WireGuard tunnels
    • Connects to Headscale coordination server
  2. systemd Service (tailscale-vpn.service)

    • Auto-starts on boot
    • Restarts on failure
    • Manages lifecycle of tailscaled daemon
    • Logs to journald
  3. Auth Key Manager (headscale-auth-helper.sh)

    • Generates and validates auth keys
    • Stores keys securely at /opt/saltybot/tailscale-auth.key
    • Manages revocation
  4. Setup Script (setup-tailscale.sh)

    • One-time installation of Tailscale package
    • Configures IP forwarding
    • Sets up persistent state directories

Installation

1. Run Jetson Setup

sudo bash jetson/scripts/setup-jetson.sh

2. Install Tailscale

sudo bash jetson/scripts/setup-tailscale.sh

3. Generate Auth Key

sudo bash jetson/scripts/headscale-auth-helper.sh generate

4. Install systemd Services

sudo bash jetson/systemd/install_systemd.sh

5. Start the VPN Service

sudo systemctl start tailscale-vpn

Usage

Check VPN Status

sudo tailscale status

Access via SSH

ssh <username>@saltylab-orin.tail12345.ts.net

View Logs

sudo journalctl -fu tailscale-vpn

WiFi Resilience

Automatic restart after WiFi drops with aggressive restart policies:

Restart=always
RestartSec=5s
StartLimitInterval=60s
StartLimitBurst=10

Persistent Storage

Auth Key: /opt/saltybot/tailscale-auth.key State Directory: /var/lib/tailscale/

Troubleshooting

Service Won't Start

sudo systemctl status tailscale-vpn
sudo journalctl -u tailscale-vpn -n 30

Can't Connect to Headscale

ping 8.8.8.8
nslookup tailscale.vayrette.com

Auth Key Expired

sudo bash jetson/scripts/headscale-auth-helper.sh revoke
sudo bash jetson/scripts/headscale-auth-helper.sh generate
sudo systemctl restart tailscale-vpn

Security

  • Auth key stored in plaintext at /opt/saltybot/tailscale-auth.key
  • File permissions: 600 (readable only by root)
  • State directory restricted: 700 (only root)
  • SSH over tailnet with no ACL restrictions by default

MQTT Reporting

VPN status reported to MQTT:

saltylab/jetson/vpn/status -> online|offline|connecting
saltylab/jetson/vpn/ip -> 100.x.x.x
saltylab/jetson/vpn/hostname -> saltylab-orin.tail12345.ts.net

References