Tailscale 与 Clash 共存排查 SOP
同一台 Mac 同时运行 Tailscale 和 Clash 后,启智平台打不开了。恢复启智访问后,另一个问题又出现了:Tailscale 设备能 ping 通,浏览器却打不开它的服务首页。
两次故障发生在不同环节。启智请求最初卡在 DNS;Tailscale 服务则是直接访问返回 HTTP 200,经过本地 Clash 代理返回 502。最后的配置是:Tailscale 保留设备互联,关闭 DNS 和接受子网路由;Clash 继续按规则分流,同时在 TUN 和系统代理两处绕过 Tailscale 目标。
下面先复盘证据,再给出可以直接复用的排查 SOP。设备地址统一替换为示例 100.101.102.103,代理端口以本次环境的 7897 为例。
1. 先明确要保留的网络行为
这次需求很具体:通过 Tailscale IP 访问一台设备上的服务,其余日常访问继续由 Clash 按现有规则处理。既不需要用设备名访问,也不需要借助远端设备访问它背后的局域网。
| 功能 | 负责什么 | 本次选择 |
|---|---|---|
| Tailscale 设备互联 | 通过虚拟 IP 连接已加入 tailnet 的设备 | 保留 |
| Tailscale DNS | 接受管理后台配置的 DNS,包括设备名及其他域名的解析设置 | 关闭 |
| 接受 Tailscale 子网路由 | 通过其他节点访问其发布的网段,如远端局域网 | 关闭 |
| Tailscale 出口节点 | 借助指定节点转发普通互联网流量 | 不启用 |
| Clash TUN | 在系统路由层接入流量,再交给规则引擎处理 | 保留,并排除 Tailscale 地址段 |
| Clash 系统代理 | 让遵循系统代理设置的应用连接本地代理端口 | 保留,并绕过目标 IP |
直接访问设备自己的 Tailscale IP,不依赖 DNS,也不需要接受子网路由。关闭这两项不会断开设备互联;如果以后要使用设备名或远端局域网,再按需求重新启用。Tailscale 客户端设置文档
这是一套流量分工配置,不是访问控制策略。其他获授权的 Tailscale 设备仍可能可达;若要限制只能访问某个 IP 或端口,应另行配置 tailnet 访问策略。
2. 第一次故障发生在 DNS 阶段
2.1 先确认连接停在哪里
访问启智平台时,最初的请求直接报错:
1 | curl: (6) Could not resolve host: qz.sii.edu.cn |
这说明请求还没有进入目标站点的 TCP 和 TLS 阶段。此时应先看解析路径,不能直接判断服务器宕机或证书失效。
macOS 的检查命令是:
1 | scutil --dns |
当时系统解析器中存在 Tailscale 的 100.100.100.100,域名查询返回 NXDOMAIN,即解析器回答该域名不存在。
2.2 指定公共 DNS 也可能仍被 Clash 接管
再分别查询两个公共 DNS:
1 | dig @223.5.5.5 qz.sii.edu.cn A +time=3 +tries=1 |
两次却都立即返回 198.18.0.7。结合 Clash 已启用 fake-ip 和 dns-hijack 的配置,可以判断这些查询进入了 Clash 的 DNS 处理路径,返回的是虚拟地址。
指定 @公共DNS,不足以证明查询真的抵达了那个公共 DNS。 在 TUN 接管 DNS 的环境下,需要同时核对配置和响应特征。这里也不能把 198.18.0.7 当成启智服务器的真实地址,更不能写进 hosts 长期使用。
随后用加密 DNS 查询做对照,Google 和 Cloudflare 的解析服务也返回了 NXDOMAIN。这修正了最初的判断:问题不能简单归结为“Tailscale 把一个公开可解析的域名解析坏了”;更准确的结论是,当时的普通解析路径无法得到可用地址,而既有启智代理路径能够完成访问。
2.3 用显式代理验证整条登录链
1 | curl --proxy 'http://127.0.0.1:7897' --noproxy '' \ |
这条路径最终返回 HTTP 200,TLS 验证通过。启智入口先重定向到身份认证服务,所以只看到入口的 302 还不够;必须继续检查登录域名和最终页面。
本机已有启智域名到专用代理组的分流规则。开启系统代理后,遵循系统设置的浏览器可以把目标域名交给 Clash 处理。之后关闭 Tailscale DNS,让本次不需要的 DNS 接管退出,也简化了两套工具的职责。
3. 第二次故障暴露了系统代理路径
3.1 隧道连通和服务可访问是两次验收
第一轮配置完成后,目标设备的 Tailscale ping 成功,系统路由也指向 Tailscale 接口。但浏览器仍然打不开服务首页。
最有区分度的测试,是对同一个 URL 分别走直连和显式代理:
1 | TARGET_IP='100.101.102.103' |
现场结果如下:
| 测试 | 结果 | 能确认的事实 |
|---|---|---|
tailscale ping |
成功,约几十毫秒 | 对端 Tailscale 连接可建立 |
route -n get 目标IP |
指向 Tailscale 接口 | 系统直接连接目标时有对应路由 |
| HTTP 直连 | 200 | 目标 Web 服务可经当前直连路径访问 |
| HTTP 显式经过 Clash | 502,约 5 秒后返回 | 经本地代理的请求路径失败 |
这组对照把问题收敛到了经过本地代理的访问路径。它没有证明目标服务宕机,也不足以单凭现象断定 Mihomo 内部究竟是哪项接口选择或拨号行为导致失败。
3.2 TUN 排除和系统代理绕过作用在不同入口
两种应用访问路径可以画成:
1 | 应用直接连接目标 IP |
Clash 的 TUN 排除网段只影响自动路由的接管范围。如果浏览器已经主动连接本地代理端口,TUN 排除不会撤销这个应用层选择。Mihomo TUN 配置说明
当时 Clash 中已经存在目标 IP 的 DIRECT 规则,但代理访问仍然返回 502。因此,DIRECT 只能说明规则选择了直连出站,不能单独作为“浏览器访问已经成功”的证据。
修复是在系统代理绕过列表中明确加入目标 IP,让浏览器直接连接目标,再由系统的 Tailscale 路由接手。
4. 最终使用的最小配置
4.1 关闭 Tailscale DNS 和接受子网路由
macOS 独立版客户端可以使用应用内的 CLI:
1 | /Applications/Tailscale.app/Contents/MacOS/Tailscale set \ |
若安装方式不同,先确认本机 CLI 路径。set 只修改显式提供的设置;这里没有重新登录或重置整个客户端配置。
同时在 Tailscale 菜单中确认出口节点为 None。本次原本就没有启用出口节点,无需额外更改。
4.2 保留 Clash TUN 的 Tailscale 网段排除
在 Clash Verge 的持久扩展配置中,合并以下字段:
1 | tun: |
合并时保留已有的其他排除项,不要用这个片段覆盖整个 TUN 配置。100.64.0.0/10 是共享地址空间,具体环境若还使用该地址段,需要一并核对路由。
本次 IPv6 TUN 配置已经存在,予以保留;没有通过关闭 IPv6 来掩盖访问路径问题。也不需要为了这次修复再启用全局模式或修改已有策略组。
4.3 把目标 IP 加入系统代理绕过列表
本次 Clash Verge Rev v2.5.8 的操作路径为:
- 打开 Settings → System Proxy 旁的齿轮。
- 关闭 Always use Default Bypass,进入自定义列表。
- 保留原有默认绕过项,新增
100.101.102.103。 - 点击 NEW 加入列表,再点击 SAVE 保存。
- 用
scutil --proxy确认ExceptionsList中确实出现目标 IP。
这个列表填写单个 IP,适用于该 IP 的不同服务端口;不要把 http://、路径或端口一起填进去。不同版本的界面名称可能有所不同。
把设置保存到 Clash Verge,而不只是临时修改 macOS 网络设置,可以避免客户端下次重新应用系统代理时丢失绕过项。
4.4 保留目标 IP 的直连规则作为补充
在持久自定义规则的前部加入:
1 | rules: |
这里展示的是要添加的规则,不是让整个 rules 列表只剩这一项。保留其他规则并重新激活配置,确认内核规则列表中已出现该项。Mihomo 按从上到下的顺序匹配规则。规则说明
这条规则为进入 Clash 的请求提供分流意图;本次浏览器恢复访问的关键,仍是上一节的系统代理绕过。
5. 可复用的分层排查 SOP
步骤一 固定目标和失败表现
先记录 URL、协议、端口、浏览器报错,以及 Tailscale 和 Clash 的当前状态。IP 能打开而域名打不开,优先查 DNS;域名已解析但连接失败,再看路由、端口和代理。
先把下面的示例 IP、服务端口和本地代理端口替换为自己的值:
1 | TARGET_IP='100.101.102.103' |
不要先同时关闭两个软件。否则即使恢复访问,也难以知道究竟哪条路径发生了变化。
步骤二 读取 DNS 和代理设置
1 | scutil --dns |
检查三个位置:系统选择了哪些解析器,HTTP/HTTPS/SOCKS 是否启用,以及目标是否在 ExceptionsList 中。DNS 查询若被 Clash 劫持,应结合 fake-ip 配置解释结果。
对于域名故障,可以用 dig 和加密 DNS 做对照;测试内部域名时,使用自己有权访问且适合该域名的解析器。
步骤三 核对目标路由和 Tailscale 连接
1 | route -n get "$TARGET_IP" |
记录路由对应的接口,以及 Tailscale 报告的连接路径。接口名称可能是 utun8、utun9 或其他编号,应结合本机配置识别,不能把编号写死。
这一步通过后,仍需测试实际服务端口。ping 成功不能替代 HTTP、SSH 或应用自身的验收。
步骤四 对同一服务做直连和代理对照
复用第三节的两条 curl 命令,固定 URL 和超时,只改变是否进入代理。
| 观察结果 | 下一步检查 |
|---|---|
| DNS 失败,显式代理成功 | 解析器选择、域名是否依赖特定代理或网络 |
| Tailscale ping 成功,HTTP 直连失败 | 服务监听地址、端口、主机防火墙及访问策略 |
| HTTP 直连成功,代理失败 | 系统代理绕过、Clash 规则和代理出站路径 |
| HTTP 两种方式成功,浏览器失败 | 浏览器自身代理设置、扩展、缓存及页面子资源 |
| 入口成功,登录页或子域名失败 | 完整重定向链与后续域名的分流和解析 |
curl --noproxy '*' 只排除 curl 的显式代理,不会跳过系统级 TUN。反过来,命令行 curl 也不会自动完整复现浏览器对 macOS 系统代理的使用方式,因此浏览器验收必须单独做。
步骤五 每次修改只对应一个已定位的环节
先改 DNS 或单条绕过规则,再重复同一组测试。把修改写入客户端的持久配置,并核对当前运行状态;不要只编辑会被重新生成的运行时配置文件。
如果系统代理绕过后浏览器已经成功,手动强制 curl --proxy ... --noproxy '' 仍然失败,并不表示绕过设置失效。这条命令刻意绕过了正常分工,用于重现原先失败的代理路径。
步骤六 在浏览器里完成最终验收
本次旧错误标签页没有立即恢复,但新标签页成功显示了目标服务首页。验收应打开原始 URL 或新建标签页,确认实际页面内容,而不只看路由表和 HTTP 状态码。
启智平台还要确认身份认证页面正常渲染;如果只有 302 跳转,或登录页仍然空白,排查还没有结束。
6. 验收表和回退方法
本次最终完成了以下检查:
| 检查项 | 验收结果 |
|---|---|
| Tailscale DNS | 已关闭,系统不再使用其 DNS 配置 |
| 接受子网路由 | 已关闭,Tailscale 继续连接 |
| 出口节点 | 未启用 |
| 目标 IP 路由 | 指向 Tailscale 接口 |
| 目标服务 | HTTP 200,浏览器显示服务首页 |
| 系统代理绕过项 | 目标 IP 同时存在于系统状态和客户端持久设置中 |
| Clash | 保持 Rule 模式,TUN 与系统代理开启 |
| 公网测试 IP | 系统路由进入 Clash TUN,后续按规则决定出站 |
| 启智平台 | TUN 与显式代理路径均返回最终 HTTP 200,TLS 验证通过,浏览器显示认证页面 |
回退时,恢复修改前记录的设置即可:删除新增的单 IP 绕过项和直连规则,恢复原来的自定义或默认绕过列表模式;DNS 和子网路由也只恢复各自的原值,不要一律设为开启。原本已存在的 TUN 排除项和 IPv6 设置应保留。
这次最值得保留的排查习惯,是把 DNS 解析、系统路由、代理路径和应用响应 分别验证。一次 ping、一个 DIRECT 标签或一条正确路由都只覆盖其中一部分;同一 URL 的直连与代理对照,加上浏览器里的实际页面,才能把这类共存问题定位到具体环节。



