Tutorial Setup Headscale Self-Hosted: Zero Trust VPN di VPS Linux
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.
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/dataUnduh berkas konfigurasi acuan resmi dari repositori Headscale:
curl -fLo /opt/headscale/config/config.yaml https://raw.githubusercontent.com/juanfont/headscale/main/config-example.yamlBuka 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.sqlitePenjelasan 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 serveJalankan kontainer Headscale dalam mode detached:
cd /opt/headscale && docker compose up -dVerifikasi status kontainer dan log jalannya aplikasi:
docker compose logs -f headscaleKonfigurasi 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 caddyCaddy 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 admin2. 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.comClient 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:1234567890abcdefAlternatif registrasi otomatis tanpa persetujuan manual dapat menggunakan Pre-Authenticated Key:
docker exec -it headscale headscale preauthkeys create --user admin --reusable --expiration 24hGunakan kunci yang dihasilkan pada client:
tailscale up --login-server https://headscale.domainanda.com --authkey KUNCI_PREAUTH_ANDAImplementasi 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 headscaleBest 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.confPromosikan rute dari client router:
tailscale up --login-server https://headscale.domainanda.com --advertise-routes=192.168.1.0/24Setujui pengajuan rute dari server Headscale:
docker exec -it headscale headscale routes list
docker exec -it headscale headscale routes enable -r ID_RUTE_SUBNET2. 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.8Setiap 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 netcheckApabila hasilnya menunjukkan status relayed alih-alih direct, lakukan langkah isolasi berikut:
- 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.
- 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).


