自建 Tailscale DERP 中继服务器

自建 Tailscale DERP 中继服务器

1.不使用Docker

准备工作

购置服务器、域名,以及做域名解析和 SSL 证书申请(最新版tailscle derp 只需要公网ip即可)

服务器选择阿里云,系统选择最流行的Ubuntu24.04,域名dnspod进行购买,并且实现域名解析和ssl证书申请

安装 Tailscale 客户端

首先在你的服务器上安装 tailscale 客户端。参考 tailscale 官网的命令。

安装成功后,先 不要 输入sudo tailscale up 启动tailscale,因为启动后 iptables 会添加规则导致阿里云workbench无法使用。

安装 Golang

如果你的服务器上有其他软件依赖旧版本的 go,将其升级为新版本可能会发生不可预料的后果,请务必仔细评估与衡量。

旧版本 golang 会极大概率导致 derp 安装失败。最新版golang可以编译

安装 derper 服务

建立目录:sudo mkdir -p /usr/local/gopath/bin

分别输入并回车以下两行代码,设置go代理+安装

1
2
go env -w GOPROXY=https://goproxy.cn,direct
go install tailscale.com/cmd/derper@main

用vim建立启动脚本:sudo vim /usr/local/gopath/bin/runderper

1
2
3
4
#!/bin/sh
cd /usr/local/gopath/bin
nohup ./derper -hostname <你的域名> -c=derper.conf -a :56473 -http-port -1 -certdir /usr/local/cert -certmode manual -verify-clients -stun > console.log 2>&1 &
echo $! > app.pid

参数 -verify-clients用来防止别人(知道你的域名后)白嫖你的中继节点,只认服务器上 tailscale 客户端登录的账号。如果你有给朋友白嫖的需求,可以把这个参数去掉。

然后输入下面脚本建立停止脚本:

1
sudo vim /usr/local/gopath/bin/stopderper.sh
1
2
3
#!/bin/sh
kill `cat app.pid`
rm -rf app.pid

然后输入以下代码赋权

1
2
chmod +x /usr/local/gopath/bin/runderper
chmod +x /usr/local/gopath/bin/stopderper.sh

然后建立服务。输入 sudo vim /etc/systemd/system/derper.service

1
2
3
4
5
6
7
8
9
10
Description=derper服务
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/gopath/bin/runderper
ExecStop=/usr/local/gopath/bin/stopderper.sh

[Install]
WantedBy=multi-user.target

然后去阿里云控制面板,放行derp端口(TCP 56473 和 UDP 3478):

CMD SSH登录

接下来要使用 cmd ssh 登录服务器而不能接着在阿里云workbench上操作,因为启动tailscale后workbench就用不了了。

在 cmd 中输入 ssh root@服务器IP 回车,输入密码即可登录

IP冲突解决

找到 Tailscale 注入的规则

1
2
sudo iptables -S | grep 100.64
-A ts-input -s 100.64.0.0/10 ! -i tailscale0 -j DROP

根源:RFC 6598 与阿里云 CGNAT 地址

什么是 CGNAT

100.64.0.0/10RFC 6598 中被保留为 运营商级 NAT (Carrier-Grade NAT) 地址段,用于 ISP 做 NAT 时避免与内网冲突。Tailscale 把它当成“只允许来自 Tailscale 接口的地址段”是符合规范的。

阿里云为何用 CGNAT 当内网 DNS

阿里云在 VPC 内把 100.100.2.136100.100.2.138 作为内网 DNS 服务地址,初衷是:

  • 地址在公网不可路由,避免外泄;
  • 与经典网络互通时不会冲突。

但这与 RFC 6598 的本意并不完全一致,于是与 Tailscale 的默认防火墙策略产生了冲突

五种可行方案与利弊对照

下面给出五种经过验证的方案,按“侵入性”从低到高排序。你可以根据业务场景挑一条最合适的。

方案 一句话描述 优点 缺点 适用场景
① 手动删除 DROP 规则 iptables -D ts-input ... 立即生效 每次重启 Tailscale 会恢复 临时调试
② 顶部插入白名单规则 iptables -I ts-input ... 允许 100.100.2.136 对业务无感 规则可能被 Tailscale 覆盖 单台机器、不频繁重启
③ 脚本轮询维护 cron 每分钟检查并修复 自动化 系统复杂度增加 多台机器、能写脚本
④ 改走公共 DNS /etc/resolv.conf 换成 8.8.8.8、1.1.1.1 根治冲突 无法解析阿里云内网域名,OSS、RDS 内网地址失效 不使用阿里云内网产品
⑤ 关闭 Tailscale 防火墙 tailscale up --netfilter-mode=off 不碰 iptables 失去子网路由、exit-node 等高级功能 仅做点对点互联

如何快速验证方案是否生效

  1. DNS 测试

    1
    dig @100.100.2.136 example.com +short

    出现 IP 即成功。

  2. 外网连通性

    1
    curl -I https://example.com
  3. Tailscale 功能

    1
    2
    tailscale ping some-node
    tailscale status
  4. 规则持久化
    重启机器或 systemctl restart tailscaled 后再次检查,确保规则仍在。

2.使用Docker

启动 Docker 镜像

