VPN 连接成功,而三个互不相关的工具在同一下午以三种不同方式失败。mitmproxy 不再看到请求,Chrome 拒绝调试器,Gradle 在绑定本地套接字时抛出BindException。共同猜测是隧道挡住了 localhost。这不可能:127.0.0.1在内核中处理,根本到不了网卡,更谈不上网关。

三层,三种不同的故障

随客户端改变的不是通往 loopback 的路由。它是本地代理、机器信任存储和包过滤器,每一个都破坏开发者工作流的不同环节。把症状归到正确的行,就是诊断的大部分:

SymptomLayerFirst check
Proxy sees no trafficLocal proxy or listenerss -lntp, ornetstat -anoon Windows
TLS error in one tool onlyTrust storeWhich CA bundle does that runtime read?
Local bind or connect refusedPacket filter or port ownerWho holds the port, and what rule drops it?

几乎所有这类报告都从最后一行开始往上查,这恰恰反了。隧道是唯一可证明看不到 loopback 流量的一层,因此最不该被怀疑。先看进程绑定了什么,再问它信任哪些证书,最后才看机器上的策略。

从监听器开始,而不是隧道

当127.0.0.1:8080上的代理停止接收流量时,三种情况之一发生了:它从未启动、绑定了别的端口,或者过滤器丢掉了 loopback 连接。列出正在监听的套接字一条命令就能回答,还能在两个工具争论谁先占用时显示端口归哪个进程。

路由查询返回本地地址,从另一个方向印证了同一件事。ip route get对 loopback 目标报告的是 loopback 设备,绝不会是隧道;当有人提议改路由来修复时,这句话很有用。

代理层:环境变量不等于配置

许多企业客户端运行 HTTP 代理,并期望应用使用它。HTTPS_PROXY在 shell 里设置只修好尊重它的命令行工具,对读取自己配置文件的应用毫无作用。Gradle、Selenium 和 gRPC 各自查自己的配置,在一个终端里导出的代理,对 IDE 启动的守护进程不可见。

容器工具是最常见的暴露点。容器的 loopback 是它自己的,所以127.0.0.1在容器内绝不代表宿主机的代理;需要宿主机地址或 host 网络设置。在 shell 里有效、在构建里失败的修复,通常属于这一行,而不是隧道。

信任层:工具读的是另一个存储

拦截是第二个变化,也制造了最容易误导人的错误。客户端对受检连接出示自己的证书,因此信任企业根证书的浏览器能成功,而自带 CA 存储的工具会失败。Python 的 requests 通过 certifi 是经典案例:操作系统存储可能完全正确,请求仍然失败,因为那个运行时根本不读它。错误文本很少提到拦截,所以这一行常被误诊为端点损坏。

比较端点实际出示的内容与工具验证的结果。openssl s_client显示服务器发来的证书链;工具自己的验证输出显示它做了什么判断。两者不一致时,REQUESTS_CA_BUNDLE或SSL_CERT_FILE就是那个运行时的杠杆;企业根证书应放进该运行时读取的存储,而不是只被部分工具尊重的全局位置。

修好 SSL 错误的版本变更,是关于信任与加密策略的证据,与 VPN 无关。让 SOAP 客户端恢复工作的 Python 降级,改变的是自带 CA 存储和默认 TLS 设置;网络路径并没有移动。

过滤器层:它也能丢弃 loopback

第三个变化是包过滤器,有些产品会安装规则,丢弃不进入隧道的流量。本地监听器也可能被波及。这会把正常套接字变成被拒绝或超时的连接,产生的错误看起来像应用缺陷,而不是机器策略。

端口归属是这一层的另一半。BindException意味着套接字无法打开,这与连接被拒绝是不同的失败。先查有没有别的进程占着端口,再去找丢弃本地流量的规则,并加上产品支持的最窄放行。两个工具抢同一个调试端口时,报错与策略拦截一模一样,而只有其中一个能靠改防火墙解决。

Chrome 的调试端口属于同一家族。浏览器启动了,驱动却到不了 DevTools 端点,报错只提 DevTools,不提网络。额外的启动参数和多余的驱动管理器会改变端口归属,所以删掉它们有时看起来修好了 VPN。

修复真正失效的那一层

修复失效的那一层,而不是隧道。无法访问的 loopback 监听器要修监听器或端口。不受信任的证书要把企业根装到该运行时真正读取的位置,并把例外保持得足够窄。丢弃本地流量的过滤器要加一条具体放行,而不是整个关掉,因为关掉也会重新打开它原本保护的一切。

重连 VPN 不会检验这些层中的任何一层。它只是把破坏它们的那个变更再跑一遍,所以同样三种故障每次重连都会回来,而每次让客户端保持不活动的重启之后又会消失。

相关文章