1614 字
8 分钟
Tailscale DNS 死锁:一次 Raspberry Pi 掉线排查

Raspberry Pi 又掉线了。Tailscale 显示 offline,SSH 连不上。上次遇到类似的情况是路由器封锁了 MAC 地址,更早之前还有一次是 WiFi WPA 握手超时。这台 Pi 似乎每隔一段时间就要给我整点新花样。这次的表象和 MAC 封锁几乎一模一样——但根因完全不同。

症状#

通过 Tailscale IP 连 Pi,SSH 直接报 Permission denied (publickey),之后的连接尝试全部挂住不返回。

先做基本排查:

Terminal window
ping -c 3 100.96.22.81
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 8.866/21.540/41.213/14.103 ms

Ping 完全正常,0% 丢包。再看 Tailscale 状态:

Terminal window
tailscale status | grep raspberrypi-local
100.96.22.81 raspberrypi-local tagged-devices linux
active; offers exit node; direct 192.168.1.x:41641;
offline, last seen 2h ago, tx 26744 rx 19832

标记着 active 但又说 offline, last seen 2h ago——矛盾的状态。Ping 能通是因为走的局域网 direct path,但 Tailscale 的控制通道已经断了 2 小时。

排查过程#

第一步:确认端口和网络层#

Terminal window
nc -z -w 5 100.96.22.81 22
Connection to 100.96.22.81 port 22 [tcp/ssh] succeeded!

SSH 端口开着。网络层没问题,端口也通,但 SSH 认证过不去。这时候怀疑是旧的 SSH ControlMaster 连接在捣乱,清理之后直接用局域网 IP 连:

Terminal window
ssh -o StrictHostKeyChecking=accept-new pi@192.168.1.x
connected
raspberrypi
10:09:35 up 16 days, 10:18, 0 user, load average: 0.07, 0.06, 0.01

局域网 SSH 通了。Pi 本身活着,uptime 16 天,负载正常。

第二步:检查 Tailscale 日志#

登上 Pi 之后直接看 Tailscale 的 journal:

Terminal window
sudo journalctl -u tailscaled --since '2 hours ago' --no-pager | tail -30

关键日志:

health: Unable to connect to the Tailscale coordination server
derphttp.Client.Recv: connecting to derp-1 (nyc)
health: Tailscale could not connect to the 'New York City' relay server.
Your Internet connection might be down, or the server might be temporarily unavailable.
Post "https://controlplane.tailscale.com/machine/map":
connection attempts aborted by context: context deadline exceeded
all connection attempts failed
(HTTP: TLS forced: no port 80 dialed, HTTPS: context deadline exceeded)

Tailscale 连不上控制平面 controlplane.tailscale.com,也连不上 NYC 的 DERP 中继服务器。所有 HTTPS 连接超时。

第三步:定位网络断点#

逐层排查 Pi 的外网连通性:

Terminal window
ping -c 2 8.8.8.8
2 packets transmitted, 2 received, 0% packet loss
rtt min/avg/max/mdev = 16.092/16.484/16.877/0.392 ms

ICMP 完全正常。再试 HTTPS:

