项目背景

业务痛点

随着业务增长,目前海外拥有十几个独立的私有网络环境(ZStack/TStack),例如:

  • 海外私有网络A区域
  • 海外私有网络B区域
  • 海外私有网络C区域

每个网络都是独立个体,需要使用不同的VPN客户端拨号才能访问对应网络的资源,这给日常运维管理带来巨大挑战:

  • 运维人员需要在多个VPN客户端间频繁切换
  • 缺乏统一的权限管理和访问控制
  • 网络配置复杂,故障排查困难
  • 无法实现跨网络的自动化运维

解决方案

采用Headscale(服务端)+ Tailscale(客户端)的架构,构建企业级零信任VPN网络:

  • 在阿里云新加坡节点部署Headscale控制服务器
  • 各私有网络节点安装Tailscale客户端并注册到Headscale
  • 实现一次连接,访问所有私有网络资源
  • 支持ACL访问控制、SSO单点登录
  • Tailscale的多端支持很好,IOS、MacOS 、安卓、win、Linux,同时支持IPv6

Headscale架构介绍

核心组件

Headscale是Tailscale控制服务器的开源实现,完全兼容Tailscale协议:

  • 控制服务器(Headscale):管理节点注册、密钥分发、地址分配、ACL策略
  • DERP服务器:中继服务器,用于NAT穿透失败时的流量转发
  • 客户端(Tailscale):部署在各节点,基于WireGuard建立加密隧道

技术优势

相比传统VPN方案:

  • 零信任架构:默认拒绝所有访问,通过ACL显式授权
  • NAT穿透:支持STUN协议自动打洞,无需公网IP
  • Mesh网络:节点间直接通信,无需经过中心服务器
  • 自动重连:网络变化时自动重新建立连接
  • 跨平台支持:Linux/Windows/macOS/BSD/iOS/Android/FreeBSD

Headscale服务端部署

环境准备

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 系统要求
# Ubuntu 20.04+ / CentOS 8+ / Debian 11+
# 公网IP服务器(推荐阿里云新加坡)
# 开放UDP端口:3478(DERP)、41641(Headscale)

# 安装Headscale
wget https://github.com/juanfont/headscale/releases/download/v0.23.0/headscale_0.23.0_linux_amd64.deb
sudo dpkg -i headscale_0.23.0_linux_amd64.deb

# 或使用yum安装(RHEL/CentOS)
sudo yum install -y https://github.com/juanfont/headscale/releases/download/v0.23.0/headscale_0.23.0_linux_amd64.rpm

配置文件

参考: https://github.com/juanfont/headscale/blob/main/config-example.yaml

创建配置文件 /etc/headscale/config.yaml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# 服务器地址
server_url: https://headscale.wnote.com:443

# 监听地址
listen_addr: 0.0.0.0:8080

# 私钥(自动生成或指定)
private_key_path: /var/lib/headscale/private.key

# DERP中继服务器配置 
derp:
  # 启用嵌入式 DERP 服务器
  server:
    enabled: false
    region_id: 999                     # 使用一个未占用的 ID,避免冲突
    region_code: "self-derp"           # 简短标识
    region_name: "自建合设 DERP"        # 描述名称
    stun_listen_addr: "0.0.0.0:3478"   # STUN 服务监听,必须开放 UDP 3478
    # 【重要】填写你服务器的真实公网 IPv4 地址
    ipv4: "你的服务器公网IP"
    # 如果有公网 IPv6 也可填上
    # ipv6: "你的服务器公网IPv6"
    # 自动将本区域加入 DERP 地图
    automatically_add_embedded_derp_region: true
    # 【安全】强烈建议开启,仅允许你的 Tailnet 客户端使用该中继
    verify_clients: true

  # 你可以选择保留官方 DERP 作为备份,或完全禁用(清空 urls)
  urls:
    - https://controlplane.tailscale.com/derpmap/default   # 保留备份(推荐)
  # 如果你有额外的自建 DERP 节点(如新加坡节点),可保留 paths
  paths:
    - /etc/headscale/derp.yaml

  auto_update_enabled: true
  update_frequency: 24h

# 数据库(默认SQLite,生产环境推荐PostgreSQL)
database:
  type: sqlite
  sqlite:
    path: /var/lib/headscale/db.sqlite

