Cài đặt Headscale trên Fedora Server: Tự host mạng VPN Mesh Tailscale riêng tư

Fedora tutorial - IT technology blog
Fedora tutorial - IT technology blog

Tailscale là lựa chọn hàng đầu khi cần dựng VPN mesh nhờ cài đặt nhanh và cấu hình WireGuard gần như bằng 0. Dẫu vậy, bản SaaS miễn phí vẫn giới hạn 3 người dùng và 100 thiết bị. Với doanh nghiệp có chính sách bảo mật khắt khe hoặc homelab nhiều node, đẩy toàn bộ metadata định tuyến lên server của bên thứ ba không phải phương án tối ưu. Headscale giải quyết bài toán này: một coordination server mã nguồn mở, độc lập và tương thích hoàn toàn với Tailscale client.

Trên môi trường server, Fedora ghi điểm nhờ kernel 6.x mới, module WireGuard tích hợp sẵn ở cấp nhân và SELinux bảo vệ chặt chẽ. Dưới đây là quy trình triển khai Headscale thực tế trên Fedora Server 39/40.

1. Quick start: Triển khai Headscale trong 5 phút

Giả sử bạn đã có một máy Fedora Server sạch tại IP 203.0.113.10 (hoặc domain vpn.example.com đã trỏ A record về IP này).

Bước 1: Tải binary và phân quyền hệ thống

