自建 Tailscale DERP 与 Peer Relay:阿里云 DNS 踩坑全记录

自建 Tailscale DERP 与 Peer Relay:阿里云 DNS 踩坑全记录

Tailscale 在国内用起来最大的痛点之一,是 P2P 打洞失败后会掉到官方 DERP 中继。官方 DERP 节点在复杂网络、跨境链路下经常表现为延迟高、吞吐差,甚至几乎不可用。于是就有了自建私有 DERP 的需求:在直连打不通时,至少还能走一条低延迟、可控的中继路径。


1. DERP 是什么,为什么要自建

DERP(Distributed, Encrypted, and Resilient Packet Relay)是 Tailscale 的加密流量中继组件。连接路径大致是:

1
2
优先:直连 / NAT 打洞(P2P)
失败:走 DERP 中继

官方 DERP 全球有很多节点,但对国内访问并不友好。自建 DERP 的目标很明确:

  • 把中继落在自己可控的机房/云厂商
  • 降低 relay 场景下的延迟和抖动
  • 在 P2P 失败时仍能维持可用的 Tailscale 连接

补充一句:DERP 版本也会跟着 Tailscale 更新。旧版 derper 在中转模式下未必能吃到新版本在吞吐等方面的改进,所以镜像最好能跟着上游版本走。


2. 环境准备

项目建议
系统Linux(Ubuntu / Debian 等)
运行时已安装 Docker,docker --version 可正常输出
Tailscale宿主机安装并登录 Tailscale 客户端
网络能出公网;映射端口未被占用,安全组/防火墙放行
公网有公网 IP 或可被客户端访问的入口(IP + 端口即可)

宿主机启动 Tailscale 后,一般会在:

1
/var/run/tailscale/tailscaled.sock

(部分系统路径为 /run/tailscale/tailscaled.sock

提供套接字文件。DERP 容器需要挂载这个目录,才能做客户端校验(--verify-clients)。


3. Docker 部署 DERP

3.1 镜像说明

这里使用的是 deepfal/tailscale-derp。作者侧用 GitHub Actions 跟踪上游 derper 版本、打补丁后推到 Docker Hub,直观效果包括:

  • 跟进较新的 derper 构建
  • 去除域名强制校验相关限制,国内可用 IP + 端口,不必为了 DERP 单独去搞备案域名
  • 安全边界仍由客户端校验等机制兜住,宿主机本身要管好权限与暴露面

拉取镜像:

1
docker pull deepfal/tailscale-derp:latest

3.2 启动容器

1
2
3
4
5
6
7
8
9
10
11
12
13
docker run -d \
--name tailscale-derp \
--restart unless-stopped \
-p 0.0.0.0:59443:36666 \
-p 0.0.0.0:3478:3478/udp \
-v /run/tailscale:/var/run/tailscale:ro \
deepfal/tailscale-derp:latest \
./derper \
-hostname derp.deepfal.cn \
-a :36666 \
-certmode manual \
-certdir /ssl \
--verify-clients

若你机器上套接字在 /var/run/tailscale,把挂载源路径改成对应目录即可。建议挂目录而不是单文件,避免客户端重启后 sock 重建导致容器侧异常。

3.3 参数说明

参数含义
-d / --restart unless-stopped后台运行,异常退出后自动拉起
-p 0.0.0.0:59443:36666宿主机 TCP 59443 → 容器 DERP 监听 36666
-p 0.0.0.0:3478:3478/udpSTUN 相关 UDP 端口
-v /run/tailscale:/var/run/tailscale:ro只读挂载 tailscaled 套接字目录,供 --verify-clients 使用
-hostname ...derper 主机名参数(与证书/手工模式配合)
-a :36666容器内监听地址
-certmode manual / -certdir /ssl手动证书模式
--verify-clients校验连接方是否为当前 Tailnet 合法客户端

3.4 看日志确认启动

1
docker logs -f tailscale-derp

看到类似 DERP server started 的信息即可继续。

3.5 防火墙与安全组

至少放行:

1
2
TCP 59443   # 你映射的 DERP 端口,可按需改
UDP 3478 # STUN

确认端口未被占用,云厂商安全组与本机 ufw/firewalld 规则一致。


4. 在 Tailscale 中接入自建 DERP

在 Tailscale 管理后台(Access controls / 自定义 derpMap,以你当前控制台入口为准)写入类似配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"derpMap": {
"OmitDefaultRegions": false,
"Regions": {
"910": {
"RegionID": 910,
"RegionCode": "自建节点",
"RegionName": "自建节点",
"Nodes": [
{
"Name": "910a",
"RegionID": 910,
"HostName": "你的公网IP或可解析主机名",
"InsecureForTests": true,
"DERPPort": 59443
}
]
}
}
}
}

