Hướng dẫn triển khai mạng Mesh VPN với Nebula: Kết nối hàng nghìn server an toàn từ Slack

Network tutorial - IT technology blog
Network tutorial - IT technology blog

VPN truyền thống bắt đầu vỡ trận khi hạ tầng lớn dần

Quản lý vài chục server trải dài trên nhiều cloud provider — AWS ở Virginia, Hetzner ở Nuremberg, thêm mấy VPS ở Singapore — và bạn sẽ sớm nhận ra OpenVPN hay WireGuard theo mô hình hub-and-spoke không còn phù hợp nữa. Tất cả traffic phải đi qua server trung tâm. Latency tăng. Server đó mà chết là cả mạng tắt.

Slack đối mặt đúng vấn đề này ở quy mô hàng nghìn node. Họ tự xây và open-source Nebula — một mesh VPN overlay, trong đó mỗi node kết nối trực tiếp peer-to-peer với nhau sau khi xác thực qua một lighthouse (node chỉ đường, không relay traffic). Không còn điểm chết đơn lẻ. Không còn bottleneck.

Mình đã dùng Nebula để kết nối 3 VPS ở 3 datacenter khác nhau và một laptop dev. Mọi thứ chạy sau 30 phút setup. Latency giữa hai node cùng region thấp hơn ~15ms so với WireGuard qua relay.

Cơ chế hoạt động của Nebula

Mỗi node nhận một IP ảo trong dải bạn tự chọn (thường là 192.168.100.0/24). Traffic giữa các node mã hóa bằng Noise Protocol Framework — cùng nền tảng với WireGuard, nhưng kiến trúc khác hoàn toàn. Bốn điểm tạo nên sự khác biệt:

  • Lighthouse: Node có IP public, giúp các node khác tìm thấy nhau (hoạt động như STUN server). Không relay traffic, chỉ làm discovery.
  • Peer-to-peer thực sự: Sau discovery, hai node kết nối thẳng qua UDP hole punching — kể cả khi cả hai đều sau NAT.
  • Certificate-based auth: Mỗi node có certificate ký bởi CA của bạn. Không có cert hợp lệ thì không vào được mạng, đơn giản vậy thôi.
  • Firewall nhúng trong config: Kiểm soát traffic ở tầng overlay, không cần iptables riêng.

Cài đặt Nebula

Yêu cầu

  • Ít nhất 1 server có IP public (làm lighthouse)
  • Linux/macOS/Windows đều hỗ trợ
  • UDP port 4242 mở trên firewall của lighthouse

Tải binary

Trên mỗi node (kể cả lighthouse), tải binary từ GitHub releases:

# Trên Linux x86_64
wget https://github.com/slackhq/nebula/releases/latest/download/nebula-linux-amd64.tar.gz
tar -xzf nebula-linux-amd64.tar.gz
sudo mv nebula nebula-cert /usr/local/bin/

Tạo CA và certificate cho từng node

Bước này chạy một lần duy nhất trên máy admin, sau đó phân phối cert đến từng node qua scp. Quan trọng: ca.key không bao giờ rời khỏi máy này.

# Tạo CA
nebula-cert ca -name "MyInfra CA"
# Sinh ra: ca.crt và ca.key — giữ ca.key CỰC KỲ bí mật

# Tạo cert cho lighthouse (IP ảo: 192.168.100.1)
nebula-cert sign -name "lighthouse" \
  -ip "192.168.100.1/24" \
  -ca-crt ca.crt -ca-key ca.key
# Sinh ra: lighthouse.crt, lighthouse.key

# Tạo cert cho server app (IP ảo: 192.168.100.10)
nebula-cert sign -name "app-server" \
  -ip "192.168.100.10/24" \
  -groups "servers" \
  -ca-crt ca.crt -ca-key ca.key

# Tạo cert cho laptop dev (IP ảo: 192.168.100.50)
nebula-cert sign -name "dev-laptop" \
  -ip "192.168.100.50/24" \
  -groups "developers" \
  -ca-crt ca.crt -ca-key ca.key

Khi cần tính subnet cho dải IP overlay, mình hay dùng toolcraft.app/vi/tools/developer/ip-subnet-calculator — nhập CIDR là ra ngay network range, broadcast, số host tối đa. Tiện hơn nhiều so với tính tay.

Cấu hình chi tiết

Config cho Lighthouse

Tạo file /etc/nebula/config.yaml trên lighthouse server:

pki:
  ca: /etc/nebula/ca.crt
  cert: /etc/nebula/lighthouse.crt
  key: /etc/nebula/lighthouse.key

lighthouse:
  am_lighthouse: true

listen:
  host: 0.0.0.0
  port: 4242

punchy:
  punch: true

logging:
  level: info

firewall:
  outbound:
    - port: any
      proto: any
      host: any
  inbound:
    - port: any
      proto: icmp
      host: any

Config cho node thông thường (app-server, dev-laptop)

pki:
  ca: /etc/nebula/ca.crt
  cert: /etc/nebula/app-server.crt
  key: /etc/nebula/app-server.key

lighthouse:
  am_lighthouse: false
  interval: 60
  hosts:
    - "192.168.100.1"  # IP ảo của lighthouse

static_host_map:
  "192.168.100.1": ["203.0.113.10:4242"]  # IP public thật của lighthouse

listen:
  host: 0.0.0.0
  port: 4242

punchy:
  punch: true
  respond: true

logging:
  level: info

firewall:
  outbound:
    - port: any
      proto: any
      host: any
  inbound:
    - port: any
      proto: icmp
      host: any
    # Cho phép SSH từ group developers
    - port: 22
      proto: tcp
      groups:
        - developers
    # Cho phép app traffic giữa các server
    - port: 8080
      proto: tcp
      groups:
        - servers

Deploy cert và chạy thử

Copy cert lên từng node rồi test trước khi đưa vào systemd:

# Copy lên lighthouse
scp ca.crt lighthouse.crt lighthouse.key root@<lighthouse-ip>:/etc/nebula/

# Copy lên app-server
scp ca.crt app-server.crt app-server.key root@<app-server-ip>:/etc/nebula/

# Chạy foreground để xem log trực tiếp
sudo nebula -config /etc/nebula/config.yaml

Khi log xuất hiện dòng Handshake message sentHandshake message received, nghĩa là hai node đã bắt tay thành công. Lúc đó mới chuyển sang systemd:

cat > /etc/systemd/system/nebula.service << 'EOF'
[Unit]
Description=Nebula VPN
After=network.target

[Service]
ExecStart=/usr/local/bin/nebula -config /etc/nebula/config.yaml
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now nebula

Kiểm tra kết nối và Monitoring

Ping qua overlay network

Khi Nebula chạy trên tất cả node, kiểm tra bằng IP ảo:

# Từ app-server, ping dev-laptop qua Nebula overlay
ping 192.168.100.50

# Xem thông tin cert của node hiện tại
nebula-cert print -path /etc/nebula/app-server.crt

Xem trạng thái peer qua Prometheus metrics

Nebula hỗ trợ expose metrics theo định dạng Prometheus. Thêm vào config:

stats:
  type: prometheus
  listen: 127.0.0.1:8080
  path: /metrics
  namespace: nebula
  subsystem: stats
  interval: 10s
# Xem số tunnel đang active, handshake thành công/thất bại
curl -s http://127.0.0.1:8080/metrics | grep -E "(tunnel|handshake)"

Debug firewall rules

# Bật log level debug để xem packet bị allow hay deny
# Sửa config: logging.level: debug
# Restart rồi theo dõi
journalctl -u nebula -f | grep -E "(ALLOW|DENY|firewall)"

Test throughput thực tế

Đo băng thông giữa hai node sau khi mạng ổn định:

# Trên node receiver (192.168.100.10)
iperf3 -s -B 192.168.100.10

# Trên node sender — 4 luồng song song, test 30 giây
iperf3 -c 192.168.100.10 -t 30 -P 4

Những điều cần nhớ khi vận hành thực tế

Vài tuần đầu chạy production mình đã vấp phải đủ thứ. Ghi lại đây để bạn khỏi mất công debug:

  • Lighthouse redundancy: Tối thiểu 2 lighthouse ở 2 datacenter khác nhau. Thêm IP lighthouse thứ hai vào lighthouse.hostsstatic_host_map trong config của mỗi node là xong.
  • Certificate expiry: Cert mặc định không có thời hạn — nghe có vẻ tiện nhưng thực ra rất nguy hiểm nếu cert bị lộ. Luôn đặt -duration 8760h (1 năm) khi ký cert và có lịch rotate định kỳ.
  • MTU overhead: Nebula thêm khoảng 60 bytes header. App bị timeout hoặc packet loss không rõ lý do? Thử thêm tun: mtu: 1300 vào config.
  • Revoke node: Cần kick một node ra khỏi mạng ngay lập tức? Thêm cert của nó vào pki.blocklist trong config của tất cả node, reload — Nebula từ chối kết nối từ cert đó ngay.

So với WireGuard, Nebula phức tạp hơn ở bước setup ban đầu. Nhưng firewall rules nhúng thẳng vào node config tiết kiệm rất nhiều công quản lý access control khi số node vượt qua 10. Không cần iptables riêng, không cần đồng bộ rules thủ công giữa các server. Với hạ tầng đang lớn dần, đây là lựa chọn đáng thay thế OpenVPN hub-and-spoke cổ điển.

Share: