AizuDemy

Tutorial Setup Headscale Self-Hosted: Zero Trust VPN di VPS Linux

Tutorial Setup Headscale Self-Hosted: Zero Trust VPN di VPS Linux
🎧
Dengarkan Artikel Ini
Suara AI Otomatis • 6 mnt baca baca
⚡ TL;DR

Poin Kunci Artikel Ini:

  • Buka port UDP 3478 (STUN) pada firewall VPC/VPS (UFW atau Cloud Security Group).
  • Pastikan port UDP tidak terblokir oleh aturan firewall upstream provider ISP.
  • Pastikan HTTP/2 dan WebSocket tidak di-strip oleh reverse proxy Caddy/Nginx.
📋 Daftar Isi Materi Tutup ▴

Bahaya Ekosistem Port Terbuka dan Pergeseran Ke Arsitektur Zero Trust Network (ZTA)

Membuka port SSH (22), HTTP/HTTPS (80/443), atau database (3306, 5432, 6379) langsung ke IP publik VPS Linux memicu risiko keamanan tinggi. Bot scanner otomatis seperti Shodan dan Censys memindai rentang IP publik secara konstan untuk melancarkan serangan brute-force, credential stuffing, serta eksploitasi celah keamanan zero-day pada layanan yang terekspos.

Metode pengamanan berbasis perimeter tradisional—seperti pembatasan IP statis di firewall—tidak lagi efektif untuk infrastruktur modern yang dinamis. Pendekatan VPN konvensional (OpenVPN atau IPsec) berbasis arsitektur hub-and-spoke juga menimbulkan masalah baru: trafik terpusat memicu bottleneck bandwidth, latensi tinggi, serta ketiadaan mikrosegmentasi. Ketika satu gateway VPN berhasil ditembus, penyerang mendapatkan akses luas ke seluruh segmen jaringan internal.

Solusi modern mengadopsi model Zero Trust Network Architecture (ZTA) dengan prinsip utama: Never Trust, Always Verify. Setiap entitas dan permintaan akses wajib diotentikasi, diotorisasi, dan dienkripsi secara terus-menerus tanpa memandang lokasi asal koneksi.

Tailscale menerapkan protokol WireGuard untuk membentuk jaringan mesh terenkripsi end-to-end (P2P) antar node. Dalam ekosistem ini, Headscale hadir sebagai implementasi open-source dari kontroler Tailscale. Dengan menjalankan Headscale secara self-hosted, pengguna memegang kendali 100% atas server koordinasi, data otentikasi, kunci enkripsi, dan kebijakan akses tanpa tergantung pada infrastruktur SaaS pihak ketiga.

Deployment Headscale Server Menggunakan Docker dan Docker Compose

Prosedur deployment Headscale di VPS Linux (Ubuntu/Debian) menggunakan Docker container untuk isolasi dependensi dan kemudahan perawatan.

1. Struktur Direktori dan Inisialisasi Konfigurasi

Buat struktur direktori kerja pada host VPS:

mkdir -p /opt/headscale/config /opt/headscale/data

Unduh berkas konfigurasi acuan resmi dari repositori Headscale:

curl -fLo /opt/headscale/config/config.yaml https://raw.githubusercontent.com/juanfont/headscale/main/config-example.yaml

Buka dan edit berkas /opt/headscale/config/config.yaml. Sesuaikan parameter kritis berikut:

server_url: https://headscale.domainanda.com:443
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 0.0.0.0:9090

ip_prefixes:
  - 100.64.0.0/10
  - fd7a:115c:a1e0::/48

derp:
  server:
    enabled: true
    region_id: 999
    region_code: "headscale-embedded"
    region_name: "Headscale Embedded DERP"
    stun_listen_addr: "0.0.0.0:3478"
  urls:
    - https://controlplane.tailscale.com/derp/derp-default.json

