概述

什么是 Tailscale?

Tailscale 是基于 WireGuard 的零配置 VPN 解决方案,可以轻松地将分布在不同网络中的设备组建成一个安全的虚拟局域网。

什么是 Headscale?

Headscale 是 Tailscale 控制服务器的开源自托管实现。使用 Headscale 可以完全自主控制你的网络,无需依赖 Tailscale 官方服务器。

什么是 DERP?

DERP (Designated Encrypted Relay for Packets) 是 Tailscale 的中继服务器,当两个节点无法直接建立点对点连接时(如双方都在 NAT 后面),流量会通过 DERP 服务器中继。

什么是 STUN?

STUN(Session Traversal Utilities for NAT,NAT会话穿越工具)是一种网络协议,它帮助位于网络地址转换器(NAT)设备后方的客户端(如电脑、手机)发现自己的公共IP地址和端口,并了解NAT设备的类型,从而在P2P(点对点)应用(如VoIP、视频会议)中,让这些设备能够相互直接通信,建立端到端的连接

核心技术原理

WireGuard 协议

WireGuard 是一种现代化的 VPN(虚拟专用网络)协议,基于 UDP,支持漫游,以极简设计、高性能和强安全性著称。相比传统 VPN(如 OpenVPN、IPsec),它代码量小、配置简单、速度快,近年来被大量用于个人科学上网、企业内网互联、云服务器组网等场景。

NAT 穿透原理

利用中继服务器获取双方公网地址,再通过“打洞”或建立临时映射实现内网设备间直接通信,关键在于利用NAT设备允许内部发起的连接返回数据的特性,通过一个公网中介(STUN/TURN服务器)发现彼此的公网IP和端口,并制造一个允许对方连接的“洞”,最终实现点对点通信

2. UDP 打洞 (UDP Hole Punching)

当两个设备都在 NAT 后面时,通过 STUN 获取各自的公网地址后,同时向对方发送 UDP 包来建立连接:

1
2
3
4
时间轴:
T1: A 向 B 的公网地址发送 UDP 包(在 A 的 NAT 上打洞)
T2: B 向 A 的公网地址发送 UDP 包(在 B 的 NAT 上打洞)
T3: 双方的包都能通过,直连建立

3. DERP 中继

当 NAT 穿透失败时(如双方都是 Symmetric NAT),使用 DERP 服务器中继流量:
![upload successful](/images/自建Headscale + DERP/pasted-0.png)

连接建立流程

1
2
3
4
5
6
7
8
9
10
11
12
13
1. 客户端启动
└─► 连接 Headscale 控制服务器
└─► 获取网络配置和其他节点信息

2. 发现其他节点
└─► 通过 STUN 获取自己的公网地址
└─► 尝试 UDP 打洞直连
├─► 成功:建立 WireGuard 直连隧道
└─► 失败:通过 DERP 中继

3. 持续优化
└─► 后台持续尝试建立直连
└─► 一旦成功,从 DERP 切换到直连

架构说明

系统架构图

整体架构

端口说明

端口 协议 服务 说明
443 TCP Nginx HTTPS 反向代理,SSL 终结
8080 TCP Headscale 控制服务器(本地监听)
3478 UDP STUN NAT 类型检测和穿透辅助
50443 TCP gRPC Headscale CLI(本地监听)
41641 UDP WireGuard 客户端 P2P 通信端口

数据流向

控制平面(Control Plane)

1
客户端 ──HTTPS──► Nginx ──HTTP──► Headscale

数据平面(Data Plane)

1
2
3
4
方式1 - 直连:
设备A ◄────WireGuard────► 设备B
方式2 - DERP中继:
设备A ──► DERP服务器 ──► 设备B

前置条件

服务器要求

一台公网服务器,一个备案了的域名,不然https不会放行,vps位置最好距离自己位置近的,然后将域名解析到服务器上后面会用到,服务器配置安全组把80,443,3478端口开放,我是一开始全开了,以免配置过程中有问题。

服务端部署

1. 安装 Headscale

1
2
3
4
5
6
# 下载最新版本
wget https://github.com/juanfont/headscale/releases/download/v0.26.1/headscale_0.26.1_linux_amd64.deb
# 安装
sudo dpkg -i headscale_0.26.1_linux_amd64.deb
# 验证安装
headscale version

验证安装

2. 配置 Headscale

编辑配置文件 /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
64
65
66
67
68
69
70
71
72
73
74
---
# 客户端连接的 URL(必须是 HTTPS,因为默认拒绝htpp,因为需要传输认证密钥等敏感信息)
server_url: https://xxx.yourdomain.com:443
# 监听地址(由 Nginx 反向代理,只监听本地)
listen_addr: 127.0.0.1:8080

