mitmproxy、DevTools、Gradle:VPN 背后的三层
loopback 永远不会进入隧道。把症状归入监听器、信任存储或包过滤器,并修复真正失效的那一层。
VPN 连接成功,而三个互不相关的工具在同一下午以三种不同方式失败。mitmproxy 不再看到请求,Chrome 拒绝调试器,Gradle 在绑定本地套接字时抛出BindException。共同猜测是隧道挡住了 localhost。这不可能:127.0.0.1在内核中处理,根本到不了网卡,更谈不上网关。
三层,三种不同的故障
随客户端改变的不是通往 loopback 的路由。它是本地代理、机器信任存储和包过滤器,每一个都破坏开发者工作流的不同环节。把症状归到正确的行,就是诊断的大部分:
| Symptom | Layer | First check |
|---|---|---|
| Proxy sees no traffic | Local proxy or listener | ss -lntp, ornetstat -anoon Windows |
| TLS error in one tool only | Trust store | Which CA bundle does that runtime read? |
| Local bind or connect refused | Packet filter or port owner | Who 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 不会检验这些层中的任何一层。它只是把破坏它们的那个变更再跑一遍,所以同样三种故障每次重连都会回来,而每次让客户端保持不活动的重启之后又会消失。