disable_check_updates: true
ephemeral_node_inactivity_timeout: 30m
private_key_path: /var/lib/headscale/private.key
db_type: sqlite3
db_path: /var/lib/headscale/db.sqlite

Penjelasan parameter penting:

  • server_url: FQDN publik VPS yang menggunakan HTTPS SSL valid.
  • ip_prefixes: Alokasi rentang IP virtual CGNAT (100.64.0.0/10) untuk node yang terhubung.
  • derp.server: Layanan relay internal fallback ketika koneksi direct P2P terhalang NAT.
  • db_type & db_path: Penggunaan basis data SQLite3 internal untuk menyimpan state node dan otentikasi.

2. Konfigurasi Docker Compose

Buat berkas /opt/headscale/docker-compose.yml dengan isi berikut:

version: "3.7"
services:
  headscale:
    image: headscale/headscale:0.22.3
    container_name: headscale
    restart: unless-stopped
    volumes:
      - ./config:/etc/headscale
      - ./data:/var/lib/headscale
    ports:
      - "8080:8080"
      - "3478:3478/udp"
    command: headscale serve

Jalankan kontainer Headscale dalam mode detached:

cd /opt/headscale && docker compose up -d

Verifikasi status kontainer dan log jalannya aplikasi:

docker compose logs -f headscale

Konfigurasi Reverse Proxy SSL/TLS Menggunakan Caddy

Kontroler Headscale membutuhkan protokol HTTPS aman untuk proses pertukaran kunci TLS dan komunikasi client API. Web server Caddy digunakan karena mendukung otomatisasi sertifikat SSL/TLS Let's Encrypt secara bawaan.

Pasang Caddy pada OS host, lalu tambahkan konfigurasi pada /etc/caddy/Caddyfile:

headscale.domainanda.com {
    reverse_proxy localhost:8080
}

Muat ulang konfigurasi Caddy:

sudo systemctl reload caddy

Caddy otomatis menyelesaikan ACME HTTP-01 challenge dan mengaktifkan enkripsi HTTPS pada port 443.

Manajemen User, Auth Key, dan Registrasi Client

1. Pembuatan User (Namespace)

Setiap perangkat terikat pada user tertentu di kontroler Headscale. Buat user baru:

docker exec -it headscale headscale users create admin

2. Menghubungkan Perangkat Client

Pada client (Linux, macOS, Windows, Android, atau iOS), jalankan perintah penghubung dengan menunjuk ke URL server Headscale mandiri:

tailscale up --login-server https://headscale.domainanda.com

Client akan menampilkan URL otentikasi yang memuat node key unik (contoh: nodekey:1234567890abcdef).

3. Otorisasi Node di Server

Salin node key tersebut dan daftarkan pada server Headscale:

docker exec -it headscale headscale nodes register --user admin --key nodekey:1234567890abcdef

Alternatif registrasi otomatis tanpa persetujuan manual dapat menggunakan Pre-Authenticated Key:

docker exec -it headscale headscale preauthkeys create --user admin --reusable --expiration 24h

Gunakan kunci yang dihasilkan pada client:

tailscale up --login-server https://headscale.domainanda.com --authkey KUNCI_PREAUTH_ANDA

Implementasi Access Control List (ACL) Berbasis Least Privilege

Secara bawaan, seluruh node dalam Headscale dapat saling terhubung. Batasi lalu lintas antar-node dengan membuat berkas kebijakan ACL berbasis JSON di /opt/headscale/config/acl.json:

{
  "groups": {
    "group:admin": ["admin"]
  },
  "hosts": {
    "vps-prod": "100.64.0.1"
  },
  "acls": [
    {
      "action": "accept",
      "src": ["group:admin"],
      "dst": ["vps-prod:22,80,443"]
    }
  ]
}

Aktifkan jalur ACL pada /opt/headscale/config/config.yaml di bagian acl_policy_path:

acl_policy_path: "/etc/headscale/acl.json"

Muat ulang kontainer Headscale agar kebijakan baru diterapkan:

docker compose restart headscale

Best Practice: Subnet Routing, MagicDNS, dan Troubleshooting DERP Relay

1. Konfigurasi Subnet Routing untuk Homelab / LAN

Subnet Router memungkinkan akses ke seluruh subnet LAN lokal (misal: 192.168.1.0/24) tanpa perlu memasang agent Tailscale di setiap perangkat IoT atau IP Camera.

Aktifkan IP Forwarding di kernel Linux host router:

echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-headscale.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-headscale.conf
sudo sysctl -p /etc/sysctl.d/99-headscale.conf

Promosikan rute dari client router:

tailscale up --login-server https://headscale.domainanda.com --advertise-routes=192.168.1.0/24

Setujui pengajuan rute dari server Headscale:

docker exec -it headscale headscale routes list
docker exec -it headscale headscale routes enable -r ID_RUTE_SUBNET

2. Implementasi Internal MagicDNS

Fitur MagicDNS mengeliminasi kebutuhan menghafal IP virtual 100.64.x.x. Edit blok dns pada config.yaml:

dns:
  magic_dns: true
  base_domain: internal.net
  nameservers:
    global:
      - 1.1.1.1
      - 8.8.8.8

Setiap node kini dapat diakses langsung menggunakan FQDN internal seperti vps-prod.internal.net atau homelab.internal.net.

3. Troubleshooting DERP Relay dan NAT Traversal

WireGuard mencoba melakukan koneksi P2P direct via UDP STUN. Jika terhalang oleh Symmetric NAT atau CGNAT ISP, trafik akan dialihkan melalui relay DERP.

Periksa status koneksi dari perangkat client:

tailscale status
tailscale netcheck

Apabila hasilnya menunjukkan status relayed alih-alih direct, lakukan langkah isolasi berikut:

  1. Buka port UDP 3478 (STUN) pada firewall VPC/VPS (UFW atau Cloud Security Group).
  2. Pastikan port UDP tidak terblokir oleh aturan firewall upstream provider ISP.
  3. Pastikan HTTP/2 dan WebSocket tidak di-strip oleh reverse proxy Caddy/Nginx.
  4. Periksa ukuran MTU interface jaringan. Jika terjadi kekerdilan paket (packet fragmentation), sesuaikan MTU interface WireGuard ke 1280.

Checklist Implementasi Deployment Infrastructure

  • Port inbound publik ditutup total pada Firewall VPS (kecuali port 80/443 TCP untuk Caddy, dan 3478 UDP untuk STUN).
  • Kontainer Headscale berjalan stabil dengan konfigurasi volume persisten yang valid.
  • Domain kontroler diproteksi HTTPS SSL TLS aktif via Let's Encrypt.
  • Kebijakan ACL diterapkan dengan prinsip least-privilege.
  • IP Forwarding diaktifkan pada node subnet router.
  • Resolusi MagicDNS berjalan presisi antar-node internal.

Hardening Keamanan dan Kesimpulan

Penggunaan Headscale self-hosted memindahkan total kendali infrastruktur Zero Trust VPN ke tangan pengelola sistem. Seluruh port publik sensitif pada VPS dan homelab dapat ditutup rapat dari internet luar.

Rekomendasi hardening akhir:

  • Ubah opsi listen address pada daemon SSH VPS (/etc/ssh/sshd_config) agar hanya mendengarkan IP interface WireGuard Tailscale (100.64.x.x).
  • Nonaktifkan total port SSH (22) pada interface publik via UFW (sudo ufw deny 22/tcp).
  • Terapkan otentikasi berbasis SSH Key serta 2FA/MFA di tingkat OS.
  • Lakukan backup berkala pada file basis data SQLite (/opt/headscale/data/db.sqlite) dan kunci privat (private.key).

📖 Artikel Terkait