说明:

  • OmitDefaultRegions: false:保留官方区域,自建作为补充;若只想强制走自建,再考虑改为 true(需充分评估可用性)。
  • RegionID 建议用 900+ 自定义段,避免和官方区域冲突。
  • InsecureForTests: true 适用于 IP + 手动证书/测试场景;生产若配好正规证书,可再收紧。
  • HostName / DERPPort 必须和容器实际对外入口一致。

保存后,在客户端执行:

1
tailscale netcheck

确认自定义 region 出现且可达。再结合实际跨 NAT 设备互 ping、传文件,看是否从高延迟官方中继切到自建节点。


5. 踩坑:阿里云 ECS 装上 Tailscale 后「没网」

DERP / Peer Relay 往往要落在有公网的云主机上。我在阿里云 ECS 上装完 Tailscale 后,出现了经典但很容易误判的故障:

1
2
3
安装前:网络正常
安装并启动 Tailscale 后:访问互联网异常
停止/卸载 Tailscale 后:网络恢复

常见失败命令:

1
2
3
apt update
curl https://www.baidu.com
ping www.baidu.com

但直接 ping 公网 IP 可能正常:

1
ping -c 3 223.5.5.5

说明默认路由未必断了,更可疑的是 DNS

5.1 现象确认

1
cat /etc/resolv.conf

阿里云 VPC 常见:

1
2
nameserver 100.100.2.136
nameserver 100.100.2.138

再对比:

1
2
ping -c 3 223.5.5.5          # 往往通
getent ahostsv4 www.baidu.com # 可能无结果

结论方向:

1
公网三层可达,域名解析链路异常

5.2 根因:阿里云 DNS 落在 Tailscale CGNAT 网段

阿里云默认 DNS:

1
2
100.100.2.136
100.100.2.138

属于:

1
100.64.0.0/10

而 Tailscale IPv4 地址空间也使用 CGNAT 的 100.64.0.0/10

Linux 上 Tailscale 默认 netfilter-mode=on 时,会加防护规则,大意是:

1
2
允许从 tailscale0 进来的 Tailnet 流量
丢弃从其他接口进入、却声称属于 100.64.0.0/10 的流量

目的是防止物理网卡侧伪造 Tailscale 地址。问题在于:阿里云 DNS 响应包源地址正好是 100.100.2.x,入口却是 eth0 一类物理网卡,于是被规则直接 DROP。

链路示意:

1
2
3
4
5
6
7
应用 → DNS 查询 100.100.2.136

阿里云 DNS 响应(src=100.100.2.136,入接口 eth0)

Tailscale netfilter:非 tailscale0 的 CGNAT → DROP

解析失败 → apt/curl/域名访问全挂

