目录

Codex远程与双VPN小记

啊米诺斯!
啊米诺斯!
啊米诺斯!

今天来记一件很烦人、但是搞清楚之后又挺顺理成章的小事:
本机要开北大 VPN 连集群,同时又要用 Clash 让 Codex 访问 ChatGPT;远程环境里的会话动不动就断。

场景大概是这样的:

  • 本机 Mac:Ivanti 连 vpn.pku.edu.cn,才能 SSH 到校内机器(比如 xeon6
  • 本机还开着 Clash Verge,不然 ChatGPT / Codex 基本用不了
  • Codex 桌面端连着远程项目(工作目录在集群上)时,报错长这样:
stream disconnected before completion:
error sending request for url (https://chatgpt.com/backend-api/codex/responses)
1
2

本地项目会话有时是好的,远程一开就「正在重新连接」。人很裂。

# 问题到底出在哪

先把两条路拆开看,别混在一块:

你在干什么 流量实际从哪出去 需要谁
SSH / 改远程代码 / 跑集群命令 本机 → 北大 VPN → 校内机 Ivanti
Codex 调 ChatGPT API 发请求的那一侧 → chatgpt.com 能稳定出网的代理(Clash)

这里有个容易误会的点:桌面连着远程项目时,API 请求经常是在远程机器上发的,不是你以为的「全在本机发」。

所以远程会话挂了,很多时候不是「Codex 坏了」,而是:

xeon6 想访问 chatgpt.com
  → 集群自己出网?不太行 / 很不稳
  → 或者经 SSH 隧道回到 Mac,再从 Mac 出网
  → Mac 默认路由已经被 Ivanti 抢走了
  → ChatGPT 流量跟着钻进校园网 → 超时 / 流式中途断开
1
2
3
4
5

再叠一层 Clash 的 TUN:DNS 可能变成 198.18.x.x 这种 Fake-IP,SSH 到校内机时你会看到奇奇怪怪的地址,VPN 图标还显示「已连接」。那不是 VPN 没连上,是 Clash 把流量劫持走了

一句话:VPN 负责进校园,Clash 负责出 ChatGPT;两条路抢同一个默认出口时,长连接最先死。

# 试过但不太行 / 容易把本机搞乱的做法

这些坑我或多或少都踩过,写出来免得别人再绕:

  1. 狂改 Clash 的 TUN / 全局路由 / PAC
    能缓解一阵,但和 Ivanti 叠在一起时,本机网络很容易变得「不知道谁在接管」,回滚也麻烦。

  2. 只开 TUN,以为提示里写了「无须系统代理」就稳了
    Clash 那句是理想情况。macOS 上 ChatGPT / Codex 这种 Electron 应用,TUN 不一定抓得全;再叠加 DNS 污染,直连会打到奇怪 IP 上一直 SYN_SENT

  3. 远程写 HTTP_PROXY=http://127.0.0.1:7897
    7897 是本机 Clash 端口。远程机器上的 127.0.0.1 是远程自己,不是你的 Mac。除非 Clash 装在远程,否则这个地址是错的。

  4. SSH 动态反向代理:ssh -R 17890
    能把 SOCKS 听到远程,但流量回到 Mac 之后仍走系统默认路由。VPN 一开,ChatGPT 还是可能进校园网。短请求偶尔通,流式一长就断。

  5. 把本机网络改得「很正确」但不可恢复
    能用,但下次你只想正常上网时会哭。后面那套方案刻意把改动面收小。

# 最后比较稳、也相对好恢复的做法

思路很朴素:让远程 Codex 固定走「隧道 → 本机 Clash」,别让它自己瞎出网。

远程 Codex / codex-cli(xeon6)
  → socks5h://127.0.0.1:17890
  → SSH 反向转发到本机
  → 本机 Clash 混合端口(例如 7897)
  → 机场节点
  → chatgpt.com
1
2
3
4
5
6

对应两处改动:

# 1)本机:隧道接到 Clash,而不是动态 SOCKS

不要用裸的:

ssh -R 17890 xeon6
1

而是:

ssh -R 17890:127.0.0.1:7897 xeon6
1

含义是:远程连 127.0.0.1:17890,等于连到你 Mac 上的 Clash。
可以用一个小 keeper 挂着自动重连;挂掉了再拉起来就行。

# 2)远程:给 Codex 指到 17890

例如在远程写 ~/.codex/.env(没有就新建):

ALL_PROXY=socks5h://127.0.0.1:17890
HTTPS_PROXY=socks5h://127.0.0.1:17890
HTTP_PROXY=socks5h://127.0.0.1:17890
NO_PROXY=127.0.0.1,localhost,::1,.pku-dasys.cn,.local,.pku.edu.cn
1
2
3
4

然后重启一下远程的 codex app-server(或重开桌面远程会话),让进程吃到新环境变量。

自检可以很粗暴:

# 本机
nc -z 127.0.0.1 7897 && echo clash_ok

# 远程
curl -x socks5h://127.0.0.1:17890 -o /dev/null -w '%{http_code}\n' \
  -X POST https://chatgpt.com/backend-api/codex/responses \
  -H 'content-type: application/json' -d '{}'
# 看到 401/400 一类「到达了服务端」的响应,通常就说明链路通了
1
2
3
4
5
6
7
8

流式通不通,比只测一个 HTTP 码更关键;短请求通、长 SSE 断,多半还是中途路由/代理不稳。

# 为什么这样就会稳一些

  • 出口唯一:ChatGPT 固定进 Clash,不再跟 Ivanti 默认路由缠斗
  • 和本机能用的翻墙路径一致:节点、规则都复用你已经验证过的 Clash
  • 改动面小:主要动「SSH 转发目标 + 远程代理环境变量」,不必把本机系统代理/TUN 大卸八块
  • 远程用 codex exec / tmux 挂任务,也走同一条代理链

当然它不是魔法:本机 Clash 关了、隧道断了、机场节点挂了,远程照样会挂。这是依赖关系,不是玄学。

# 其他可能的路(各有代价)

  1. 在集群上直接装 Clash / mihomo
    最干净:远程自己出网,不依赖本机隧道。前提是机器能访问机场、你有权限常驻进程。很多校内机做不到。

  2. 需要远程时关 Clash TUN,只留系统代理;或反过来
    能减少 Fake-IP 坑,但双开场景下仍要小心默认路由。

  3. 能拆角色就拆
    只 SSH 时开 VPN;只在本机用 Codex 时开 Clash。简单,但做不到「一边连集群一边让远程会话稳定调 API」。

  4. 同步会话记录
    本地和远程的 ~/.codex/sessions/ 默认不共享。想在远程看到本机旧对话,得自己 rsync,不是代理问题。

  5. 模型本身过载
    链路通了仍可能失败,例如服务端 server_is_overloaded。那和 VPN/Clash 无关,换个模型或过会儿再试。

# 以后再遇到时,优先按这个顺序排

  1. 本机 Clash 混合端口还在听吗?
  2. ssh -R 17890:127.0.0.1:7897 这类转发还在吗?(不是裸 -R 17890
  3. 远程 ALL_PROXY / ~/.codex/.env 是否指向 socks5h://127.0.0.1:17890
  4. 远程 curl 经 17890 能否打到 chatgpt.com/backend-api/codex/responses
  5. 再考虑动 TUN / 系统代理 / 订阅规则——那是扩大爆炸半径的操作。

写这篇不是为了安利某套神配置,只是把「本地 VPN + Clash + 远程 Codex」这坨第一次理顺时的路径记下来。
下次再断,先别急着重装宇宙,先看隧道有没有接到 Clash。

上次更新: 2026/07/28, 10:01:41
最近更新
01
本科的最后一个月,我在想什么
06-10
02
Hopper里的TMA
06-04
03
MOE融合算子
05-27
更多文章>