DERP是Tailscale的中继服务器,负责交换客户端之间的连接信息和在无法直接连接时中继流量。其本身只负责协调和转发,不会解密协议承载的流量,因此使用官方的DERP服务器同样是安全的。但是,由于国内较为特殊的网络环境,官方的节点连接到中国大陆时延迟和稳定性均欠佳,而仅凭Headscale内置的单一节点有时是不满足需求的。

Tailscale Derper同样是一个官方开源软件的一部分,但是网上的教程多使用docker搭建,不是很适合在刁钻的环境下使用🥶,在此把博主的一些探索做一个简单的记录。
注意:这是一篇纯文字的流程记录,未添加详细的配图和说明,需要一定的基础进行实践。
一、编译DERP
DERP中继的docker搭建在互联网上已有大量的教程,并且操作非常简单。但是,docker下的derper镜像中不包含tailscale客户端,如果需要对接入设备进行认证则需要将宿主中的tailscale映射至容器中,反而增加了一些麻烦。实际上,derper程序本身也是一个基于go构建的binary单文件,只是官方未提供预编译包,需要我们自己使用go环境进行编译。
# 从https://go.dev/dl/下载对应架构的go环境
wget https://go.dev/dl/go1.23.3.linux-amd64.tar.gz
# 解压
tar -zxvf go1.23.3.linux-amd64.tar.gz
# 移动到go环境目录
cd ./go
# 从源码构建derper二进制文件
./bin/go install tailscale.com/cmd/derper@main
# 在go环境的bin路径下可以找到编译好的derper二进制文件,并进行测试
./bin/derper -version
# 提取文件(可选至指定目录)
cp ./bin/derper /usr/bin/derper
# 添加可执行权限
chmod +x /usr/bin/derper
# 如不需要再次编译,可直接删除go环境
rm -rf /root/go在以上的方法中,我们只是临时使用go环境,无需将其添加至环境变量,在使用后也可直接删除。
在此获得derper二进制文件可以直接复制到任意同架构和系统版本的系统中运行,也就是你可以在任意同类的环境编译好后分再发到其他的服务器。
二、安装Tailscale
为了防止derper服务器被其他用户使用,我们需要在服务器上运行tailscale客户端接入自己的tailnet网络,来为开启了-verify-clients的derp提供认证信息。
# 最简单地加入一个Tailnet
tailscale up --login-server=https://my.headscale --netfilter-mode=off --accept-dns=false安装过程不再赘述,但需要特别注意在阿里云的ECS上,MagicDNS使用的100.100.100.100地址可能与阿里云内网DNS冲突,会导致服务器联网出现问题。可以考虑切换服务器DNS至PublicDNS或修改Tailscale源码解除对该地址的占用。
三、运行DERPer
准备好derper二进制包后,接下来可以进行一些运行的测试工作。在此假设所有文件都位于/home/derp目录下,可以按照以下的方式选择一个合适你使用场景的命令。
# 独占443端口HTTPS访问,HTTP 80端口跳转HTTPS,并验证连接客户端
# 证书通过Let's Encrypt HTTP获取,必须具有独占的80和443端口
/home/derp/derper -c /home/derp/derper.conf -a :443 -http-port 80 -stun-port 3478 -hostname my.derp -certmode letsencrypt -verify-clients
# 使用非标准端口HTTPS访问,手动更新证书,并验证连接客户端
# 证书手动放置于/home/derp中,文件名必须为主机名,如my.derp.crt和my.derp.key
/home/derp/derper -c /home/derp/derper.conf -a :8443 -http-port 8080 -stun-port 3478 -hostname my.derp -certmode manual -certdir /home/derp -verify-clients
# 监听本地HTTP端口,通过NGINX反代访问
# 证书配置在NGINX上,可与其他网站共存
/home/derp/derper -c /home/derp/derper.conf -a :8080 -stun-port 3478 --verify-clients搭配NGINX转发可以更好的隐藏服务特征,因此博主均使用第三种配置方式作为演示。对于使用systemd的系统(如debian、ubuntu等),将其修改好写入对应位置后,依次进行以下配置实现授权、启动和开机自启:
文件路径:
/etc/systemd/system/derper.service
管理进程:service derper start|stop|status
开机自启:rc-upadte add derper
derper.service
[Unit]
Description=Derper Service
After=network.target
[Service]
Type=simple
User=root
Restart=on-failure
RestartSec=5s
# 替换为上述测试好合适的启动命令
WorkingDirectory=/home/derp
ExecStart=/home/derp/derper -c /home/derp/derper.conf -a :8080 -stun-port 3478 -verify-clients
[Install]
WantedBy=multi-user.target对于像Alpine一样使用init.d和rc-local的系统,将其修改好写入对应位置后,依次进行以下配置实现授权、启动和开机自启:
文件路径:
/etc/init.d/derper
文件提权:chmod +x /etc/init.d/derper
管理进程:/etc/init.d/derper start|stop|status
开机自启:rc-upadte add derper
service
#!/sbin/openrc-run
supervisor=supervise-daemon
name="derper"
# 替换为上述测试好合适的启动命令
command="/home/derp/derper"
command_args="-c /home/derp/derper.conf -a :8080 -stun-port 3478 -verify-clients"
pidfile="/run/derper.pid"
respawn_delay=5
respawn_max=0
depend() {
need net
after firewall
use logger
}NGINX在vhsot和证书配置好后,只需要为/derp路径添加反向代理配置即可,无需对全局进行反向代理(也就是可以做到依附在某个网站上)。如果在headscale的derp.yaml中设定了IP地址则该域名可以不进行DNS解析,具有非常好的隐蔽性。配置好后,直接访问对应的路径会有DERP的提示信息,如果确有需要还可以通过sub_filter去掉。
proxy.conf
location /derp {
proxy_pass http://127.0.0.1:8080;
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 REMOTE-HOST $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_http_version 1.1;
proxy_buffering off;
}四、添加DERP
Headscale的搭建可以参考博主之前的文章(点击前往),首先需要在headscale的config.yaml中derp字段下取消注释paths:并设置好其下的derp.yaml路径,实现从该文件载入DERP列表。官方有提供derp.yaml的示例(点击前往),按照derper的实际信息进行填写。配置好后,重启headscale服务即可。
derp.yaml
regions:
# 自建DERPER的RegionID设置区间为900-999,不会与官方节点冲突
900:
regionid: 900
regioncode: Shanghai
regionname: Shanghai Mobile
nodes:
- name: 900a
regionid: 900
# 主机名,可搭配NGINX反向代理使用
hostname: derp1.headscale.web
# IP地址,注意IPv6需添加半角引号,没有时注释掉
# 使用DDNS的DERP时,请将ipv4和ipv6都注释掉
ipv4: 123.123.123.123
ipv6: "2001:da8::1"
# STUN端口,设置为-1即不开启STUN服务
stunport: 3478
# 设置为true仅作为STUN打洞使用
stunonly: false
# 通信端口,必须为通过HTTPS访问的端口
derpport: 443
# 第一个地域配置完成后,可添加其他地域,同时一个地域也可有多台不同的节点YAML
由于境内的云服务器多不具备IPv6支持,如果你不想在Headscale引入官方节点以支持IPv6打洞的话,你可以在自己的节点中引入一些支持IPv6的公开STUN节点(比如Cloudflare和Google等)。这样也有一个小问题就是IPv6延迟偏高,会降低IPv6连接优先级,可以尝试寻找一些低延迟的STUN服务器来解决。
regions:
910:
regionid: 910
regioncode: Hubei
regionname: Hubei Telecom
nodes:
# 自己纯IPv4的DERP节点
- name: 910a
regionid: 910
hostname: hb.lty.fun
ipv4: 59.153.1.1
stunport: 3478
stunonly: false
derpport: 443
# 添加公开的STUN用于IPv6打洞
- name: 910x
regionid: 910
hostname: stun.cloudflare.com
ipv6: "2a06:98c1:3200::"
stunport: 3478
stunonly: true
- name: 910y
regionid: 910
hostname: stun.l.google.com
ipv6: "2001:4860:4864:5:8000::1"
stunport: 19302
stunonly: true五、结语
添加结束后,可在客户端使用tailscale netcheck检查是否添加成功,并获得延迟信息。由于Tailscale客户端以延迟排序选择DERP节点,可能需要暂时移除部分其他DERP节点以对其进行单独测试。当DERP节点不可用时Tailscale客户端会有很明显的报错出现,tailscale status中的relay也会降级至其他节点。
评论区