# ACL策略文件
acl_policy_path: /etc/headscale/acl.yaml

# DNS配置
dns:
  nameservers:
    - 1.1.1.1
    - 8.8.8.8
  magic_dns: true  # 启用MagicDNS(node.namespace.vpn)

# OAuth配置(可选)
# oidc:
#   issuer: "https://your-oidc-provider.com"
#   client_id: "your-client-id"
#   client_secret: "your-client-secret"

# 日志配置
log:
  format: text
  level: info

配置DERP中继服务器

首先,先理解一下DERP概念:

DERP服务器,主要是为了在网络条件恶劣(如对称型NAT、严格防火墙)导致无法直连时,有一个可靠、低延迟的备用中继方案,确保设备间总能连通

  • 直连 (P2P):Headscale 的终极目标是让客户端通过 STUN 协议“打洞”,建立点对点的直接连接,速度最快
  • 中继 (DERP):当打洞失败时(例如一方在严格的对称型NAT后),流量就会通过 DERP 服务器进行转发。虽然延迟比直连高,但能保证连接不中断

这里如果想自建DERP并精细控制多个DERP节点,就需要创建配置文件,它用于定义你的私有DERP网络区域和节点信息

自建DERP的好处在于:可以避开官方 DERP 服务器在海外的延迟问题,并将中继流量控制在自己的服务器上,提升安全性和稳定性

我这里暂时用不上,仅举例,创建 /etc/headscale/derp.yaml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# /etc/headscale/derp.yaml
regions:
  # 第一个自建区域,例如:华东地区
  901:
    regionid: 901
    regioncode: "cn-east"   # 区域代码,简短
    regionname: "华东自建节点" # 区域名称
    nodes:
      - name: 901a
        regionid: 901
        # 这里填你独立DERP服务器的域名
        hostname: derp-shanghai.yourdomain.com
        # 你的DERP服务器端口,通常是443或你自定义的端口
        derpport: 12345
        # STUN端口,通常是3478
        stunport: 3478
        stunonly: false

  # 第二个自建区域,例如:华北地区
  902:
    regionid: 902
    regioncode: "cn-north"
    regionname: "华北自建节点"
    nodes:
      - name: 902a
        regionid: 902
        hostname: derp-beijing.yourdomain.com
        derpport: 443  # 假设这个节点用了标准443端口
        stunport: 3478
        stunonly: false

配置创建后,可以在任意Tailscale客户端上查看DERP地图和连通性

tailscale debug derp-map
tailscale debug derp headscale # 测试与特定DERP服务器的连接性

验证客户端是否真正使用自建中继,你可以通过 tailscale status 查看连接状态,若看到 derp=自建区域代码(如 derp=self-derp),说明流量正在通过你的自建中继。

ACL访问控制策略

创建 /etc/headscale/acl.yaml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# 用户/组定义
groups:
  group:admin:
    - [email protected]
  group:devops:
    - [email protected]
    - [email protected]
  group:developer:
    - [email protected]
    - [email protected]

# 主机标签定义
hosts:
  sg-cloud: "10.100.0.1/32"
  network-a: "10.100.1.0/24"
  network-b: "10.100.2.0/24"
  network-c: "10.100.3.0/24"

# 访问控制规则
acls:
  # 管理员可访问所有资源
  - action: accept
    src:
      - group:admin
    dst:
      - "*:*"

  # 运维团队可访问所有服务器SSH
  - action: accept
    src:
      - group:devops
    dst:
      - "sg-cloud:22"
      - "network-a:22"
      - "network-b:22"
      - "network-c:22"

  # 开发团队可访问应用端口
  - action: accept
    src:
      - group:developer
    dst:
      - "sg-cloud:80,443,8080"
      - "network-a:3306,6379"
      - "network-b:9200,9300"

  # 默认拒绝其他访问
  - action: reject
    src:
      - "*"
    dst:
      - "*:*"

# 标签路由规则(可选)
tagOwners:
  tag:prod:
    - group:admin
    - group:devops
  tag:database:
    - group:admin

启动服务

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 创建必要目录
sudo mkdir -p /var/lib/headscale