使用 imgGitHub - yangchuansheng/ip_derper: 无需域名的 derper 大佬已经做好的 Docker image

Docker compose 文件编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
services:
derper:
image: ghcr.io/yangchuansheng/ip_derper:latest
container_name: derper
restart: always
ports:
- "12345:12345" # 这里的12345请改成你自己想要的10000以上的高位端口
- "3478:3478/udp" # 3478 为stun端口,如果不冲突请勿修改
volumes:
- /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock # 映射本地 tailscale 客户端验证连接,用来验证是否被偷
environment:
- DERP_ADDR=:12345 # 此处需要与上面的同步修改
- DERP_CERTS=/app/certs
- DERP_VERIFY_CLIENTS=true # 启动客户端验证,这是防偷的最重要的参数

新建一个文件,命名为:docker-compose.yml

在该文件夹内,docker compose up -d 即可启动该 docker

对于服务器在国内的情况,可以将 ghcr 的链接替换一下,比如南大的(替换第三行的 image)

1
2
3
services:
derper:
image: ghcr.nju.edu.cn/yangchuansheng/ip_derper:latest

修改 tailscale ACL 配置启用第三方 DERP

  1. 进入 ACL 编辑页面:Tailscale
  2. 加入新配置文件
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
"derpMap": {
"OmitDefaultRegions": false, // 可以设置为 true,这样不会下发官方的 derper 节点,测试或者实际使用都可以考虑打开
"Regions": {
"900": {
"RegionID": 900, // tailscale 900-999 是保留给自定义 derper 的
"RegionCode": "abc1",
"RegionName": "abcc1",// 这俩随便命名
"Nodes": [
{
"Name": "fff",
"RegionID": 900,
"IPv4": "1.1.1.1", // 你的VPS 公网IP地址
"DERPPort": 12345, //上面 12345 你自定义的端口
"InsecureForTests": true, // 因为是自签名证书,所以客户端不做校验
},
],
},
"901": {
"RegionID": 901, // 加入新 derp 的时候记得修改
"RegionCode": "abc2",
"RegionName": "abcc2",
"Nodes": [
{
"Name": "kkk",
"RegionID": 902,
"IPv4": "8.8.8.8", // 你的VPS 公网IP地址
"DERPPort": 4000, //上面 12345 你自定义的端口
"InsecureForTests": true, // 因为是自签名证书,所以客户端不做校验
},
],
},
},
},

找到一个在用 tailscale 的客户端,输入 tailscale netcheck

3.远程桌面

延迟:使用windows远程桌面时,如果两端环境支持,打洞直连效果最好,使用自建derp中继服务器反而有操作延迟不稳定,有延迟的环境下windows远程桌面效果没有其他远程软件好。

中继和直连延迟差不多,但是直连效果更好,说明和网络稳定性有很大关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
中继服务器
tailscale ping yoga
pong from yoga (100.69.xxxx) via DERP(abc1) in 62ms
pong from yoga (100.69.xxxx) via DERP(abc1) in 62ms
pong from yoga (100.69.xxxx) via DERP(abc1) in 65ms
pong from yoga (100.69.xxxx) via DERP(abc1) in 63ms
pong from yoga (100.69.xxxx) via DERP(abc1) in 59ms
pong from yoga (100.69.xxxx) via DERP(abc1) in 130ms

直接连接
tailscale status
100.77.xxxx yangless windows -
100.115.xxxx izbp11pb148b02kc6runz linux -
100.69.xxxx yoga windows active; direct [2409:8924:4841:6a91:58b1:e07f:692]:41641, tx 810398896 rx 14154500

tailscale ping yoga
pong from yoga (100.69.xxxx) via [2409:8924:4841:6a91:58b1:e07f:692b:b385]:41641 in 87ms

画质:windows远程桌面画质屏幕适配吊打其他远程连接。

4.总结

总的来说,从外网访问内网的服务大致有三种方案:

  • 获取公网 IP + DDNS 解析
    • 但这种方案的可行性正逐渐降低。主要是因为 IPv4 地址枯竭,国内宽带运营商不再为家庭用户分配公网 IP,导致许多家用宽带的 IP 地址是内网地址,无法实现动态地址解析,从而使得外部访问变得越发艰难。
  • 隧道穿透
    • 隧道穿透方案,即通过一台具有公网 IP 的服务器作为跳板,暴露内网服务到公网中实现访问,这个方案的优点在于网络访问稳定,带宽速度取决于公网 IP 服务器的带宽。缺点则是配置较为复杂,需要在服务器上安装相关的服务,还需要在内网服务端写配置项。同时隧道穿透对一些网络协议支持较差,尤其是 HTTPS 协议配置极其复杂繁琐。
  • 构建虚拟局域网
    • 最后一种方案则是构建虚拟局域网,简单来说就是将分处不同网络下的设备跨网络组建在一个虚拟局域网中,从而实现基于内网 IP 的设备间互访,这种方案最大的好处在于不受限于网络协议,并且部署安装客户端较为简单并支持私有化部署,缺点则是提供该服务的厂商多来自国外,相对应的是服务在国内网络环境下稳定下较差。

运营商UPD流量封的越来越狠,IPv4地址大多为假的公网IP,IPv6封端口,最终还是得走自建服务器配合隧道穿透。