TECH
从 Mirrored 到 NAT:CodeX/Copilot 在 WSL 场景下的双代理原理与实战
一次真实迁移复盘:为什么在 WSL NAT 网络与代码迁移到 Linux 盘后,必须同时配置 Windows 与 WSL 两套代理,以及如何稳定通过 OAuth 与流式请求。
Why:为什么这次“明明配好过”还是会翻车
先说背景:我之前已经把 CodeX 登录跑通,一直在用,当时 WSL 用的是 mirrored 网络模式,本机代理开在 127.0.0.1:<port>,整体难度不高。
后来因为容器与网络隔离需求,我把 WSL 调整到了 NAT 路径(同时看到 podman-usermode 默认路由),并且为了避免在 Windows 盘执行编译导致的磁盘 I/O 抖动和卡死,把代码仓库迁到了 WSL 的 Linux 文件系统里。
这两个变化叠加后,问题从“单点代理配置”升级成“多执行面代理一致性”问题:
- 插件登录面:VS Code 中的 CodeX/Copilot 需要稳定访问 OAuth 与流式接口。
- 命令执行面:构建命令在 WSL 内执行,CLI 与扩展后端都可能在 Linux 网络栈下发请求。
- 代理拓扑面:Windows 与 WSL 不再天然共享同一个
127.0.0.1语义,错误地复用旧配置会导致断流、超时、反复重连。
这篇文章的目标是把“为什么会这样”讲清楚,让后续每次切网络模式或换代理软件时,都能快速重建可用配置。
What:这次到底在解决什么问题(边界与成功标准)
这不是“某个插件突然抽风”,而是三个网络事实叠加:
- OAuth 是两段链路:浏览器登录成功,不代表扩展后端换 token 一定成功。
- WSL NAT 下地址语义变化:
127.0.0.1、WSL 网关、Windows 主机地址可能分别代表不同路径。 - CodeX/Copilot 有长连接与流式请求:比一次性 API 调用更容易暴露代理遗漏。
成功标准建议定义为:
- 浏览器可完成登录跳转,并能回调本地
localhost。 - VS Code 插件不再出现连续
Reconnecting...与stream disconnected before completion。 - WSL 终端中的 CLI(如
codex login status)与插件会话都能稳定工作。
如果只满足第 1 条,通常只是“表面可用”。
How:最小可用路径
第一步:先识别你现在是哪种网络拓扑
在 WSL 执行:
ip route
env | rg -i '^(http|https|all|no)_proxy='
看两个信息:
- 默认网关是否已经不是传统 WSL 网关(例如出现
podman-usermode)。 - 代理环境变量是否仍指向旧地址。
我在 2026-03-05 的现场快照是:
- 默认网关:
192.168.127.1(podman-usermode) - 生效代理:
http://172.27.176.1:7078 127.0.0.1:7078在 WSL 内不可达
这说明“沿用 mirrored 时代的 localhost 代理”在当前机器上不成立。
这里补一条经常被问到的问题:192.168.127.1 (podman-usermode) 不是“手工多配了一条代理”,而是当前 WSL distro 的默认出站网关。它会影响该 distro 内进程的出站路径,但不会直接改 Windows 全局代理,也不会强制改你服务的监听地址。
如果你的目标是“Windows 浏览器访问 Podman 暴露端口”,关键是端口映射与 Windows 侧访问路径是否打通,而不是把所有代理都改成网关地址。
第二步:把“谁发请求”对应到“谁的代理配置”
把代理配置按执行面拆开,不要混写:
- Windows VS Code 进程面:
User settings.json中的http.proxy系列配置。 - WSL 进程面:
HTTP_PROXY/HTTPS_PROXY/ALL_PROXY/NO_PROXY环境变量。 - WSL Remote 场景:确认扩展主机在 WSL 还是 Windows,再决定配置优先级。
核心原则是:谁发请求,就给谁配代理;不要指望“系统全局代理”自动覆盖所有进程。
第三步:用“可达性测试”替代“感觉能用”
在 WSL 里分别测试:
# 禁用代理直连(看真实裸连能力)
curl --noproxy '*' -I --connect-timeout 8 https://chatgpt.com
# 显式走代理(看代理路径是否打通)
curl -x http://172.27.176.1:7078 -I --connect-timeout 8 https://chatgpt.com
curl -x http://172.27.176.1:7078 -I --connect-timeout 8 https://auth.openai.com
我这次排查中,禁用代理访问 chatgpt.com 会超时,而显式代理可返回 HTTP 状态码(如 403/405)。这类状态码不代表业务成功,但代表链路已到达目标服务,是“网络层通了”的证据。
Principles:为什么必须双代理,以及为什么会出现“登录成功但仍断流”
原理 1:OAuth 成功了一半,不等于全链路成功
登录流程里至少有两个网络主体:
- 浏览器:负责账号登录与授权页交互。
- 扩展后端/CLI:负责拿授权码去换 token,并维持后续 API/流式请求。
浏览器可达,只能证明“浏览器那半段”可达。扩展后端若没走代理,仍会在 token exchange 或后续流式接口阶段失败。
原理 2:NAT 模式会让“localhost 心智模型”失效
在 mirrored 场景中,127.0.0.1 代理常常能直接复用;在 NAT 场景中,WSL 与 Windows 的本地回环可能不再等价。你必须根据当前路由与可达性测试重新确定代理入口,而不是继承旧教程。
原理 3:CodeX/Copilot 的流式链路更敏感
一次性请求偶尔还能“碰运气成功”,流式连接更容易暴露代理不稳定、TLS 检查、长连接中断等问题,所以你会看到:
Reconnecting... 1/5 ... 5/5stream disconnected before completion
这往往是“链路部分可达但不稳定”或“请求有一部分没走代理”的信号。
Operations:高频故障速查(给未来的自己)
症状 1:浏览器登录没问题,插件一直重连
先查:
- VS Code 发请求的执行面在哪(Windows 还是 WSL)。
- 对应执行面的代理是否独立配置。
- 同一会话里是否同时存在互相冲突的代理变量与
settings.json。
症状 2:WSL CLI 能登录,插件仍报断流
先查:
- CLI 与插件是否真的在同一网络命名空间里运行。
- VS Code Remote 窗口是否连接到了预期的 distro。
- 插件进程日志里目标 URL 是否与代理策略匹配。
症状 3:换成 NAT 后全部配置看起来都对,但就是不稳
优先做四件事:
- 重新跑一次
ip route与代理端口可达性检测。 - 明确一个主代理入口,删除历史遗留的备用地址。
- 完整重启 VS Code 与 WSL 会话,避免旧环境变量残留。
- 用“直连 vs 显式代理”双对照测试确认是否真打通。
与上一篇的关系
如果你还没看这两篇基础文,先读: Windows 网络受限环境下配置 CodeX / Cloud Code 的代理指南 AI Loop 本机开发环境实战:Windows + WSL + Podman 从 0 到 1
可以把三篇连起来理解:
- 基础篇回答“为什么 VS Code 代理不能只靠系统代理”。
- Podman 篇回答“为什么为了稳定本机开发回路,会把执行面迁到 WSL + 容器”。
- 这篇进阶篇回答“当执行面迁移到 WSL NAT 后,为什么必须做双代理与链路分层验证”。
结语
这次最有价值的收获不是某几条配置,而是一个判断框架:
- 先识别执行面。
- 再识别网络拓扑。
- 最后用可达性证据验证每一段链路。
只要这个框架在,网络模式从 mirrored 切到 NAT、代理工具从 A 换到 B,最终都只是“重新映射路径”的工程问题,而不是玄学问题。
评论