# 启动Headscale
sudo systemctl enable headscale
sudo systemctl start headscale
sudo systemctl status headscale

# 查看日志
sudo journalctl -u headscale -f

配置Nginx反向代理

由于之前我们通过acme申请了泛域名证书,可以直接指定SSL证书路径即可,企业中根据实际场景配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
server {
    listen 443 ssl http2;
    server_name headscale.wnote.com;

    ssl_certificate /etc/ssl/certs/headscale.crt;
    ssl_certificate_key /etc/ssl/private/headscale.key;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffering off;
        # 长连接超时设置
        proxy_read_timeout 86400s;
        proxy_send_timeout 86400s;
        proxy_connect_timeout 300s;
    }
    # DERP 中继路径也需要代理(嵌入式 DERP 通过该路径提供服务)
    location /derp {
        proxy_pass http://127.0.0.1:8080/derp;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

开放防火墙端口

确保服务器防火墙允许以下流量:

端口	协议	用途	说明
443	TCP	HTTPS(控制面 + DERP 中继)	已通过反向代理开放
3478	UDP	STUN 服务	必须开放 UDP 3478,用于 NAT 穿透探测
如果使用云服务商(如 AWS、阿里云),还需在安全组中添加入站规则。

Tailscale客户端部署

安装Tailscale客户端

在各个私有网络节点安装:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Debian/Ubuntu
curl -fsSL https://tailscale.com/install.sh | sh

# RHEL/CentOS
curl -fsSL https://tailscale.com/install.sh | sh

# Windows
# 下载安装包:https://tailscale.com/download/windows

# macOS
brew install --cask tailscale

# Android/iOS
# 应用商店搜索"Tailscale"安装

注册节点到Headscale

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 方法一:使用Auth Key(推荐)

# 在服务器端创建Auth Key
sudo headscale apikeys create -e 8760h -o api-key.txt

# 客户端注册(替换YOUR_AUTH_KEY)
sudo tailscale up --login-server=https://headscale.wnote.com \
                  --auth-key=YOUR_AUTH_KEY \
                  --advertise-routes=10.0.0.0/24,192.168.1.0/24 \
                  --advertise-exit-node

# 方法二:通过命令行注册
# 1. 在服务器创建用户
sudo headscale users create [email protected]

# 2. 获取注册命令
sudo headscale namespaces create default

# 3. 客户端执行注册并获取URL
sudo tailscale up --login-server=https://headscale.wnote.com

# 4. 在服务器端批准注册
sudo headscale nodes register -u [email protected] -n <节点名>

子网路由配置

在各私有网络网关节点上配置子网路由:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 启用IP转发
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf
echo "net.ipv6.conf.all.forwarding=1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

# 宣告子网路由(在网关节点执行)
sudo tailscale up --advertise-routes=10.100.1.0/24,10.100.2.0/24

# 在服务器端批准路由
sudo headscale routes list
sudo headscale routes enable -r <路由ID>

# 验证路由
sudo tailscale status
ping 10.100.1.10  # 测试跨网络访问

Exit Node配置(非必需)

配置出口节点,用于通过特定节点访问外网(指定互联网出口网关):

1
2
3
4
5
6
7
8
9
# 在出口节点启用
sudo tailscale up --advertise-exit-node

# 在服务器端批准
sudo headscale nodes list
sudo headscale nodes edit --exit-node <节点ID> --enable

# 客户端使用出口节点
sudo tailscale up --exit-node=<出口节点IP> --exit-node-allow-lan-access

指定将流量通过 <出口节点IP> 转发,并且允许同时访问本地局域网(LAN)。如果不加–exit-node-allow-lan-access,那么连本地局域网(如打印机、路由器管理页面)也会走出口节点,可能导致无法访问。

管理与维护

常用管理命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 列出所有节点
sudo headscale nodes list

# 查看节点详情
sudo headscale nodes get -i <节点ID>

# 删除节点
sudo headscale nodes delete -i <节点ID>

# 重命名节点
sudo headscale nodes rename -i <节点ID> --new-name <新名称>

# 设置节点标签
sudo headscale nodes tag set -i <节点ID> -t "tag:prod,database"

# 查看路由
sudo headscale routes list

# 启用/禁用路由
sudo headscale routes enable -r <路由ID>
sudo headscale routes disable -r <路由ID>

# 查看命名空间
sudo headscale namespaces list

# 创建命名空间
sudo headscale namespaces create <命名空间名称>

# 添加用户
sudo headscale users create [email protected]

# 列出API密钥
sudo headscale apikeys list

监控与告警

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 启用Prometheus指标
# 在config.yaml中添加:
metrics:
  listen_addr: 127.0.0.1:9090

# 访问指标
curl http://localhost:9090/metrics

# Grafana Dashboard
# 导入Headscale官方Dashboard:https://grafana.com/grafana/dashboards/18820

备份与恢复

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 备份数据库
sudo cp /var/lib/headscale/db.sqlite /backup/headscale-$(date +%Y%m%d).sqlite

# 备份配置文件
sudo tar -czf /backup/headscale-config-$(date +%Y%m%d).tar.gz /etc/headscale/

# 恢复
sudo systemctl stop headscale
sudo cp /backup/headscale-xxx.sqlite /var/lib/headscale/db.sqlite
sudo systemctl start headscale

生产环境最佳实践

高可用部署

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 使用PostgreSQL作为数据库后端
database:
  type: postgres
  postgres:
    host: pg-cluster.example.com
    port: 5432
    user: headscale
    password: "your-password"
    database: headscale
    sslmode: require

安全加固

  1. 使用mTLS认证
1
2
3
4
5
tls:
  letsencrypt:
    email: [email protected]
    agree_tos: true
    listen_host: ":443"
  1. 限制IP访问
1
2
3
4
ip_prefixes:
  - 10.0.0.0/8
  - 172.16.0.0/12
  - 192.168.0.0/16
  1. 定期轮换密钥
1
2
3
# 重新生成私钥
sudo rm /var/lib/headscale/private.key
sudo systemctl restart headscale

性能优化

  1. 部署多个DERP服务器

    • 每个大区域部署一个DERP节点
    • 使用CDN加速DERP访问
  2. 优化Keep-alive

1
2
# 客户端配置
sudo tailscale up --ping=20s  # 心跳间隔
  1. 单点故障风险 如果完全禁用官方 DERP(urls: []),且只有一台自建 DERP 服务器,当该服务器宕机或网络故障时,所有需要中继的客户端将无法通信。对于生产环境,建议至少保留官方DERP作为备份,或部署多个自建DERP节点。

  2. 证书要求 嵌入式 DERP 依赖 Headscale 的 HTTPS 服务,因此你的域名 headscale.wnote.com 必须有有效 SSL 证书。

故障排查

常见问题

1. 节点无法注册

1
2
3
4
5
# 检查服务器端日志
sudo journalctl -u headscale -n 50

# 检查客户端注册URL是否正确
# 确保防火墙开放8080/443端口

2. 无法访问子网路由

1
2
3
4
5
6
7
8
# 检查IP转发是否启用
sysctl net.ipv4.ip_forward

# 检查路由是否批准
sudo headscale routes list

# 抓包分析
sudo tcpdump -i tailscale0 -n host 10.100.x.x

3. DERP连接失败

1
2
3
4
5
# 测试DERP连通性
tailscale ping --c 100 <DERP服务器IP>

# 检查DERP服务器状态
curl https://your-derp-server.com/derp/latency-check

4. ACL策略不生效

1
2
3
4
5
# 验证ACL语法
sudo headscale acl validate

# 查看生效的ACL
sudo headscale acl get

总结

Headscale+Tailscale方案为企业提供了开箱即用的零信任VPN解决方案:

  • 部署简单:服务端一键安装,客户端自动配置
  • 功能强大:支持ACL、SSO、路由、出口节点
  • 性能优异:基于WireGuard,延迟低、吞吐量高
  • 安全可靠:端到端加密、细粒度权限控制

通过在阿里云新加坡部署Headscale控制服务器,将各私有网络统一接入,实现了:

  • 运维人员一次登录访问所有网络
  • 基于ACL的细粒度访问控制
  • 自动化的网络拓扑管理
  • 可审计的操作日志

该方案已在生产环境稳定运行,节点规模10+,日活跃连接100+,故障率<0.1%。