1. 缘由 #
最近想拉通家里网络(组网),先是用的wg,因为我自己用的ios surge 只支持wg(不然我应该会直接上Tailscale),然后就先拉通了wg回家,但是吧,感觉wg好麻烦,每次都得进wg服务端生成个密钥才能授权认证等,相对繁琐。因此有了本次折腾。
2. 什么是Tailscale #
Tailscale 是一种基于 WireGuard 的虚拟组网工具,WG的势头目前确实越来越好。那Tailscale相比WG有什么优势呢?
- 装个客户端就能用了
- 无需配置防火墙
- 没有额外的配置
- 高安全性/私密性
- 自动密钥轮换
- 点对点连接
- 支持用户审查端到端的访问记录
- 在原有的 ICE、STUN 等 UDP 协议外,实现了 DERP TCP 协议来实现 NAT 穿透
但是相比于WG还是有一定缺点的,Tailscale 相比于内核态 WireGuard 性能会有所损失。
其次的话就是 Tailscale 并不是完全免费
对于免费账户,他携带100个免费设备的资格 如果个人使用 完全足够咯
(这里就不探讨开源不开源了问题啦 个人是觉得 都ok 只不过 Tailscale的控制服务没开源罢了
当然如果不想经过Tailscale这层审计的话 自建Headscale是一个不错的选择 而我也选择这个方式
3. 什么是Headscale #
Headscale 是一个由 Juan Font 开发的开源项目,它使用 Go 语言编写并在 BSD 许可下发布。它旨在实现 Tailscale 控制服务器的所有主要功能,并可以在企业内部进行部署。以下是一些关于 Headscale 的关键点:
- 功能实现:Headscale 实现了 Tailscale 控制服务器的主要功能,允许用户创建和管理虚拟网络,使设备之间能够安全地通信。
- 内部部署:与 Tailscale 不同,Headscale 可以在企业内部部署,不需要依赖外部的控制服务器。这意味着企业可以完全控制自己的网络基础设施。
- 无限设备支持:Headscale 不限制设备的数量,可以根据需要扩展网络,这对大型组织特别有利。
- 网络流量控制:所有的网络流量都由用户自己控制,不经过第三方服务器,这增强了隐私性和安全性。
- 开源和许可:Headscale 是开源的,在 BSD 许可下发布,这意味着任何人都可以自由地使用、修改和分发该软件。
- 使用场景:Headscale 适用于需要高度控制和安全性的环境,例如企业内部网络、私有云基础设施或任何不希望将网络流量通过第三方的情景。
通过这些特性,Headscale 提供了一个强大的、灵活的解决方案,使用户能够享受 Tailscale 的便利,同时保持对其网络的完全控制。
4. Headscale部署 #
网上很多不同方式的部署教程,这边只记录自己总结以及在自己的Debian服务器的折腾记录。
官网提供了两种不同的文档,一种是目前沿用的新版本的采用 .dev 的方式一键安装的形式,不过此版本有一定的坑,具体也是在后续启动headscale的时候没法启动(针对我自己的环境而言,因此我使用了二进制的安装方式)
这边罗列几篇部署参考的教程文档:
而我也是根据这几篇教程慢慢踩坑过来的。
截止到 2024/8/7 官网releases版本是 0.22.3 ,因此接下来都是以该版本进行
首先下载二进制文件
# 下载需要代理 自行解决
wget -O /usr/local/bin/headscale https://github.com/juanfont/headscale/releases/download/v0.22.3/headscale_0.22.3_linux_amd64给予二进制文件可执行权限
# 简单声明下为什么要a all_user 只是偷个懒 因为启动的时候不以root权限执行
chmod a+x /usr/local/bin/headscale创建需要用到的文件夹
# 用来存放headscale的配置文件,以及后续自建derp也放这里(derp没说必须放这里 只是放一起好找)
mkdir /etc/headscale/
# 用于存放 Headscale 服务在运行时需要的临时文件,如 UNIX 域套接字文件 headscale.sock。
mkdir -p /var/run/headscale/下载二进制同版本的示例配置文件
wget -O /etc/headscale/config.yaml https://raw.githubusercontent.com/juanfont/headscale/v0.22.3/config-example.yaml创建 SystemD service 配置文件(简单理解就是后台运行的配置文件)
cat > /etc/systemd/system/headscale.service << EOF
[Unit]
Description=headscale controller
After=syslog.target
After=network.target
[Service]
Type=simple
User=headscale
Group=headscale
ExecStart=/usr/local/bin/headscale serve
Restart=always
RestartSec=5
# Optional security enhancements
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
WorkingDirectory=/var/lib/headscale
ReadWritePaths=/var/lib/headscale /var/run/headscale
AmbientCapabilities=CAP_NET_BIND_SERVICE
RuntimeDirectory=headscale
[Install]
WantedBy=multi-user.target
EOFHeadscale本身的作用实际就是取代Tailscale的控制中心的作用,因此不需要root权限
这边有个坑,我直接用root用户启动的时候各种报错,哪怕修改了 headscale.service 中的 User 也是报错,因此我不建议使用root用户,而是采用专门建立的 headscale 用户来进行下一步操作。
创建 headscale 用户
# 创建一个系统用户 "headscale"
useradd \
--create-home \ # 如果主目录不存在,则创建主目录
--home-dir /var/lib/headscale/ \ # 指定用户的主目录为 /var/lib/headscale/
--system \ # 创建一个系统账户
--user-group \ # 为用户创建一个同名的用户组
--shell /usr/sbin/nologin \ # 禁止用户登录
headscale创建空的 SQLite 数据库文件和 derp配置文件,并给予 headscale 用户有这些文件夹/文件的权限
touch /var/lib/headscale/db.sqlite /etc/headscale/derp.yaml
chown -R headscale:headscale /var/run/headscale/ /var/lib/headscale
chmod a+r /etc/headscale/config.yaml /etc/headscale/derp.yaml修改headscale的配置文件 /etc/headscale/config.yaml 这边只罗列需要修改的部分
server_url: http://<公网ip 或者ddns的域名>:8080
# 0.0.0.0才能监听到 当然可以用公网ip 或者 网卡的ip
listen_addr: 0.0.0.0:8080
# ip段范围(我用不到v6 所以注释)
ip_prefixes:
# - fd7a:115c:a1e0::/48
- 100.64.0.0/10
dns_config:
# 改为 false 不覆盖本地 DNS
override_local_dns: false
# 关闭 magic_dns
magic_dns: false
# 设置为你自己的标识,否则后续 tailscale 端连接上显示是 [email protected]
# 比如我的域名是 ddns.cn 那客户端显示就的就是 [email protected] 这里可改可不改 我是顺手改了
base_domain: ddns.cn
# 随机端口要打开, tailscale 客户端会使用41641 端口建立 wireguard 链接,这个端口会被中间网络设备阻止
randomize_client_port: true启动 headscale daemon 进程
# 测试文件 看看输出是否正常
headscale configtest
# 测试启动一次
headscale serve
# 这边需要注意 ctrl + c 的时候 请耐心等待自己结束进程 不然会出现很奇怪的问题 将会导致后续 systemd启动失败
# 配置文件没问题就 ctrl + c 取消掉使用 systemd 启动(后台静默运行)
systemctl daemon-reload
systemctl enable --now headscale
# 查看运行状态是否正常启动
systemctl status headscale5. 客户端接入 #
5.1 创建用户 #
# default 自己取
headscale user create default5.2 生成 pre-authkey 的 key #
# 生成一个过期时间 365d 且可以重复使用的 authkey (因为我是自用 所以创建天数多点 方便) 不然正常24h就很多了 安全
headscale preauthkeys --user default create --reusable --expiration 365d
# 查看下创建的authkey
headscale preauthkeys --user default list
ID | Key | Reusable | Ephemeral | Used | Expiration | Created | Tags
1 | 4fe241dxxxxxxxxx | true | false | true | 2025-08-06 06:30:50 | 2024-08-06 06:30:50 |5.3 Windows接入 #
下面是 tailscale up 时候一些常用通用选项:
--login-server: 指定使用的中央服务器地址(必填)--advertise-routes: 向中央服务器报告当前客户端处于哪个内网网段下, 便于中央服务器让同内网设备直接内网直连(可选的)或者将其他设备指定流量路由到当前内网(可选),多条路由英文逗号隔开--accept-routes: 是否接受中央服务器下发的用于路由到其他客户端内网的路由规则(可选)--accept-dns: 是否使用中央服务器下发的 DNS 相关配置(可选, 推荐关闭)--hostname: 设置 machine name,否则默认会以 hostname 注册上去,特别安卓的 hostname 无法修改
tailscale cli 官方文档 https://tailscale.com/kb/1080/cli,也可以自己 tailscale --help 看命令帮助。
执行 tailscale up 需要 在 Tailscale 的安装目录 比如 D:\software\Tailscale 进行cmd
贴一个自用的命令
tailscale up --login-server <server_url> --hostname <自己取 需要英文> --accept-routes=true --accept-dns=false --authkey <就是刚刚生成的 authkey>还有一些简单的命令 这些命令都需要在 安装目录下的cmd 进行 使用 不然就得加个 path 环境变量
# 这个可以看derp服务 用的哪条 哪些
tailscale netcheck
# 这个可以看状态 可以看看是否打洞成功还是走的derp服务等
tailscale status
# 用来ping测试
tailscale ping <headscale 分配的ip>至此 headscale 部署就完成了 并且也能够接入不同的客户端了 但是但是 有个很严重的问题 由于 Tailscale和Headscale在国内都没有derp服务,而且说句实话,用人家的也觉得不是很安全,且使用的时候很卡ping测试都是上1000ms的 因此,我们需要自建derp中继服务
6. Derp部署 #
6.1 使用域名容器(推荐) #
这种方案需要满足以下几个条件:
- 要有自己的域名,并且申请了 SSL 证书
- 需要准备一台或多台云主机
- 如果服务器在国内,域名需要备案
如果以上条件都俱备,就可以按照下面的步骤开始部署了。
使用 yangchuansheng 构建的docker进行部署
docker run --restart always \
--name derper -p 12345:12345 -p 3478:3478/udp \
-v /root/.acme.sh/xxxx/:/app/certs \ # 这边并不需要用acme 自己申请证书 放到目录用来映射就行了 比如 /root/cert/ddns.cn/
-e DERP_CERT_MODE=manual \
-e DERP_ADDR=:12345 \
-e DERP_DOMAIN=xxxx \
-d ghcr.io/yangchuansheng/derper:latest注意点:
- 默认情况下也会开启 STUN 服务,UDP 端口是
3478; - 防火墙需要放行端口 12345 和 3478;
- 准备好 SSL 证书;
- 域名部分我打了码,请换成你自己的域名。就是把 xxxx 替换成形如 ddns.cn
证书的话命名有严格要求的:
假设你的域名是 ddns.cn,那么证书的名称必须是 ddns.cn.crt,一个字符都不能错!同理,私钥名称必须是 ddns.cn.key,一个字符都不能错!
查看docker日志
docker logs -f derper
2024/08/06 12:50:07 no config path specified; using /var/lib/derper/derper.key
2024/08/06 12:50:07 derper: serving on :12345 with TLS
2024/08/06 12:50:07 running STUN server on [::]:3478部署好 derper 之后,就可以修改 Headscale 的配置来使用自定义的 DERP 服务器了。Headscale 可以通过两种形式的配置来使用自定义 DERP:
- 一种是在线 URL,格式是
JSON,与 Tailscale 官方控制服务器使用的格式和语法相同。 - 另一种是本地文件,格式是
YAML。
# /etc/headscale/derp.yaml
regions:
901:
regionid: 901
regioncode: home
regionname: Home XX
nodes:
- name: 901a
regionid: 901
hostname: <就是上面的域名>
ipv4: <公网ip 如果你跟我一样是ddns 那就写ddns的域名就行>
stunport: 3478
stunonly: false
derpport: 12345配置说明:
regions是 YAML 中的对象,下面的每一个对象表示一个可用区,每个可用区里面可设置多个 DERP 节点,即nodes。- 每个可用区的
regionid不能重复。 - 每个
node的name不能重复。 regionname一般用来描述可用区,regioncode一般设置成可用区的缩写。ipv4字段不是必须的,如果你的域名可以通过公网解析到你的 DERP 服务器地址,这里可以不填。如果你使用了一个二级域名,而这个域名你并没有在公共 DNS server 中添加相关的解析记录,那么这里就需要指定 IP(前提是你的证书包含了这个二级域名,这个很好支持,搞个泛域名证书就行了)。stunonly: false表示除了使用 STUN 服务,还可以使用 DERP 服务。
接下来还需要修改 Headscale 的配置文件,引用上面的自定义 DERP 配置文件。需要修改的配置项如下(只粘贴了需要修改的地方!!!):
# /etc/headscale/config.yaml
derp:
server:
# 不启用官网自带的derp
enabled: false
urls:
# - https://controlplane.tailscale.com/derpmap/defaul
paths:
- /etc/headscale/derp.yaml
# paths: []修改完这两份配置后 需要重启headscale服务 (一般重启比较慢 慢慢等就行 )
systemctl restart headscale之后在客户端执行 tailscale netcheck 就可以看到自己目前使用的DERP服务了
tailscale netcheck
Report:
* UDP: true
* IPv4: yes, xxxxx:11874
* IPv6: no, but OS has support
* MappingVariesByDestIP:
* PortMapping:
* Nearest DERP: Home xxx
* DERP latency:
- home: 31.8ms (Home xxx)输出类似以上。
这个命令实际上只检测了 3478/udp 的端口, 就算 netcheck 显示能连,也不一定代表 12345 端口可以转发流量。最简单的办法是直接打开 DERP 服务器的 URL:https://xxxx:12345,如果看到如下页面,且地址栏的 SSL 证书标签显示正常可用,那才是真没问题了。

6.2 使用纯IP容器 #
其实大部分人都有域名,但是呢都没备案,这点就很头疼了,只能用IP(针对国内服务器)。
但是但是但是,使用纯IP容器部署的话,有一个天坑,就是derper容器和headscale服务,不能处于同一台服务器中,用了一个下午才踩完的坑= = 这点一定要切记!!!!!! 如果你只有一台公网服务器,那么已经可以不用往下看了!!!!!!
使用 yangchuansheng 构建的纯IP docker进行部署
docker run --restart always --name derper -d -p 59443:443 -p 3478:3478 -p 3478:3478/udp ghcr.io/yangchuansheng/ip_derper记得放行 59443 和 3478 这两个端口
这里贴一个作者的docker日志(懒得再新建一个容器了)
docker logs -f derper
Generating a RSA private key
.......................................+++++
..............+++++
writing new private key to '/app/certs//127.0.0.1.key'
-----
2022/03/26 14:30:31 no config path specified; using /var/lib/derper/derper.key
2022/03/26 14:30:31 derper: serving on :443 with TLS
2022/03/26 14:30:31 running STUN server on [::]:3478接下来就是配置Derp的配置文件咯。
Headscale 的本地 YAML 文件目前还不支持这个配置项,所以没办法,目前只能使用在线 URL 了。(这点我没有验证,沿用作者的说法,不想在折腾这个纯IP了= =)
{
"Regions": {
"901": {
"RegionID": 901,
"RegionCode": "ali-sh",
"RegionName": "Aliyun Shanghai",
"Nodes": [
{
"Name": "901a",
"RegionID": 901,
"DERPPort": 443,
"HostName": "xxxx",
"IPv4": "xxxx",
"InsecureForTests": true
}
]
}
}
}配置说明:
HostName直接填 derper 的公网 IP,即和IPv4的值相同。InsecureForTests一定要设置为 true,以跳过域名验证。
需要把这个 JSON 文件变成 Headscale 服务器可以访问的 URL,比如在 Headscale 主机上搭个 Nginx,或者上传到对象存储(比如阿里云 OSS)。
一样的修改Headscale 的配置文件
# /etc/headscale/config.yaml
derp:
server:
# 不启用官网自带的derp
enabled: false
urls:
- https://xxxxx/derp.json
paths:
# - /etc/headscale/derp.yaml
# paths: []修改完这两份配置后 需要重启headscale服务 (一般重启比较慢 慢慢等就行 )
systemctl restart headscale之后在客户端执行 tailscale netcheck 就可以看到自己目前使用的DERP服务了
tailscale netcheck
Report:
* UDP: true
* IPv4: yes, xxxxx:11874
* IPv6: no, but OS has support
* MappingVariesByDestIP:
* PortMapping:
* Nearest DERP: Home xxx
* DERP latency:
- home: 31.8ms (Home xxx)输出类似以上。
这个命令实际上只检测了 3478/udp 的端口, 就算 netcheck 显示能连,也不一定代表 12345 端口可以转发流量。最简单的办法是直接打开 DERP 服务器的 URL:https://xxxx:12345,如果看到如下页面,且地址栏的 SSL 证书标签显示正常可用,那才是真没问题了。

7. 踩坑总结 #
- 建议不要用root 直接运行 headscale 否则会有很多奇奇怪怪的问题 用一上午的踩坑经验出来的
- 使用纯IP 一定要把derper建在非headscale服务所在的服务,否则大大的问题 用一下午的踩坑经验出来的
至此,教程结束,希望能够在本文的经验下,能够帮助到大家!