文章

一次 Tailscale + Clash Party 排障复盘:别被 nslookup 带偏

一次 Tailscale + Clash Party 排障复盘:别被 nslookup 带偏

日期:2026-05-23 场景:本地开着 Clash / Mihomo,访问 Tailscale 内网域名 *.ts.net 症状:nslookup 返回 NXDOMAIN,代理转发访问失败


背景

这次问题表面上很简单:

1
nslookup pi5-memect-dev.tail9733c5.ts.net

返回:

1
server can't find pi5-memect-dev.tail9733c5.ts.net: NXDOMAIN

第一眼很容易得出结论:Tailscale 的 MagicDNS 挂了,或者 Clash 把系统 DNS 搞坏了。

但这次真正有价值的地方,不是把问题修掉,而是把排障路径复盘清楚。因为中间有几次判断差点把人带进错误方向。


先说结论

最后确认的问题根因不是单一故障,而是两层问题叠在一起:

  1. nslookup 本身会绕过 macOS 的 split DNS,直接走公共 DNS,所以它对 *.ts.net 的报错有误导性。
  2. Clash Party / mihomo 内部 DNS 确实没有正确为 Tailscale 域名做分流,导致代理场景下解析失败。

最终修复点是:

1
2
3
nameserver-policy:
  "+.ts.net": "100.100.100.100"
  "+.tailscale.net": "100.100.100.100"

重点不是 100.100.100.100 这个地址本身,而是:

  • 要把 Tailscale 域名交给它自己的 DNS 解析器
  • 模式必须用 +.ts.net
  • 不能写成 *.ts.net

排障过程里最重要的几个转折

1. 不要只盯着 nslookup

一开始看到 NXDOMAIN,最自然的推断是:

  • Clash 接管了 DNS
  • *.ts.net 被发给了 8.8.8.8
  • 公共 DNS 不认识 Tailscale 域名

这个方向不算错,但不完整。

后面继续看系统 DNS 配置时,发现 macOS 的 resolver 里其实已经存在针对 tail9733c5.ts.net 的专用解析器,nameserver 正是 100.100.100.100。这说明:

  • Tailscale 自己的 split DNS 是正常的
  • 系统级解析能力并没有坏
  • nslookup 只是没有按我们以为的那样走系统 resolver

这一步的经验非常关键:

nslookup 只能说明某个 DNS server 的结果,不等于说明系统真实访问路径。

排障时至少要把下面几类验证分开看:

  • 工具层验证:nslookup / dig
  • 系统层验证:scutil --dns、系统 resolver
  • 真实业务验证:curlssh、浏览器、WebSocket

2. 真实流量比配置文件更能说明问题

后面做了一次关键验证:

  • 直接访问目标服务,WebSocket 可以 101 Switching Protocols
  • 通过 Clash 代理访问时,TLS 握手阶段报 SSL_ERROR_SYSCALL

这说明:

  • 服务端没挂
  • Tailscale 网络本身没挂
  • 问题发生在代理链路内部

很多时候我们喜欢围着配置文件打转,但更有效的方式其实是先判断:

失败发生在“解析前、连接前、TLS 阶段,还是应用层”。

这次就是因为真实流量验证把范围快速缩小到了 Clash 内部 DNS。


3. 改“运行时文件”不等于真正修好

中间还踩了一个很常见的坑:找到运行中的生成配置,直接改,表面上看也改成功了,但 reload 后不生效,或者一重启就被覆盖。

这类代理工具通常会有几层配置:

  • 持久化基础配置
  • 订阅合并后的运行时配置
  • 内核当前已加载配置

如果只改了运行时产物,结论往往是不稳定的。

这次后面真正有效的做法,是去找 持久化的基础配置入口,而不是盯着临时生成文件。

经验是:

看到 “Generated by …” 之类的文件时,先别急着改,先确认它是不是最终可持续的配置源。


4. *.ts.net+.ts.net 不是一回事

这是本次排障里最值钱的细节。

一开始写的是:

1
2
nameserver-policy:
  "*.ts.net": "100.100.100.100"

看起来很合理,但对 pi5-memect-dev.tail9733c5.ts.net 这种域名并没有匹配成功。

原因是它不是简单的一层子域名,而是更深层级。对 mihomo 来说,这里要用 +.ts.net

准确写法是:

1
2
3
nameserver-policy:
  "+.ts.net": "100.100.100.100"
  "+.tailscale.net": "100.100.100.100"

这一步之后,再 reload 并重新走代理链路,TLS 握手才恢复正常。

这类问题的经验不是“记住某个语法”,而是:

域名匹配规则必须拿真实目标域名去验证,不要凭感觉。


这次排障值得固化的工作方法

如果以后再遇到“代理 + 内网域名 + DNS 解析异常”,我会按这个顺序处理:

1. 先分层,不要混着看

  • 系统 resolver 是否正常
  • 目标服务是否正常
  • 代理内核是否能独立解析该域名
  • 代理后的真实请求是否成功

2. 不要把 nslookup 当成最终证据

尤其在 macOS、Tailscale、split DNS、企业 VPN 这类场景下,nslookup 很容易误导。

3. 优先做端到端验证

比起“配置看起来对了”,我更信:

  • curl 能不能通
  • WebSocket 能不能 101
  • 代理后的 TLS 握手能不能完整走完

4. 先找到持久化配置入口

不要只改生成产物,不然很容易出现:

  • reload 不生效
  • 重启丢配置
  • 订阅一更新又回滚

5. 域名规则必须用真实样本验证

*.ts.net+.ts.net 这种差异,单看配置很难一眼看出,必须用真实域名验证命中情况。


最后总结成一句话

这次排障最值得记住的,不是 “给 Tailscale 配一个 nameserver-policy”,而是:

当系统里同时存在 split DNS、代理 DNS、生成配置和运行时内核时,任何单点工具输出都不该被直接当成结论。

真正有效的排障,不是更快地下判断,而是更早地把“哪一层在失败”分清楚。

这也是 AI Agent 协作里很有意思的一点:Agent 可以很快帮你做搜索、改配置、反复验证,但人还是要盯住判断框架,否则很容易被一个看似合理的中间结论带偏。

本文由作者按照 CC BY 4.0 进行授权