Skip to content

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。

参考#


Last update: 2026-06-07
Created: 2026-06-07