这是一篇踩坑记录,关于tailscale derper的。

懒狗快捷指南:

引言(和主题关系不大,不想看直接跳过):

众所周知,中国大陆的网络环境,ISP几乎不提供公网ipv4地址,而云服务器的流量价格是海外同类产品的数倍乃至十数倍:以阿里云为例,流量价格为500CNY/TB(ECS共享流量包),而AWS的价格则是5USD/TB(lightsail实例),虽然大陆也有一些“大带宽”云服务器,例如雨云和物语云,但SLA保证远低于头部厂商。

(你也不想在需要编译软件时,打开vscode remote-ssh时,提示个连接超时吧?)

选择 tailscale 原因:

废话不多说,有以下几点:

  • 使用P2P打洞,流量不流经中转服务器节点

  • 相较于同样P2P的zerotier,部署简便开箱即用

  • 跨平台(Windows、Linux、iOS、Android、MacOS)乃至对集群环境(Kubernetes、AWS等)兼容性好

  • 打洞失败有官方中继可用,无需额外支出

但是随着设备越来越多、流量越来越大,官方中继已经难以满足使用需求,具体问题有延迟过高(~300ms)、带宽较小(~1Mbps)。经查询后了解到具有自建中转的做法,可以极大的提升中国大陆内和跨境数据传输的体验。

软件架构:

tailscale分为控制平面和数据平面,是“半个零信任”的架构。控制平面负责身份认证,数据平面负责打洞和中继。这两者都有官方开源的自建方案:Headscale和Derper。

Headscale私有化了控制平面,在具有较高的安全性需求的环境可以考虑使用,而Derper则是私有化的数据平面节点,具体节点则由tailscale客户端选择。由于我是个人使用,因此只做了derper的自建。

部署踩坑:

derper是使用golang(go语言)编写的。为了操作简便,我使用docker部署。

  • The DERP protocol does a protocol switch inside TLS from HTTP to a custom bidirectional binary protocol. It is thus incompatible with many HTTP proxies. Do not put derper behind another HTTP proxy. *tailscale/cmd/derper

这是derper的第一个坑点:

你会在网上找到很多教程,其中写着通过各种代理:Nginx、Caddy……
注意:Derper是Tailscale自研的协议,虽然一样使用HTTP/TLS,端口也是80/443,但通过WEB代理会拒绝连接。

然后就到了第二个坑点:

Derper有官方的域名部署,还有通过ip derper部署的方式,即使用 "InsecureForTests": true 这一测试环境使用的ACL配置项,跳过TLS验证和域名验证。

例如这个项目:yangchuansheng/ip_derper ,是ip derper常用的docker项目。我尝试使用非标端口进行部署,一开始测试通过,但在使用大约2-3小时后,自建derper在客户端使用tailscale netcheck命令测试通过,但无论如何都无法实现连接,已经建立的连接也被强制中断,包括打洞和中继。报错在这里我没有保存,我个人也不推荐使用ip derper搭建

我在这里卡了两天时间,查了几十篇教程和文档,你要是愿意搞不舍得买个域名的钱请自行折腾。

另一种则是tailscale官方的使用域名搭建。你可以选择开放80&443端口给它,内置的acme会获取letsencrypt证书。但由于我的80/443被其余web服务占用,我使用了manual方式。

接下来来到了第三个坑点:

derper程序不会识别 cert.pemcert.key 这样的证书文件,所以直接绑定到通配符证书目录不可用,而是需要重命名为example.com.crtexample.com.key,再进行使用。以及如果docker或容器内使用的不是root用户,还需要重新配置证书权限

最后一个坑点是我踩了最长时间才爬出来的:

我选择的是 fredliang44/derper-docker 这个项目,按照README使用 DERP_ADDR=':8443' 配置项进行自定义端口,但启动时会出现神秘报错:

Attaching to derper
derper  | 2024/12/21 04:41:12 no config path specified; using /var/lib/derper/derper.key
derper  | 2024/12/21 04:41:12 derper: serving on ":8443" with TLS
derper  | 2024/12/21 04:41:12 derper: listen tcp: lookup tcp/8443": unknown port
derper exited with code 0

不管使用default bridge、custom bridge 还是 host 网络,均无法访问。macvlan模式由于tailscale官方文档要求禁止部署在防火墙后,而我又没有自己的公网ip池,因此无法使用。

一开始怀疑是镜像问题,后面尝试重新构建docker image,甚至连带golang依赖也重新构建了,均无法运行。换了其余好几个版本的镜像也是一样的报错,才意识到可能是第三方教程和官方README的问题:他们不约而同的选择了 ENV 定义非标端口,而这一配置项会被翻译成官方二进制文件的 -a 参数。而在容器内测试使用这一参数时,无法识别这一命令……

所以,即使想使用非标端口,也请删除这一ENV,然后使用 -p <custom_port>:443 这样的形式,在bridge network实现部署

部署流程:

好吧,今天又说了太多废话。下面直接放配置文件:

services:
  derper:
    container_name: derper
    environment:
#      - DERP_ADDR=":8443" # 这里不要加,会启动失败
      - DERP_DOMAIN=derp.sunnypai.top
      - DERP_CERT_MODE=manual
      - DERP_CERT_DIR=/app/certs
      - DERP_HTTP_PORT=-1
# 这里如果不使用非标端口,直接删掉ports部分配置,使用host
#    network_mode: host
    ports:
      - 8443:443
      - 3478:3478/udp
    volumes:
      - /root/AppData/derper/certs/derp.sunnypai.top:/app/certs
      - /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock
    image: fredliang/derper:latest
    restart: unless-stopped
    networks:
      - derper_network

networks:
  derper_network:
    external: true

如果不使用非标端口,也可以使用letsencrypt配置证书,这边建议看其余教程,我懒得测试

然后在控制平面的Access Controls文件中添加这些内容:(请注意缩进)

	// 增加下方这部分
	"derpMap": {
		"Regions": {
			"912": { // 这个节点里面的912随便填,900以上即可
				"RegionID":   912,
				// 这里可以改名,code和name不要求一致
				"RegionCode": "derper_self",
				"RegionName": "Derper Self",
				"Nodes": [
					{
						"Name":     "derper_self",
						"RegionID": 912,
						"DERPPort": 8443, // 更换为自己的端口号
						"STUNPort": 3478,

						"HostName": "example.com", // 此 HostName 参数填写自己的域名

						"InsecureForTests": false, // 测试时可以先写成true
					},
				],
			},
		},
	},

最后重启全部客户机节点,即可实现自建derper部署。

半天后追加:

高峰期2%~3%丢包率,平均延迟75ms,最长279ms,使用酒店wifi,到路由延迟5-10ms,效果很棒!
欢迎搭建成功的朋友们评论留言使用体验!


感谢来自 Kiyoi 的支持。