自从进入大学以来,作为一个工科学生,笔记本电脑一向是“告别 mac,平板本性能太拉”。平时背个游戏本怎么着也得是负重 5kg,所以日常的搭配都是 iPad mini + 键盘鼠标远程连到放在宿舍的游戏本。Tailscale 组网在校园网比较正常的时候延迟可以稳定在 3ms 左右,但是一旦出现网络波动或者在外访问,Tailscale 自带的 DERP Server 延迟又太高,于是就有了自建的想法。
Tailscale 的终极目标是让两台处于网络上的任何位置的机器建立点对点连接(直连),但现实世界是复杂的,大部份情况下机器都位于 NAT 和防火墙后面,这时候就需要通过打洞来实现直连,也就是 NAT 穿透。
NAT 按照 NAT 映射行为和有状态防火墙行为可以分为多种类型,但对于 NAT 穿透来说根本不需要关心这么多类型,只需要看 NAT 或者有状态防火墙是否会严格检查目标 Endpoint,根据这个因素,可以将 NAT 分为 Easy NAT 和 Hard NAT。
2.2.2.2:4242,其他客户端一律不允许。这种 NAT 更加严格,所以叫 Hard NAT。对于 Easy NAT,我们只需要提供一个第三方的服务,它能够告诉客户端“它看到的客户端的公网 ip:port 是什么”,然后将这个信息以某种方式告诉通信对端(peer),后者就知道该和哪个地址建连了!这种服务就叫 STUN (Session Traversal Utilities for NAT,NAT会话穿越应用程序)。它的工作流程如下图所示:
ip:port”。
对于 Hard NAT 来说,STUN 就不好使了,即使 STUN 拿到了客户端的公网 ip:port 告诉通信对端也于事无补,因为防火墙是和 STUN 通信才打开的缺口,这个缺口只允许 STUN 的入向包进入,其他通信对端知道了这个缺口也进不来。通常企业级 NAT 都属于 Hard NAT。
这种情况下打洞是不可能了,但也不能就此放弃,可以选择一种折衷的方式:创建一个中继服务器(relay server),客户端与中继服务器进行通信,中继服务器再将包中继(relay)给通信对端。
至于中继的性能,那就要看具体情况了:
事实上对于大部分网络而言,Tailscale 都可以通过各种黑科技打洞成功,只有极少数情况下才会选择中继,中继只是一种 fallback 机制。
DERP 即 Detoured Encrypted Routing Protocol,这是 Tailscale 自研的一个协议:

Tailscale 使用的算法很有趣,所有客户端之间的连接都是先选择 DERP 模式(中继模式),这意味着连接立即就能建立(优先级最低但 100% 能成功的模式),用户不用任何等待。然后开始并行地进行路径发现,通常几秒钟之后,我们就能发现一条更优路径,然后将现有连接透明升级(upgrade)过去,变成点对点连接(直连)。
因此,DERP 既是 Tailscale 在 NAT 穿透失败时的保底通信方式(此时的角色与 TURN 类似),也是在其他一些场景下帮助我们完成 NAT 穿透的旁路信道。 换句话说,它既是我们的保底方式,也是有更好的穿透链路时,帮助我们进行连接升级(upgrade to a peer-to-peer connection)的基础设施。
Tailscale 的私钥只会保存在当前节点,因此 DERP server 无法解密流量,它只能和互联网上的其他路由器一样,呆呆地将加密的流量从一个节点转发到另一个节点,只不过 DERP 使用了一个稍微高级一点的协议来防止滥用。
Tailscale 开源了 DERP 服务器的代码,如果你感兴趣,可以阅读 DERP 的源代码。
Tailscale 官方内置了很多 DERP 服务器,分步在全球各地,惟独不包含中国大陆,原因你懂得。这就导致了一旦流量通过 DERP 服务器进行中继,延时就会非常高。而且官方提供的 DERP 服务器是万人骑,存在安全隐患。
为了实现低延迟、高安全性,我们可以参考 Tailscale 官方文档自建私有的 DERP 服务器。
使用自有域名部署需要满足以下几个条件:
如果以上条件都俱备,就可以按照下面的步骤开始部署了。
由于众所周知的网络原因,在安装 golang 时需要使用镜像源,我这里使用的是阿里云的镜像。
在 golang 官网找到 golang 的最新版本并记录

随后登录云服务器,输入
$ wget https://mirrors.aliyun.com/golang/go1.xx.x.linux-amd64.tar.gz # x 换成刚刚记下的版本号
$ sudo rm -rf /usr/local/go && tar -C /usr/local -xzf go1.21.4.linux-amd64.tar.gz # 安装 golang
$ echo "export PATH=$PATH:/usr/local/go/bin" >> ~/.bashrc && source ~/.bashrc # 添加 golang 到环境变量
$ go env -w GO111MODULE=on
$ go env -w GOPROXY=https://goproxy.cn,direct # 设置 GOPROXY
在云服务器中输入
$ go install tailscale.com/cmd/derper@main # 安装官方 derper
$ mv $HOME/go/bin/derper /usr/bin # 移动至 /usr/bin,便于访问
$ sudo curl -fsSL https://tailscale.com/install.sh | sudo sh # (可选)安装 Tailscale,用于防止被白嫖 DERP Server
$ sudo tailscale up # 此处按提示登录即可
先创建 service 文件:
$ sudo touch /etc/systemd/system/tailscale-derper.service
然后在其中输入:
[Unit]
Description=Tailscale DERP Server
After=network.target
[Service]
ExecStart=derper --hostname=derp.yourdomain.com --verify-clients
# 将 derp.yourdomain.com 替换为你的域名,若不需要防止白嫖则不需要使用 --verify-clients 参数
Restart=always
User=root
[Install]
WantedBy=multi-user.target
接着,重新加载systemctl配置:
$ sudo systemctl daemon-reload
启用开机自启并启动 DERP Server:!别忘了打开服务器的安全组哦!
$ sudo systemctl enable tailscale-derper.service
$ sudo systemctl start tailscale-derper.service
若需要检查服务状态或查看日志,则请输入:
sudo systemctl status tailscale-derper.service # 查看服务状态
sudo journalctl -u tailscale-derper.service # 查看日志
在云服务器中输入 crontab -e (可能会需要选择编辑器)并在文件最后加入
0 0 * * * go install tailscale.com/cmd/derper@main && mv $HOME/go/bin/derper /usr/bin -f && systemctl restart tailscale-derper.service
保存退出即可
打开 Tailscale - Access Controls,在其中加入
{
// 前略
"derpMap": {
// 如果想要所有节点只使用自建中继的话,就启用这条配置
// "OmitDefaultRegions": true,
"Regions": {
"900": {
"RegionID": 900,
"RegionCode": "tsh",
"RegionName": "Shanghai",
"Nodes": [
{
"Name": "Shanghai",
"RegionID": 900,
"HostName": "derp.mydomain.com",
},
// 如果有多个区域、多个节点,或者使用了自定义端口,那么可以参考这部分
"901": {
"RegionID": 901,
"RegionCode": "Oracle-OSAKA",
"Nodes": [
{
"Name": "Oracle-OSAKA-1",
"RegionID": 901,
"HostName": "osaka1.derp.mydomain.com",
"DERPPort": 4443,
},
{
"Name": "Oracle-OSAKA-1",
"RegionID": 901,
"HostName": "osaka2.derp.mydomain.com",
"DERPPort": 4443,
},
]
],
},
},
},
}
自从进入大学以来,作为一个工科学生,笔记本电脑一向是“告别 mac,平板本性能太拉”。平时背个游戏本怎么着也得是负重 5kg,所以日常的搭配都是 iPad mini + 键盘鼠标远程连到放在宿舍的游戏本。Tailscale 组网在校园网比较正常的时候延迟可以稳定在 3ms 左右,但是一旦出现网络波动或者在外访问,Tailscale 自带的 DERP Server 延迟又太高,于是就有了自建的想法。
Tailscale 的终极目标是让两台处于网络上的任何位置的机器建立点对点连接(直连),但现实世界是复杂的,大部份情况下机器都位于 NAT 和防火墙后面,这时候就需要通过打洞来实现直连,也就是 NAT 穿透。
NAT 按照 NAT 映射行为和有状态防火墙行为可以分为多种类型,但对于 NAT 穿透来说根本不需要关心这么多类型,只需要看 NAT 或者有状态防火墙是否会严格检查目标 Endpoint,根据这个因素,可以将 NAT 分为 Easy NAT 和 Hard NAT。
2.2.2.2:4242,其他客户端一律不允许。这种 NAT 更加严格,所以叫 Hard NAT。对于 Easy NAT,我们只需要提供一个第三方的服务,它能够告诉客户端“它看到的客户端的公网 ip:port 是什么”,然后将这个信息以某种方式告诉通信对端(peer),后者就知道该和哪个地址建连了!这种服务就叫 STUN (Session Traversal Utilities for NAT,NAT会话穿越应用程序)。它的工作流程如下图所示:
ip:port”。
对于 Hard NAT 来说,STUN 就不好使了,即使 STUN 拿到了客户端的公网 ip:port 告诉通信对端也于事无补,因为防火墙是和 STUN 通信才打开的缺口,这个缺口只允许 STUN 的入向包进入,其他通信对端知道了这个缺口也进不来。通常企业级 NAT 都属于 Hard NAT。
这种情况下打洞是不可能了,但也不能就此放弃,可以选择一种折衷的方式:创建一个中继服务器(relay server),客户端与中继服务器进行通信,中继服务器再将包中继(relay)给通信对端。
至于中继的性能,那就要看具体情况了:
事实上对于大部分网络而言,Tailscale 都可以通过各种黑科技打洞成功,只有极少数情况下才会选择中继,中继只是一种 fallback 机制。
DERP 即 Detoured Encrypted Routing Protocol,这是 Tailscale 自研的一个协议:

Tailscale 使用的算法很有趣,所有客户端之间的连接都是先选择 DERP 模式(中继模式),这意味着连接立即就能建立(优先级最低但 100% 能成功的模式),用户不用任何等待。然后开始并行地进行路径发现,通常几秒钟之后,我们就能发现一条更优路径,然后将现有连接透明升级(upgrade)过去,变成点对点连接(直连)。
因此,DERP 既是 Tailscale 在 NAT 穿透失败时的保底通信方式(此时的角色与 TURN 类似),也是在其他一些场景下帮助我们完成 NAT 穿透的旁路信道。 换句话说,它既是我们的保底方式,也是有更好的穿透链路时,帮助我们进行连接升级(upgrade to a peer-to-peer connection)的基础设施。
Tailscale 的私钥只会保存在当前节点,因此 DERP server 无法解密流量,它只能和互联网上的其他路由器一样,呆呆地将加密的流量从一个节点转发到另一个节点,只不过 DERP 使用了一个稍微高级一点的协议来防止滥用。
Tailscale 开源了 DERP 服务器的代码,如果你感兴趣,可以阅读 DERP 的源代码。
Tailscale 官方内置了很多 DERP 服务器,分步在全球各地,惟独不包含中国大陆,原因你懂得。这就导致了一旦流量通过 DERP 服务器进行中继,延时就会非常高。而且官方提供的 DERP 服务器是万人骑,存在安全隐患。
为了实现低延迟、高安全性,我们可以参考 Tailscale 官方文档自建私有的 DERP 服务器。
使用自有域名部署需要满足以下几个条件:
如果以上条件都俱备,就可以按照下面的步骤开始部署了。
由于众所周知的网络原因,在安装 golang 时需要使用镜像源,我这里使用的是阿里云的镜像。
在 golang 官网找到 golang 的最新版本并记录

随后登录云服务器,输入
$ wget https://mirrors.aliyun.com/golang/go1.xx.x.linux-amd64.tar.gz # x 换成刚刚记下的版本号
$ sudo rm -rf /usr/local/go && tar -C /usr/local -xzf go1.21.4.linux-amd64.tar.gz # 安装 golang
$ echo "export PATH=$PATH:/usr/local/go/bin" >> ~/.bashrc && source ~/.bashrc # 添加 golang 到环境变量
$ go env -w GO111MODULE=on
$ go env -w GOPROXY=https://goproxy.cn,direct # 设置 GOPROXY
在云服务器中输入
$ go install tailscale.com/cmd/derper@main # 安装官方 derper
$ mv $HOME/go/bin/derper /usr/bin # 移动至 /usr/bin,便于访问
$ sudo curl -fsSL https://tailscale.com/install.sh | sudo sh # (可选)安装 Tailscale,用于防止被白嫖 DERP Server
$ sudo tailscale up # 此处按提示登录即可
先创建 service 文件:
$ sudo touch /etc/systemd/system/tailscale-derper.service
然后在其中输入:
[Unit]
Description=Tailscale DERP Server
After=network.target
[Service]
ExecStart=derper --hostname=derp.yourdomain.com --verify-clients
# 将 derp.yourdomain.com 替换为你的域名,若不需要防止白嫖则不需要使用 --verify-clients 参数
Restart=always
User=root
[Install]
WantedBy=multi-user.target
接着,重新加载systemctl配置:
$ sudo systemctl daemon-reload
启用开机自启并启动 DERP Server:!别忘了打开服务器的安全组哦!
$ sudo systemctl enable tailscale-derper.service
$ sudo systemctl start tailscale-derper.service
若需要检查服务状态或查看日志,则请输入:
sudo systemctl status tailscale-derper.service # 查看服务状态
sudo journalctl -u tailscale-derper.service # 查看日志
在云服务器中输入 crontab -e (可能会需要选择编辑器)并在文件最后加入
0 0 * * * go install tailscale.com/cmd/derper@main && mv $HOME/go/bin/derper /usr/bin -f && systemctl restart tailscale-derper.service
保存退出即可
打开 Tailscale - Access Controls,在其中加入
{
// 前略
"derpMap": {
// 如果想要所有节点只使用自建中继的话,就启用这条配置
// "OmitDefaultRegions": true,
"Regions": {
"900": {
"RegionID": 900,
"RegionCode": "tsh",
"RegionName": "Shanghai",
"Nodes": [
{
"Name": "Shanghai",
"RegionID": 900,
"HostName": "derp.mydomain.com",
},
// 如果有多个区域、多个节点,或者使用了自定义端口,那么可以参考这部分
"901": {
"RegionID": 901,
"RegionCode": "Oracle-OSAKA",
"Nodes": [
{
"Name": "Oracle-OSAKA-1",
"RegionID": 901,
"HostName": "osaka1.derp.mydomain.com",
"DERPPort": 4443,
},
{
"Name": "Oracle-OSAKA-1",
"RegionID": 901,
"HostName": "osaka2.derp.mydomain.com",
"DERPPort": 4443,
},
]
],
},
},
},
}