# Lấy bản release ổn định mới nhất
HEADSCALE_VERSION=$(curl -s https://api.github.com/repos/juanfont/headscale/releases/latest | grep tag_name | cut -d '"' -f 4 | sed 's/v//')
wget https://github.com/juanfont/headscale/releases/download/v${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION}_linux_amd64 -O /usr/local/bin/headscale
chmod +x /usr/local/bin/headscale

# Tạo user dịch vụ không có shell login để tăng bảo mật
useradd --system --shell /sbin/nologin --create-home headscale
mkdir -p /etc/headscale /var/lib/headscale /var/run/headscale
chown -R headscale:headscale /etc/headscale /var/lib/headscale /var/run/headscale

Bước 2: Thiết lập file cấu hình tối giản

Tạo file /etc/headscale/config.yaml với các thông số cơ bản:

cat <<'EOF' > /etc/headscale/config.yaml
server_url: http://203.0.113.10:8080
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 127.0.0.1:9090
grpc_listen_addr: 127.0.0.1:50443
grpc_allow_insecure: true

prefixes:
  v4: 100.64.0.0/10
  v6: fd7a:115c:a1e0::/48

derp:
  server:
    enabled: false
  urls:
    - https://controlplane.tailscale.com/derpmap/default
  auto_update_enabled: true
  update_frequency: 24h

disable_check_updates: true
ephemeral_node_inactivity_timeout: 30m
database:
  type: sqlite3
  sqlite:
    path: /var/lib/headscale/db.sqlite
EOF

Bước 3: Khởi chạy systemd service và mở firewall

cat <<'EOF' > /etc/systemd/system/headscale.service
[Unit]
Description=Headscale Coordination Server
After=network.target

[Service]
User=headscale
Group=headscale
ExecStart=/usr/local/bin/headscale serve
Restart=always
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now headscale

# Mở port điều phối 8080 qua firewalld
firewall-cmd --add-port=8080/tcp --permanent
firewall-cmd --reload

Bước 4: Tạo tài khoản và ghép nối node đầu tiên

Khởi tạo một user quản lý (trước đây gọi là namespace):

headscale users create devteam

Chuyển sang máy trạm (đã cài package tailscale), chạy lệnh xác thực trỏ về server riêng:

tailscale up --login-server http://203.0.113.10:8080

Màn hình terminal sẽ trả về chuỗi xác thực dạng mkey:0123456789abcdef.... Hãy sao chép key này, quay lại terminal server Fedora và phê duyệt:

headscale nodes register --user devteam --key <NODE_KEY_TỪ_CLIENT>

Kiểm tra nhanh bằng lệnh tailscale status trên client. Máy trạm đã được cấp một IP thuộc dải 100.64.x.x và kết nối sẵn sàng.

2. Cơ chế vận hành: Vì sao Headscale an toàn và nhẹ?

Tách rời Control Plane và Data Plane

Mô hình VPN truyền thống như OpenVPN hay IPsec tập trung toàn bộ băng thông qua máy chủ trung tâm (Hub-and-Spoke). Nếu server nghẽn cổ chai hoặc sập, toàn bộ kết nối tê liệt. Kiến trúc WireGuard mesh chia tách rõ rệt:

  • Data Plane (Băng thông thực tế): Dữ liệu mã hóa chạy ngang hàng (Peer-to-Peer) giữa client A và client B. Hai máy trong cùng phòng lab truyền file cho nhau đạt trọn tốc độ cổng mạng LAN (1–10 Gbps). Server Headscale không đứng giữa giải mã, cũng không chịu tải lưu lượng này.
  • Control Plane (Máy chủ điều phối): Headscale chỉ lưu public key, quản lý cấp phát IP nội bộ và hỗ trợ kỹ thuật NAT traversal (STUN). Khi hai node đã tìm thấy nhau và thiết lập phiên mã hóa, server điều phối có tắt đi thì kết nối hiện tại giữa hai máy vẫn truyền dữ liệu bình thường.

3 tham số cấu hình cốt lõi cần nhớ

  • server_url: Địa chỉ public client dùng để ping control server. Nếu chạy qua Nginx hoặc Caddy reverse proxy với HTTPS, bạn phải đổi thành URL chuẩn tên miền (chẳng hạn https://headscale.company.internal). Tuyệt đối không để IP nội bộ tại đây nếu có client kết nối từ ngoài Internet.
  • prefixes.v4: Dải IP cấp cho mạng mesh. Tailscale quy ước dải CGNAT RFC6598 (100.64.0.0/10). Giữ nguyên dải này giúp bạn không bao giờ lo trùng IP khi truy cập vào mạng gia đình (192.168.1.0/24) hay mạng văn phòng (10.0.0.0/8).
  • derp.urls: Danh sách server DERP relay công cộng. Nếu cả hai máy client đều nằm sau Symmetric NAT (NAT đối xứng ở mạng 4G/5G hoặc WiFi công cộng) và không thể đục lỗ UDP trực tiếp, Headscale sẽ mượn DERP server trung gian để chuyển tiếp gói tin, giữ kết nối liên tục.

3. Thiết lập nâng cao cho hạ tầng thực tế

Pre-authentication Key: Tự động hóa kết nối CI/CD và Server

Khi build cụm Kubernetes worker hoặc chạy máy ảo staging tự động, bạn không thể ngồi copy chuỗi mkey thủ công mỗi lần tạo node. Giải pháp là cấp sẵn một auth key dùng nhiều lần:

# Tạo key tái sử dụng trong vòng 24 giờ cho cụm dev
headscale preauthkeys create --user devteam --reusable --expiration 24h

Nhúng key vừa nhận vào script cloud-init hoặc pipeline triển khai:

tailscale up --login-server http://203.0.113.10:8080 --authkey <PREAUTH_KEY> --accept-routes

Subnet Router: Biến node thành cầu nối vào mạng LAN

Nhiều thiết bị như máy in mạng, switch quản lý hay camera IP không hỗ trợ cài Tailscale. Bạn có thể dùng chính node Fedora Server làm gateway để đứng ngoài vẫn truy cập được toàn bộ dải mạng 192.168.1.0/24.

# 1. Bật tính năng chuyển tiếp gói tin ở cấp kernel
echo 'net.ipv4.ip_forward = 1' | tee -a /etc/sysctl.d/99-tailscale.conf
echo 'net.ipv6.conf.all.forwarding = 1' | tee -a /etc/sysctl.d/99-tailscale.conf
sysctl -p /etc/sysctl.d/99-tailscale.conf

# 2. Yêu cầu node quảng bá dải LAN nội bộ
tailscale up --login-server http://203.0.113.10:8080 --advertise-routes=192.168.1.0/24

# 3. Phê duyệt route trên Headscale
headscale routes list
headscale routes enable -r 1

Giờ đây khi ngồi làm việc từ quán cà phê, laptop bật Tailscale có thể gọi trực tiếp dashboard Proxmox tại https://192.168.1.50:8006 mà không cần mở bất kỳ port nào trên router nhà mạng.

Kiểm soát truy cập bằng Access Control Lists (ACLs)

Khi chưa có ACL, mọi node chung user đều ping thông suốt tới nhau. Khi cần siết quyền (ví dụ chỉ cho nhóm dev truy cập port 80/443 của cụm web server), hãy tạo file /etc/headscale/acl.json:

{
  "acls": [
    {
      "action": "accept",
      "src": ["devteam"],
      "dst": ["tag:web:80,443"]
    }
  ],
  "tagOwners": {
    "tag:web": ["devteam"]
  }
}

Khai báo đường dẫn acl.policy_path: /etc/headscale/acl.json trong file cấu hình rồi reload service. Mọi nỗ lực truy cập vào port SSH 22 hoặc database 5432 trên các node gắn tag web sẽ bị drop ngay lập tức.

4. Kinh nghiệm vận hành và khắc phục sự cố

Tối ưu kết nối P2P: Chênh lệch giữa Direct và Relay

Để hai node đục lỗ NAT thành công và truyền trực tiếp bằng UDP thay vì đi vòng qua relay, bạn nên mở port WireGuard mặc định trên firewall server:

firewall-cmd --add-port=41641/udp --permanent
firewall-cmd --reload

Hãy chạy tailscale status trên máy trạm để đánh giá hiệu năng đường truyền:

# Kết nối lý tưởng (độ trễ ~15ms, băng thông 800+ Mbps)
100.64.0.2   workstation   devteam   linux   active; direct 14.225.2.18:41641

# Kết nối bị kẹt qua relay (độ trễ nhảy vọt lên 90-180ms, băng thông tụt còn 5-15 Mbps)
100.64.0.2   workstation   devteam   linux   active; relay "hkg"

Nếu thấy trạng thái relay "hkg" (Hong Kong), kiểm tra lại firewall và router xem UDP outbound có bị chặn hay không.

Backup nóng cơ sở dữ liệu SQLite an toàn

Toàn bộ danh bạ node, auth key và bảng định tuyến đều ghi vào file duy nhất /var/lib/headscale/db.sqlite. Không nên copy file thô bằng lệnh cp vì dễ làm hỏng file nếu server đang ghi dữ liệu. Hãy dùng lệnh backup an toàn của SQLite:

sqlite3 /var/lib/headscale/db.sqlite ".backup '/var/backups/headscale_$(date +%F_%H%M).sqlite'"

File backup này chỉ tốn vài trăm KB, bạn có thể ném vào cronjob chạy lúc 2h sáng mỗi ngày.

Khắc phục lỗi phân giải MagicDNS trên Fedora Client

Trường hợp máy client ping IP 100.64.0.2 thành công nhưng không gõ được hostname ping workstation, nguyên nhân 90% nằm ở xung đột cấu hình systemd-resolved:

resolvectl status tailscale0

Kiểm tra xem interface tailscale0 đã nhận DNS Server 100.100.100.100 hay chưa. Nếu thiếu search domain, hãy chạy lệnh sau để ép máy trạm ưu tiên DNS của Tailscale:

resolvectl domain tailscale0 "~example.com"
Share: