Skip to content

Latest commit

 

History

History
237 lines (162 loc) · 5.65 KB

File metadata and controls

237 lines (162 loc) · 5.65 KB

VPN Client Enrollment

This guide walks you through enrolling client devices to the Charon VPN mesh using Headscale.

Prerequisites

Before enrolling a client device:

  1. Headscale deployed and accessible via external ingress
  2. Tailscale client installed on your device
  3. Pre-auth key generated (or use web UI for enrollment)

Method 1: Automated Enrollment (Recommended)

Use a pre-auth key for one-command enrollment:

# Generate a reusable pre-auth key (90-day expiration)
kubectl exec -n dev headscale-0 -- headscale preauthkeys create \
  --user default --reusable --expiration 90d

# Connect your device using the key
tailscale up --login-server https://vpn.example.com --authkey <your-key-here>

Advantages:

  • One command to connect
  • Reusable for multiple devices
  • No manual approval needed

Method 2: Web UI Enrollment

For interactive enrollment (useful for user-managed devices):

# Connect without a key to trigger web enrollment
tailscale up --login-server https://vpn.example.com

This will:

  1. Open your browser to the Headscale web UI

  2. Show you the enrollment request

  3. Require manual approval via kubectl:

    # List pending nodes
    kubectl exec -n dev headscale-0 -- headscale nodes list
    
    # Register the node
    kubectl exec -n dev headscale-0 -- headscale nodes register \
      --user default --key <node-key>

Advantages:

  • More control over approvals
  • See device details before accepting
  • Audit trail of enrollments

Verify Connection

After enrollment:

# Check Tailscale status on your device
tailscale status

# Verify node is registered in Headscale
kubectl exec -n dev headscale-0 -- headscale nodes list

# Test connectivity to a service
ping grafana.example.com  # Should resolve to 100.64.0.X
curl https://grafana.example.com  # Should work if on VPN

Common Issues

Cannot reach vpn.example.com

Symptoms: Connection timeout when trying to enroll

Solutions:

  • Check external ingress LoadBalancer has public IP
  • Verify DNS records point to LoadBalancer IP
  • Check firewall allows HTTPS (443) to LoadBalancer
# Check LoadBalancer IP
kubectl get svc -n ingress-nginx-external

# Test DNS resolution
dig vpn.example.com

# Test HTTPS connectivity
curl -I https://vpn.example.com/health

Node shows as offline

Symptoms: Device enrolled but shows offline in headscale nodes list

Solutions:

  • Check firewall allows UDP 41641 (WireGuard)
  • Check firewall allows DERP connections (various ports)
  • Verify Tailscale daemon is running on device
# On the device
sudo tailscale status
sudo tailscale ping <another-node-ip>

# Check firewall (example for UFW)
sudo ufw allow 41641/udp

Can connect to VPN but can't reach services

Symptoms: VPN connected, but can't access services by hostname

Solutions:

  • Verify DNS is working (MagicDNS should be enabled)
  • Check service hostnames resolve to VPN IPs
  • Verify services are actually running
# Check MagicDNS is enabled
tailscale status  # Should show "MagicDNS: enabled"

# Test DNS resolution
dig grafana.example.com  # Should return 100.64.0.X

# Check if service is reachable by IP
curl http://100.64.0.X:443  # Direct IP access (replace X with actual IP)

Multi-Device Management

Create device-specific keys

For temporary access or single devices:

# Create a key for a specific device (non-reusable, 7-day expiration)
kubectl exec -n dev headscale-0 -- headscale preauthkeys create \
  --user default --expiration 7d

List all connected devices

kubectl exec -n dev headscale-0 -- headscale nodes list --user default

Example output:

ID | Name               | NodeKey | IPAddresses         | Online
---|--------------------|---------|---------------------|-------
1  | laptop-work        | [abc]   | 100.64.0.1          | true
2  | phone-personal     | [def]   | 100.64.0.2          | true
3  | grafana.example    | [ghi]   | 100.64.0.3          | true

Remove a device

# Get the node ID from list above
kubectl exec -n dev headscale-0 -- headscale nodes delete --identifier <node-id>

# Or by node key
kubectl exec -n dev headscale-0 -- headscale nodes delete --key <node-key>

Advanced Usage

Offline Pre-auth Keys

Generate keys ahead of time for field deployment:

# Generate 10 keys with 30-day expiration
for i in {1..10}; do
  kubectl exec -n dev headscale-0 -- headscale preauthkeys create \
    --user default --expiration 30d
done > preauth-keys.txt

Automated Enrollment Script

Create a script for easy device enrollment:

#!/bin/bash
# enroll-device.sh

HEADSCALE_URL="https://vpn.example.com"
AUTH_KEY="your-preauth-key-here"

# Install Tailscale if not present
if ! command -v tailscale &> /dev/null; then
    echo "Installing Tailscale..."
    curl -fsSL https://tailscale.com/install.sh | sh
fi

# Connect to VPN
sudo tailscale up --login-server "$HEADSCALE_URL" --authkey "$AUTH_KEY"

# Verify connection
tailscale status

Security Considerations

  1. Protect pre-auth keys - Treat them like passwords
  2. Use short expiration - 7-90 days depending on use case
  3. Monitor enrollments - Regularly review headscale nodes list
  4. Revoke compromised keys - Delete nodes if key is exposed
  5. Use per-device keys - For production, avoid reusable keys

Related Documentation


Navigation: 📚 Documentation Index | 🏠 Home