Terminal window
curl -v --connect-timeout 5 https://google.com 2>&1
* Connected to google.com (142.251.41.174) port 443 (#0)
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* SSL connection timeout
curl: (28) SSL connection timeout

TCP 连接成功了,但 TLS 握手超时。这个表象很有迷惑性——看起来像是路由器在做深度包检测(DPI)阻断 TLS 流量。之前确实遇到过 Verizon 路由器封锁 MAC 地址的情况,所以第一反应是又被封了。

但事实不是这样。

第四步:找到真正的根因#

看 Pi 的 DNS 配置:

Terminal window
cat /etc/resolv.conf
/etc/resolv.conf
# resolv.conf(5) file generated by tailscale
# For more info, see https://tailscale.com/s/resolvconf-overwrite
# DO NOT EDIT THIS FILE BY HAND -- CHANGES WILL BE OVERWRITTEN
nameserver 100.100.100.100
search tailc2559b.ts.net

问题找到了。

Tailscale 默认开启 --accept-dns=true,会把 /etc/resolv.conf 改成指向 100.100.100.100——这是 Tailscale 内置的 MagicDNS 解析器,跑在 tailscaled 进程里。

正常情况下这没问题。但当 Tailscale 与控制平面断连后:

  1. 100.100.100.100 的 DNS 解析器虽然还在运行,但无法解析外部域名(因为上游转发链路断了)
  2. Pi 的所有 DNS 查询都发给 100.100.100.100,全部失败
  3. curl https://google.com 虽然 TCP 连上了(因为有 DNS 缓存或者 curl 自己做了 IP 解析),但 TLS 握手需要额外的 DNS 查询(OCSP、证书验证等),这些查询超时
  4. Tailscale 想重连 controlplane.tailscale.com,需要 DNS 解析这个域名——但 DNS 已经断了

这就是死锁:Tailscale 断了 → DNS 断了 → Tailscale 无法重连 → DNS 继续断着。系统无法自愈。

这不是我一个人踩的坑。Tailscale 的 GitHub 上有多个相关 issue:#3817(DNS Stuck on 100.100.100.100)和 #15385(MagicDNS + resolv.conf + network disconnect => Broken DNS)都在描述这个问题。

验证死锁理论#

为了确认,测试纯 IP 连接(绕过 DNS):

Terminal window
ping -c 2 8.8.8.8 # 纯IP,不需要DNS

正常。

Terminal window
curl --connect-timeout 5 https://google.com # 需要DNS

超时。

网络本身没问题,纯粹是 DNS 解析挂了。

修复#

修复很简单——告诉 Tailscale 不要接管 DNS:

Terminal window
sudo tailscale down
sudo tailscale up --accept-dns=false --advertise-exit-node --accept-routes
WARNING

更推荐用 tailscale set --accept-dns=false,它只改指定参数,不会重置其他设置。tailscale up 会把未指定的参数恢复默认值,容易误伤。Tailscale v1.8 以后加了安全提示,会告诉你需要显式带上所有非默认参数。

--accept-dns=false 让 Tailscale 不再修改 /etc/resolv.conf。执行后 DNS 立刻恢复到 NetworkManager 管理:

/etc/resolv.conf
# Generated by NetworkManager
search mynetworksettings.com
nameserver 192.168.1.1

DNS 指向路由器,不再依赖 Tailscale。验证一下:

Terminal window
curl -s -o /dev/null -w '%{http_code} %{time_total}s' https://google.com
301 0.416613s

HTTPS 恢复,0.4 秒返回。Tailscale 也重新上线:

Terminal window
tailscale status | grep raspberrypi-local
100.96.22.81 raspberrypi-local linux idle; offers exit node

不再显示 offline。

持久化#

tailscale up 的参数会写入状态文件 /var/lib/tailscale/tailscaled.state,重启后自动生效。验证:

Terminal window
tailscale debug prefs | grep CorpDNS
"CorpDNS": false,

CorpDNS: false 对应 --accept-dns=false,已经持久化了。Pi 用 systemd 管理 Tailscale,开机自启,不需要额外配置。

为什么表象像是 MAC 封锁#

这台 Pi 之前确实被 Verizon 路由器封过 MAC 地址。那次的症状是 ICMP 通但 HTTP/HTTPS 全部超时——和这次几乎一样。

区别在于:

上次(MAC 封锁)这次(DNS 死锁)
ICMP
TCP 连接超时成功
TLS 握手N/A超时
换 MAC 后立刻恢复也恢复了(巧合)
根因路由器 DPIDNS 解析失败

这次换 MAC 后之所以也恢复了,是因为换 MAC 触发了网络接口 down/up → NetworkManager 重新配置 → resolv.conf 被刷新为路由器 DNS → DNS 恢复 → Tailscale 重连成功。看起来像是 MAC 的问题,其实是网络重启的副作用修好了 DNS。

经验#

  1. Tailscale 接管 DNS 在无人值守设备上是个定时炸弹。一旦 Tailscale 与控制平面断连,整台机器的 DNS 就废了,而且无法自愈。对于 headless 的 Pi、路由器这类设备,建议始终用 --accept-dns=false

  2. TLS 握手超时不一定是网络封锁。TCP 连接成功但 TLS 超时,也可能是 DNS 问题——TLS 握手过程中有 OCSP 查询等需要 DNS 的操作。

  3. 相似的症状可以有完全不同的根因。上次是路由器封 MAC,这次是 DNS 死锁,表象几乎一样。curl -v 的详细输出是区分两者的关键——看 TCP 连接是否成功。

  4. Tailscale 的 100.100.100.100 DNS 即使在 tailscaled 进程存活时也可能失效。进程还在跑,但如果上游连接断了,DNS 转发就不工作了。这不是 crash,是 degraded state,更难发现。

TIP

对于任何无人值守的 Tailscale 节点(树莓派、NAS、路由器),强烈建议设置 --accept-dns=false,让 DNS 走本地网络,避免这个死锁陷阱。

Tailscale DNS 死锁:一次 Raspberry Pi 掉线排查
https://blog.lishuyu.top/posts/tailscale-dns-deadlock/
作者
猫猫魔女
发布于
2026-03-26
许可协议
CC BY-NC-SA 4.0