Tailscale 社区也有针对阿里云 DNS 的同类报告(如 issue #12696)。

5.3 快速验证是不是这个问题

1
2
3
4
5
# 看 CGNAT 相关规则(iptables)
sudo iptables -S ts-input | grep 100.64

# 或 nftables
sudo nft list ruleset | grep -C 5 '100.64.0.0/10'

仅用于测试可临时关闭 netfilter 管理:

1
2
3
sudo tailscale set --netfilter-mode=off
getent hosts www.baidu.com
curl -4I --connect-timeout 8 https://www.baidu.com

若立刻恢复,再改回:

1
sudo tailscale set --netfilter-mode=on

不建议把 netfilter-mode=off 当长期方案:Exit Node、Subnet Router、Peer Relay、Docker 宿主机叠在一起时,防火墙全靠人肉维护风险很大。

5.4 推荐修复:换 DNS + 禁止 Tailscale 接管 DNS

目标:

  1. --accept-dns=false,不接受 Tailnet 下发的 DNS
  2. 把系统 DNS 换成不在 100.64.0.0/10 的地址(例如阿里公共 DNS)
  3. 保留 netfilter-mode=on
1
sudo tailscale set --accept-dns=false

注意:只关 accept-dns 不够。若 /etc/resolv.conf 仍指向 100.100.2.136/138,响应包照样会被丢。

使用 systemd-resolved 时

1
2
3
4
5
6
systemctl is-active systemd-resolved   # 期望 active

IFACE=$(ip route show default | awk '/default/ {print $5; exit}')
sudo resolvectl dns "$IFACE" 223.5.5.5 223.6.6.6
sudo resolvectl domain "$IFACE" '~.'
sudo resolvectl flush-caches

持久化:

1
2
3
4
5
6
7
8
9
10
sudo mkdir -p /etc/systemd/resolved.conf.d
sudo tee /etc/systemd/resolved.conf.d/aliyun-tailscale.conf >/dev/null <<'EOF'
[Resolve]
DNS=223.5.5.5 223.6.6.6
FallbackDNS=1.1.1.1 8.8.8.8
Domains=~.
EOF

sudo systemctl restart systemd-resolved
sudo resolvectl flush-caches

确认 stub 链接:

1
2
3
4
ls -l /etc/resolv.conf
# 常见应为 /run/systemd/resolve/stub-resolv.conf
# 必要时:
# sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf

无 systemd-resolved 时

1
2
3
4
5
6
sudo cp -L /etc/resolv.conf /etc/resolv.conf.bak
sudo tee /etc/resolv.conf >/dev/null <<'EOF'
nameserver 223.5.5.5
nameserver 223.6.6.6
options timeout:2 attempts:2 rotate
EOF

注意 DHCP 续租可能覆盖文件;长期更稳妥的是用网络管理器或阿里云 VPC DHCP 选项集统一配 DNS。

5.5 修复后验收清单

1
2
3
4
5
6
7
8
9
10
cat /etc/resolv.conf
resolvectl status 2>/dev/null

ping -c 3 223.5.5.5
getent ahostsv4 www.baidu.com
curl -4I --connect-timeout 8 https://www.baidu.com
sudo apt update

tailscale status
tailscale netcheck

期望:

1
2
3
4
公网 IP 通
域名解析通
apt / HTTPS 正常
Tailscale 在线

6. Peer Relay 配置记录

网络修好后,在同一台阿里云上继续做 Peer Relay,作为严格 NAT 场景下的另一条中继能力(与 DERP 互补,角色不同)。

6.1 节点角色示例

1
2
3
4
5
6
7
8
9
10
11
12
13
debian
Tailscale IP:100.95.79.73
标签:tag:srv
用途:严格 NAT 后的业务机

阿里云节点
Tailscale IP:100.91.18.89
标签:tag:relay
用途:Peer Relay + Exit Node

Windows
设备名:computer
用途:访问 debian

6.2 标签、Exit Node 与 DNS

优先用 tailscale set,避免 tailscale up 重跑时漏参数:

1
2
3
4
sudo tailscale set \
--accept-dns=false \
--advertise-tags=tag:relay \
--advertise-exit-node=true

阿里云环境建议长期保留 --accept-dns=false

6.3 开启 Peer Relay 端口

1
sudo tailscale set --relay-server-port=40000

若有固定公网 EIP,建议同时声明静态端点:

1
2
3
sudo tailscale set \
--relay-server-port=40000 \
--relay-server-static-endpoints="<阿里云公网IP>:40000"

确认监听:

1
sudo ss -lunp | grep 40000

应看到 tailscaled 在 UDP 40000。

阿里云安全组入方向:

1
2
3
协议:UDP
端口:40000
来源:按实际需要收紧;测试期可先 0.0.0.0/0

本机 UFW:

1
sudo ufw allow 40000/udp

客户端与中继机都需要支持 Peer Relay 的 Tailscale 版本。

6.4 ACL / Grants 示例

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
{
"tagOwners": {
"tag:relay": ["你的账号@example.com"],
"tag:srv": ["你的账号@example.com"]
},
"grants": [
{
"src": ["autogroup:member"],
"dst": ["tag:srv"],
"ip": ["*"]
},
{
"src": ["tag:srv"],
"dst": ["tag:srv"],
"ip": ["*"]
},
{
"src": ["tag:srv"],
"dst": ["tag:relay"],
"app": {
"tailscale.com/cap/relay": []
}
}
]
}

要点:

  • tag:srv:允许使用 Peer Relay 的一侧
  • tag:relay:真正跑中继的节点
  • tailscale.com/cap/relay:下发 Peer Relay 能力

官方说明:不必再单独加一条「允许访问中继机所有 IP/端口」的普通 grant;capability 本身就是分配中继绑定的控制机制。

6.5 验证 Peer Relay

客户端查看候选中继:

1
2
# Windows
tailscale debug peer-relay-servers
1
2
# Linux
sudo tailscale debug peer-relay-servers

正常时类似:

1
2
3
[
"100.91.18.89"
]

中继机看会话:

1
sudo tailscale debug peer-relay-sessions

抓包 + 造流量:

1
2
# 阿里云
sudo tcpdump -ni any udp port 40000
1
2
3
# Windows
tailscale ping --c 20 jhxdebian
tailscale status

成功走 Peer Relay 时,tailscale status 连接类型会接近:

1
active; peer-relay <公网IP>:40000:vni:<ID>

而不是一直停在类似 active; relay "tok" 的官方 DERP 描述。


7. 整体架构回顾

1
2
3
4
5
6
7
客户端 A  ←?→  客户端 B
│ │
├─ 优先 P2P 打洞

├─ 失败时可走:自建 DERP(TCP 映射端口,如 59443)

└─ 严格 NAT 等场景:Peer Relay(UDP 40000,tag:relay)
组件作用注意点
自建 DERP替换/补充官方中继路径Docker + 宿主机 Tailscale sock;derpMap 配置
Peer Relay节点级 UDP 中继能力版本、安全组、grant cap、静态端点
阿里云 DNS 修复保证装了 Tailscale 还能解析公网域名避开 100.64 段 DNS,保留 netfilter-on

参考