2026 年 9 月 23 日,家里云一台 Linux 主机出现外网访问异常。排查最终发现两个独立故障:sing-box 的 DNS 引导形成循环依赖,RouterOS 的策略路由又把透明代理回包送回了旁路由。
修复 DNS 后,显式 SOCKS 代理已经能访问 Google,但普通 HTTPS 请求仍然超时。直到修正 ROS 的回包路由条件,透明访问才恢复。这也是本次排查最有价值的对照实验。
本文省略真实公网节点、业务域名和所有认证信息,保留私网地址用于说明路径。配置片段针对本次环境,不是可直接覆盖现网的完整模板。
现网职责:ROS 分流,sing-box 提供代理出口
客户端地址为 192.168.10.32,默认网关是 ROS 192.168.10.254,DNS 指向 192.168.100.253。ROS 同时拥有代理网段的网关 192.168.100.254;sing-box 运行在 192.168.100.253,通过 nftables TProxy 接管被转发过来的 TCP/UDP。
|
|
这套设计的国内外分流发生在 ROS。进入 sing-box 的业务流量默认交给代理出口,因此不能仅凭 sing-box 没有中国大陆直连规则,就认定国内流量分流缺失。必须同时检查 ROS 的地址列表、mangle 和路由表。
第一个故障:DNS 要先连代理,代理又要先查 DNS
故障时 sing-box 的业务 DNS 是 1.1.1.1 DoH,通过 vless-reality 出口访问。问题在于:代理服务器使用域名,而它的域名解析器也指向了同一个 remote-dns。
|
|
日志给出了直接证据,以下节点域名已替换为示例:
|
|
由于客户端只使用 192.168.100.253 作为 DNS,循环导致国内外网站都可能卡在解析阶段。此时 sing-box 服务仍显示 active,所以“进程运行正常”并不能说明代理可用。
修复方式是添加一个不依赖代理的引导解析器,并明确让代理节点域名使用它。本次采用直连阿里 DNS 的 HTTPS 服务:
|
|
同时设置:
|
|
这些是需要合并到现有配置的字段说明。业务 DNS 的 final 仍保持 remote-dns,经代理查询;引导解析器只负责打通建立连接所需的解析依赖。该写法在本次 sing-box 1.13.3 环境中通过配置检查并实际运行。
更改前备份配置,执行 sing-box check 成功后重启。随后客户端 DNS 查询恢复,显式 SOCKS 访问 Google 返回 HTTP 200,但透明访问依旧连接超时,说明还存在第二个故障。
第二个故障:连接标记让回包再次进入代理路由
ROS 中有两条关键规则,简化后如下:
|
|
第一条给需要代理的连接打 conn-cross 标记;第二条将该连接中的包送到 cross 表。本次 cross 表只有一条默认路由,下一跳是 192.168.100.253。
问题在第二条:连接标记能够匹配连接双向的包,而路由标记规则没有排除返回内网的方向。
客户端发出的请求被正确送到了 sing-box。TProxy 在客户端这一侧生成的响应仍以目标网站 IP 为源地址、客户端 IP 为目的地址。该响应经 ROS 返回时,也匹配 conn-cross,于是再次被标记进入 cross。
|
|
在 100.253 抓包时,可以看到同一序列号、同一确认号的 SYN-ACK 在极短时间内反复出现;10.32 则一直重发 SYN。这与单纯的公网延迟或远端网站拒绝访问不同。
为什么已有绕过规则没有生效?
现网有一条按 src-address=192.168.100.253 绕过 mangle 的规则。它适用于代理主机使用自身 IP 发起的出站连接,但透明代理返回给客户端的包,源地址仍是网站 IP,因此不匹配。
另一条 dst-address-type=local 只匹配目的地址属于 ROS 自身的包。192.168.10.32 是客户端,不是 ROS 的本机地址,也不会命中。“本机地址”与“内网网段”在这里不能混为一谈。
最小修复:让内网目的地址走正常路由
本次在 MARK: Routing 规则上增加:
|
|
这样只有目的地址不在该内网范围内的包才继续打 cross 路由标记。回给 192.168.10.32 的响应不再匹配,便可使用主路由表的直连路由到达客户端。
执行前应导出 mangle 配置,并确认注释只对应目标规则:
|
|
确认目标唯一后修改:
|
|
192.168.0.0/16 覆盖本次需要返回的内网。其他环境应按实际 LAN、隧道和子网路由规划排除范围;它不包含 10.0.0.0/8、172.16.0.0/12、Tailscale 的 100.64.0.0/10,也不处理 IPv6。排除策略路由本身不会自动创建这些网段的可达路由。
修复中的一个字段陷阱
第一次添加排除条件后仍然不通。读取 ROS 实际配置才发现,条件被填到了地址列表字段:
|
|
Dst. Address List 接受的是地址列表名称,不会把这里的字符串直接解释为 CIDR 网段。它检查的是名为 192.168.0.0/16 的列表成员关系,无法实现原本想要的网段排除。
纠正方法是清除误填字段,再设置目标地址字段:
|
|
上述 unset 仅适用于本次误填状态。如果已有其他有效的地址列表条件,应先评估其用途,不能直接照搬清除。
修正后读取到的状态为:
|
|
复测:必须从真实客户端走完整链路
测试中显式禁用 curl 的环境代理,确保访问经过客户端的正常路由:
|
|
显式 SOCKS 作为对照:
|
|
本次观察结果如下,耗时是单次请求记录,不代表长期性能:
| 测试项目 | 仅修复 DNS 后 | 修正 ROS 字段后 |
|---|---|---|
| 客户端 DNS 查询 | 正常 | 正常 |
| 显式 SOCKS 访问 Google | HTTP 200 | 前一阶段已验证代理出口可用 |
| 普通访问 Google | 连接超时 | HTTP 200,约 1.08 秒 |
| 普通访问 YouTube | 连接超时 | HTTP 200,约 1.50 秒 |
| 普通访问 GitHub | 连接超时 | HTTP 200,约 1.45 秒 |
| 普通访问百度 | HTTP 200 | HTTP 200,约 1.33 秒 |
| 普通访问阿里云 | 连接超时 | HTTP 302,约 1.50 秒 |
阿里云的 302 表示本次 HTTPS 请求成功获得重定向响应,并非连接故障。各网站的访问结果也不单独证明它们命中了哪条 CNIP 规则;要确认某个目的 IP 的具体出口,还需对照地址列表、规则计数和抓包。
最终用户确认连通,独立复测也得到上述结果。无需清空整张连接跟踪表或重启整台路由器。
后续排查应保留的边界
这次不是 VLESS REALITY 协议本身失效,也不能归因于缺少 sing-box 国内直连规则。可复用的排查顺序是:先区分 DNS 超时和 TCP 连接超时,再用显式代理与透明访问做对照,最后检查回程是否被策略路由再次捕获。
ROS 负责地址分流,sing-box 负责代理出口,两者之间必须同时具备正确的请求路径和响应路径。服务进程正常、节点 TCP 端口开放、SOCKS 请求成功,都只验证了链路的一部分。最终验收应回到真实客户端,以不显式指定代理的请求为准。
参考资料:sing-box DNS over HTTPS、sing-box Dial Fields、RouterOS Mangle、RouterOS Packet Flow。