# 指标监听地址,Headscale 会在这个地址暴露运行状态的监控数据,供监控系统采集。
metrics_listen_addr: 127.0.0.1:9090

# gRPC 监听地址(用于 CLI)
grpc_listen_addr: 127.0.0.1:50443
grpc_allow_insecure: false

# Noise 协议私钥,现代加密通信框架,主要用户加密通信,验证身份,建立安全的控制通道
noise:
private_key_path: /var/lib/headscale/noise_private.key

# IP 地址分配
prefixes:
v4: 100.64.0.0/10
v6: fd7a:115c:a1e0::/48
allocation: sequential

# 内置 DERP 服务器配置
derp:
server:
enabled: true
region_id: 901
region_code: "cn"
region_name: "China"
stun_listen_addr: "0.0.0.0:3478"
private_key_path: /var/lib/headscale/derp_server_private.key
automatically_add_embedded_derp_region: true
# 验证客户端(可选,增加安全性)
verify_clients: true

# 不使用官方 DERP 服务器
urls: []
paths: []
auto_update_enabled: false
update_frequency: 24h

# 数据库配置
database:
type: sqlite
sqlite:
path: /var/lib/headscale/db.sqlite
write_ahead_log: true

# TLS 配置(由 Nginx 处理,这里留空)
tls_cert_path: ""
tls_key_path: ""

# DNS 配置
dns:
magic_dns: false
base_domain: yourdomain.com
nameservers:
global:
- 114.114.114.114
- 8.8.8.8

# Unix socket
unix_socket: /var/run/headscale/headscale.sock
unix_socket_permission: "0770"

# 日志
log:
format: text
level: info

# 禁用官方日志收集
logtail:
enabled: false

3. 安装并配置 Nginx

1
2
3
4
5
# 安装 Nginx
sudo apt update
sudo apt install -y nginx
# 安装 Certbot
sudo apt install -y certbot python3-certbot-nginx

创建 Nginx 配置 /etc/nginx/sites-available/headscale

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
# Headscale 控制服务器
server {
listen 80;
listen [::]:80;
server_name xxx.yourdomain.com;

location /.well-known/acme-challenge/ {
root /var/www/html;
try_files $uri =404;
}

location / {
return 301 https://$server_name$request_uri;
}
}

server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name xxx.yourdomain.com;

ssl_certificate /etc/letsencrypt/live/xxx.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/xxx.yourdomain.com/privkey.pem;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

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 86400;
proxy_send_timeout 86400;
}
}

启用配置并申请证书:

1
2
3
4
5
6
7
8
# 启用站点
sudo ln -sf /etc/nginx/sites-available/headscale /etc/nginx/sites-enabled/
# 测试配置
sudo nginx -t
# 重载 Nginx
sudo systemctl reload nginx
# 申请 SSL 证书
sudo certbot certonly --webroot -w /var/www/html -d xxx.yourdomain.com

4. 启动 Headscale

1
2
3
4
5
6
# 启动服务
sudo systemctl start headscale
# 设置开机启动
sudo systemctl enable headscale
# 查看状态
sudo systemctl status headscale

5. 创建用户和预授权密钥

1
2
3
4
5
6
7
8
9
# 创建用户
headscale users create myuser
# 查看用户列表
headscale users list
# 创建预授权密钥(30天有效,可重复使用)
# 注意:-u 后面跟用户 ID,不是用户名
headscale preauthkeys create -u 1 --expiration 720h --reusable
# 查看预授权密钥
headscale preauthkeys list -u 1

2.在Headscale服务器上批准路由

1
2
3
4
5
6
7
#如果客户端有宣告路由的话才需要批准
# 查看待批准的路由
headscale nodes list-routes
# 批准路由(-i 是节点 ID,-r 是路由)
headscale nodes approve-routes -i 3 -r 192.168.2.0/24
# 验证路由已启用
headscale nodes list-routes

查看路由

3.在网关节点上启用IP转发

1
2
3
4
5
6
7
# 临时启用
sudo sysctl -w net.ipv4.ip_forward=1
# 永久启用
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
# 验证
cat /proc/sys/net/ipv4/ip_forward # 应该输出 1

客户端配置

macOS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 安装 Tailscale
brew install --cask tailscale
# 退出官方服务
Tailscale logout
# 连接到自建 Headscale
Tailscale up \
--login-server https://xxx.yourdomain.com \
--authkey YOUR_PREAUTHKEY
# 查看状态
Tailscale status
# 网络诊断
Tailscale netcheck
# 测试连接
Tailscale ping <目标IP>

Ubuntu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 安装 Tailscale
curl -fsSL https://tailscale.com/install.sh | sh
# 退出官方服务
sudo tailscale logout
# 连接到自建 Headscale(使用 --reset 清除旧设置)
sudo tailscale up \
--login-server https://xxx.yourdomain.com \
--authkey YOUR_PREAUTHKEY \
--reset
# 查看状态
tailscale status
# 网络诊断
tailscale netcheck
# 测试连接
tailscale ping <目标IP>

子网路由配置

子网路由允许一个节点作为网关,让其他 Tailscale 节点访问该节点所在的局域网。如果局域网入口设备上宣告局域网路由

1
2
3
4
5
6
7
8
9
# 宣告局域网路由
sudo tailscale up \
--login-server https://xxx.yourdomain.com \
--advertise-routes=192.168.xx.0/24
# 如果提示执行过了可以加个reset参数--reset
sudo tailscale up \
 --login-server https://xxx.yourdomain.com \
--advertise-routes=192.168.xx.0/24 \
--reset

Windows

1
2
3
4
5
6
7
8
1. 下载并安装 Tailscale: https://tailscale.com/download/windows
2. 以管理员身份打开 PowerShell:
3.退出官方服务
& "C:\Program Files\Tailscale\tailscale.exe" logout
# 连接到自建 Headscale
& "C:\Program Files\Tailscale\tailscale.exe" up `
--login-server https://xxx.yourdomain.com `
--authkey YOUR_PREAUTHKEY

移动设备

1
2
3
4
5
6
7
8
9
10
11
12
13
方法一:使用自定义服务器+手动注册
1. 打开 Tailscale App
2. 点击右上角设置图标
3. 滚动到底部,找到"Use custom coordination server"或 **"Alternate coordination server URL"**
4. 输入你的服务器地址:
https://xxx.yourdomain.com
5. 返回主界面,点击 **"Sign in"**
6. 会打开浏览器,显示类似以下内容:
Run the following command to register this node:
headscale nodes register --user USERNAME --key nodekey:xxxxxx
7. 在Headscale服务器上执行注册命令:
headscale nodes register --user 1 --key nodekey:xxxxxx
8. 注册成功后,手机 App 会自动连接
1
2
3
4
5
6
使用预授权密钥
部分版本的 iOS App 支持直接使用 authkey:
1. 首先在服务器创建预授权密钥:
headscale preauthkeys create -u 1 --expiration 720h --reusable
2. 在 App 设置中输入自定义服务器地址
3. 登录时如果有输入 authkey 的选项,直接粘贴密钥

4. 在客户端上接受路由

1
2
3
4
# 接受子网路由
Tailscale up \
--login-server https://xxx.yourdomain.com \
--accept-routes

多子网路由

如果需要宣告多个子网:

1
2
3
sudo tailscale up \
--login-server https://xxx.yourdomain.com \
--advertise-routes=192.168.xx.0/24,192.168.xx.0/24,10.0.0.0/8

常用命令

1
2
3
4
5
6
7
8
9
10
11
12
# 启动服务
sudo systemctl start headscale
# 停止服务
sudo systemctl stop headscale
# 重启服务
sudo systemctl restart headscale
# 查看状态
sudo systemctl status headscale
# 查看日志
sudo journalctl -u headscale -f
# 查看最近 100 行日志
sudo journalctl -u headscale -n 100

用户管理

1
2
3
4
5
6
7
8
# 创建用户
headscale users create <username>
# 列出用户
headscale users list
# 删除用户
headscale users destroy <username>
# 重命名用户
headscale users rename <old_name> <new_name>

节点管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 列出所有节点
headscale nodes list
# 删除节点(使用节点 ID)
headscale nodes delete -i <node_id>
# 注册节点(手动方式)
headscale nodes register --user <user_id> --key <node_key>
# 设置节点过期时间
headscale nodes expire -i <node_id>
# 重命名节点
headscale nodes rename -i <node_id> <new_name>
# 移动节点到其他用户
headscale nodes move -i <node_id> -u <user_id>
# 添加节点标签
headscale nodes tag -i <node_id> -t tag:server

预授权密钥管理

1
2
3
4
5
6
7
8
9
10
# 创建密钥(指定用户 ID)
headscale preauthkeys create -u <user_id> --expiration 24h
# 创建可重复使用的密钥
headscale preauthkeys create -u <user_id> --expiration 720h --reusable
# 创建临时节点密钥(节点离线后自动删除)
headscale preauthkeys create -u <user_id> --expiration 24h --ephemeral
# 列出密钥
headscale preauthkeys list -u <user_id>
# 使密钥过期
headscale preauthkeys expire -u <user_id> <preauthkey>

路由管理

1
2
3
4
5
6
# 列出所有路由
headscale nodes list-routes
# 批准路由
headscale nodes approve-routes -i <node_id> -r <route>
# 批准多个路由
headscale nodes approve-routes -i <node_id> -r 192.168.1.0/24,192.168.2.0/24