Raspberry Pi 又掉线了。Tailscale 显示 offline,SSH 连不上。上次遇到类似的情况是路由器封锁了 MAC 地址,更早之前还有一次是 WiFi WPA 握手超时。这台 Pi 似乎每隔一段时间就要给我整点新花样。这次的表象和 MAC 封锁几乎一模一样——但根因完全不同。
症状
通过 Tailscale IP 连 Pi,SSH 直接报 Permission denied (publickey),之后的连接尝试全部挂住不返回。
先做基本排查:
ping -c 3 100.96.22.813 packets transmitted, 3 packets received, 0.0% packet lossround-trip min/avg/max/stddev = 8.866/21.540/41.213/14.103 msPing 完全正常,0% 丢包。再看 Tailscale 状态:
tailscale status | grep raspberrypi-local100.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 小时。
排查过程
第一步:确认端口和网络层
nc -z -w 5 100.96.22.81 22Connection to 100.96.22.81 port 22 [tcp/ssh] succeeded!SSH 端口开着。网络层没问题,端口也通,但 SSH 认证过不去。这时候怀疑是旧的 SSH ControlMaster 连接在捣乱,清理之后直接用局域网 IP 连:
ssh -o StrictHostKeyChecking=accept-new pi@192.168.1.xconnectedraspberrypi 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:
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 的外网连通性:
ping -c 2 8.8.8.82 packets transmitted, 2 received, 0% packet lossrtt min/avg/max/mdev = 16.092/16.484/16.877/0.392 msICMP 完全正常。再试 HTTPS:
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 timeoutcurl: (28) SSL connection timeoutTCP 连接成功了,但 TLS 握手超时。这个表象很有迷惑性——看起来像是路由器在做深度包检测(DPI)阻断 TLS 流量。之前确实遇到过 Verizon 路由器封锁 MAC 地址的情况,所以第一反应是又被封了。
但事实不是这样。
第四步:找到真正的根因
看 Pi 的 DNS 配置:
cat /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.100search tailc2559b.ts.net问题找到了。
Tailscale 默认开启 --accept-dns=true,会把 /etc/resolv.conf 改成指向 100.100.100.100——这是 Tailscale 内置的 MagicDNS 解析器,跑在 tailscaled 进程里。
正常情况下这没问题。但当 Tailscale 与控制平面断连后:
100.100.100.100的 DNS 解析器虽然还在运行,但无法解析外部域名(因为上游转发链路断了)- Pi 的所有 DNS 查询都发给
100.100.100.100,全部失败 curl https://google.com虽然 TCP 连上了(因为有 DNS 缓存或者 curl 自己做了 IP 解析),但 TLS 握手需要额外的 DNS 查询(OCSP、证书验证等),这些查询超时- 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):
ping -c 2 8.8.8.8 # 纯IP,不需要DNS正常。
curl --connect-timeout 5 https://google.com # 需要DNS超时。
网络本身没问题,纯粹是 DNS 解析挂了。
修复
修复很简单——告诉 Tailscale 不要接管 DNS:
sudo tailscale downsudo tailscale up --accept-dns=false --advertise-exit-node --accept-routesWARNING更推荐用
tailscale set --accept-dns=false,它只改指定参数,不会重置其他设置。tailscale up会把未指定的参数恢复默认值,容易误伤。Tailscale v1.8 以后加了安全提示,会告诉你需要显式带上所有非默认参数。
--accept-dns=false 让 Tailscale 不再修改 /etc/resolv.conf。执行后 DNS 立刻恢复到 NetworkManager 管理:
# Generated by NetworkManagersearch mynetworksettings.comnameserver 192.168.1.1DNS 指向路由器,不再依赖 Tailscale。验证一下:
curl -s -o /dev/null -w '%{http_code} %{time_total}s' https://google.com301 0.416613sHTTPS 恢复,0.4 秒返回。Tailscale 也重新上线:
tailscale status | grep raspberrypi-local100.96.22.81 raspberrypi-local linux idle; offers exit node不再显示 offline。
持久化
tailscale up 的参数会写入状态文件 /var/lib/tailscale/tailscaled.state,重启后自动生效。验证:
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 后 | 立刻恢复 | 也恢复了(巧合) |
| 根因 | 路由器 DPI | DNS 解析失败 |
这次换 MAC 后之所以也恢复了,是因为换 MAC 触发了网络接口 down/up → NetworkManager 重新配置 → resolv.conf 被刷新为路由器 DNS → DNS 恢复 → Tailscale 重连成功。看起来像是 MAC 的问题,其实是网络重启的副作用修好了 DNS。
经验
-
Tailscale 接管 DNS 在无人值守设备上是个定时炸弹。一旦 Tailscale 与控制平面断连,整台机器的 DNS 就废了,而且无法自愈。对于 headless 的 Pi、路由器这类设备,建议始终用
--accept-dns=false。 -
TLS 握手超时不一定是网络封锁。TCP 连接成功但 TLS 超时,也可能是 DNS 问题——TLS 握手过程中有 OCSP 查询等需要 DNS 的操作。
-
相似的症状可以有完全不同的根因。上次是路由器封 MAC,这次是 DNS 死锁,表象几乎一样。
curl -v的详细输出是区分两者的关键——看 TCP 连接是否成功。 -
Tailscale 的
100.100.100.100DNS 即使在tailscaled进程存活时也可能失效。进程还在跑,但如果上游连接断了,DNS 转发就不工作了。这不是 crash,是 degraded state,更难发现。
TIP对于任何无人值守的 Tailscale 节点(树莓派、NAS、路由器),强烈建议设置
--accept-dns=false,让 DNS 走本地网络,避免这个死锁陷阱。