Tailscale MagicDNS 冷查询 REFUSED:从 direct 模式切回 systemd-resolved#
症状#
域名第一次解析失败,立刻重试就成功。浏览器表现为偶发 DNS_PROBE_FINISHED_NXDOMAIN 然后刷新就好;curl 表现为首次 Could not resolve host 第二次正常。
现场抓证据#
dig 直接打 Tailscale 的本地 DNS 入口看冷查询行为:
for h in debian.org kernel.org python.org openstreetmap.org; do
dig +tries=1 +timeout=5 @100.100.100.100 $h A | grep -E 'status|ANSWER:'
done
结果:
debian.org status: REFUSED ANSWER: 0
kernel.org status: NOERROR ANSWER: 1
python.org status: REFUSED ANSWER: 0
openstreetmap.org status: REFUSED ANSWER: 0
立刻再跑一次同样的域名,全部 NOERROR 且拿到答案。REFUSED 是 rcode 5,含义是"解析器拒绝处理这次查询",不是 NXDOMAIN,也不是超时。
随机生成的域名(保证 Tailscale 内部没缓存过)同样首次 REFUSED:
1778755192-a.example.com REFUSED ANSWER: 0
说明问题与具体域名无关,是 Tailscale 内置 DNS forwarder 的状态问题。
根因#
Tailscale 在 Linux 上管理 DNS 有两种模式(见官方文档 tailscale.com/docs/reference/linux-dns 和 issue tailscale/tailscale#7655、#7816):
resolved模式:当/etc/resolv.conf是符号链接指向/run/systemd/resolve/stub-resolv.conf时启用。Tailscale 通过 DBus 把ts.net注入为 systemd-resolved 的 split DNS 路由,其余流量由 systemd-resolved 转发到 NetworkManager/DHCP 学到的真实上游。这是 Linux 上的推荐模式。direct模式:兜底。Tailscale 直接重写/etc/resolv.conf,把唯一的nameserver 100.100.100.100指向自己内置的 DNS forwarder,再由 forwarder 转发给"备份的 pre-tailscale resolvers"。
本机原本是 direct 模式:
$ cat /etc/resolv.conf
# resolv.conf(5) file generated by tailscale
nameserver 100.100.100.100
nameserver fd7a:115c:a1e0::53
...
$ systemctl is-active systemd-resolved
inactive
direct 模式下,tailscaled 启动时会备份当时的 resolv.conf 作为上游。若那个备份本来就是空/失效(比如机器一直由 NetworkManager 维护、tailscaled 接管时机不对,或者命中了 #7816 描述的备份被污染回写为 100.100.100.100 → 形成回环),内置 forwarder 没有可用上游 → 冷查询返回 REFUSED。系统的其他旁路(控制面 fallback、内部缓存)会在背景把答案拉回来,下一次查询就成功。
tailscale dns status 显示 Resolvers (no resolvers configured) 即此症状的直接证据。
不要走 admin console 加 Global Nameserver 这条路:在
Override DNS servers关闭时,那个配置对direct模式的本地 forwarder 并不会强制作为上游下发;开启又会把全 tailnet 设备的 DNS 都接管,在 captive portal 网络下会断网。这是 workaround 不是治本。
修复#
让 systemd-resolved 上线并接管 /etc/resolv.conf,tailscaled 重启后会自动进入 resolved 模式。
Debian 13 上 systemd-resolved 是独立包,默认不预装:
# 1. 停 tailscaled,避免它继续覆盖 resolv.conf
sudo systemctl stop tailscaled
# 2. 临时给 resolv.conf 一个直连上游,让 apt 能解析镜像源
sudo rm /etc/resolv.conf
echo 'nameserver 1.1.1.1' | sudo tee /etc/resolv.conf
# 3. 安装 systemd-resolved,apt postinst 会自动 enable + 重置 symlink
sudo apt update
sudo apt install -y systemd-resolved
# 4. 确认 resolv.conf 指向 stub
ls -l /etc/resolv.conf
# 应该看到:/etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf
# 如果不是,手动改:
# sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
# 5. 让 NetworkManager 把 DHCP 学到的 DNS 推给 systemd-resolved
sudo systemctl restart NetworkManager
# 6. 重启 tailscaled,它会探测到 stub 模式,切到 resolved 模式
sudo systemctl start tailscaled
验证#
$ resolvectl status
Global
resolv.conf mode: stub
Link 3 (enx00e04c680008)
Current DNS Server: 172.26.1.1
DNS Domain: rarfadv.riken.go.jp
Link 4 (wlo1)
Current DNS Server: 103.5.140.1
DNS Servers: 103.5.140.1 103.5.140.2
→ stub 模式,每个网卡都从 DHCP 学到了真实上游。
冷查询不再 REFUSED:
$ dig +tries=1 +short @127.0.0.53 debian.org
151.101.130.132
151.101.66.132
...
ts.net MagicDNS 仍正常:
$ getent hosts tbtdebian.taile291ba.ts.net
100.88.164.82 tbtdebian.taile291ba.ts.net
复盘要点#
- DNS 这种"用着用着出问题"的现象,直接
dig @<resolver>看 rcode 比看应用日志快十倍。REFUSED/SERVFAIL/NOERROR + ANSWER:0三种空答案语义完全不同,不要混为一谈。 - Tailscale 在 Linux 上的 DNS 行为强依赖于
/etc/resolv.conf是否是 stub-resolv.conf 的符号链接。这个 symlink 是 tailscaled 选择模式的唯一探针。 - Debian 默认不装
systemd-resolved。如果机器同时跑 NetworkManager + Tailscale,把这个包加进 bootstrap 列表能避免这类坑。 - 不要被 admin console 的 Global Nameserver 这条路误导。它是给特定网络拓扑用的,不修这个
direct模式的 bug。
参考#
- tailscale/tailscale#7655 — direct mode triggered when resolv.conf points to legacy resolv path
- tailscale/tailscale#7816 — direct 模式下 forwarder 上游被回环污染的完整分析
- tailscale.com/docs/reference/linux-dns — 官方 Linux DNS 模式说明
- tailscale.com/blog/sisyphean-dns-client-linux/ — Linux DNS 客户端探测流程图
Created: 2026-06-07