项目背景
业务痛点
随着业务增长,目前海外拥有十几个独立的私有网络环境(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
|
# 创建必要目录
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
|
安全加固
- 使用mTLS认证
- 限制IP访问
1
2
3
4
|
ip_prefixes:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
|
- 定期轮换密钥
1
2
3
|
# 重新生成私钥
sudo rm /var/lib/headscale/private.key
sudo systemctl restart headscale
|
性能优化
-
部署多个DERP服务器
- 每个大区域部署一个DERP节点
- 使用CDN加速DERP访问
-
优化Keep-alive
1
2
|
# 客户端配置
sudo tailscale up --ping=20s # 心跳间隔
|
-
单点故障风险
如果完全禁用官方 DERP(urls: []),且只有一台自建 DERP 服务器,当该服务器宕机或网络故障时,所有需要中继的客户端将无法通信。对于生产环境,建议至少保留官方DERP作为备份,或部署多个自建DERP节点。
-
证书要求
嵌入式 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%。