Skip to content

Repository files navigation

codex-dual-ssh-proxy

Validate repository Latest release Scripts and examples: MIT Documentation: CC BY 4.0

一份面向 macOS、远程 Linux 服务器、Codex Desktop 与 VS Code Remote-SSH 的三别名隔离教程。目标是在不修改、不关闭、不复用当前 Codex Desktop 活跃 SSH 连接的前提下,为 VS Code Remote Codex 建立独立的 SSH RemoteForward 代理通道,并提供一个完全不转发端口的维护入口。

本仓库只包含 Markdown、配置模板和只读 Shell 诊断脚本。请先通读并替换占位符,不要直接粘贴整份个人 SSH 配置。

适用场景

典型环境如下:

  • Mac 上的 HTTP 代理监听 127.0.0.1:<LOCAL_PROXY_PORT>
  • Codex Desktop 通过 SSH 登录远程服务器,并需要服务器侧的回环代理端口;
  • VS Code Remote-SSH 连接同一台服务器,远端 Codex 扩展也需要代理;
  • 两个代理通道应能分别重启、诊断和回滚;
  • 普通维护 SSH 不应意外继承任何端口转发。

不适用于 SOCKS-only 代理、需要服务器端 GatewayPorts yes 的公网监听,或希望多个客户端共享同一个 SSH master socket 的方案。

与 codex-over-easytier 的区别

本教程受 sunshinerobot/codex-over-easytier 的网络思路启发,但解决的问题和实现边界不同。

项目 本仓库 上游项目
重点 同一台 Mac 到同一服务器的 Desktop、VS Code、维护 SSH 三别名隔离 通过 EasyTier 等网络条件运行远程 Codex
代理通道 Desktop 与 VS Code 各自一个 RemoteForward 以其仓库当前文档为准
连接复用 三个别名均禁用 ControlMasterControlPathControlPersist 以其仓库当前文档为准
维护入口 codex-shell 强制 ClearAllForwardings yes 非本教程比较重点
发布内容 脱敏模板、说明和只读诊断脚本 独立项目

本仓库不是上游项目的分支,也不复制其配置;详见 ATTRIBUTION.md

三别名、双代理通道架构

flowchart LR
    P["Mac HTTP proxy<br/>127.0.0.1:&lt;LOCAL_PROXY_PORT&gt;"]

    subgraph M["Mac SSH clients"]
        D["codex-desktop<br/>no SSH multiplexing"]
        V["codex-vscode<br/>no SSH multiplexing"]
        S["codex-shell<br/>ClearAllForwardings yes"]
    end

    subgraph R["Remote Linux server"]
        DP["127.0.0.1:&lt;DESKTOP_REMOTE_PORT&gt;<br/>Desktop-only proxy endpoint"]
        VP["127.0.0.1:&lt;VSCODE_REMOTE_PORT&gt;<br/>VS Code-only proxy endpoint"]
        DC["Codex Desktop remote process"]
        VS["VS Code Server and remote Codex"]
        SH["Maintenance shell<br/>no forwarding listener"]
    end

    P --> D --> DP --> DC
    P --> V --> VP --> VS
    S --> SH
Loading

codex-dual-ssh-proxy 中的 “dual” 指两条代理通道;第三个别名是无转发维护通道。

端口与占位符

仓库只使用以下六个环境相关占位符:

占位符 含义 示例
<SERVER_IP> SSH 可达的服务器地址 使用你自己的地址
<SERVER_USER> 服务器 SSH 用户名 使用你自己的用户
<PRIVATE_KEY> Mac 上现有私钥路径 仅填写路径,不提交密钥
<LOCAL_PROXY_PORT> Mac HTTP 代理端口 7890
<DESKTOP_REMOTE_PORT> 服务器上的 Desktop 回环端口 17890
<VSCODE_REMOTE_PORT> 服务器上的 VS Code 回环端口 17892

端口可以自行更换,但 <DESKTOP_REMOTE_PORT><VSCODE_REMOTE_PORT> 必须不同。模板使用显式回环绑定,因此端口只应监听服务器的 127.0.0.1,而不是公网接口。

数据方向容易混淆:RemoteForward 127.0.0.1:17892 127.0.0.1:7890 表示服务器访问 127.0.0.1:17892 时,流量经 SSH 回到 Mac 的 127.0.0.1:7890

安全底线

整个迁移过程遵守以下底线:

  1. 不在 Desktop 活跃工作期间关闭、重连或接管它的 SSH 会话。
  2. 不执行 pkill sshkillall ssh 或任何按名称批量结束 SSH 的命令。
  3. 不执行 codex logout,不删除 ~/.codex,也不发布 ~/.codex/auth.json
  4. 不把代理写进服务器全局 shell 配置;VS Code 代理只进入 VS Code Server 专用环境。
  5. 不提交私钥、密码、EasyTier 密钥、Clash 订阅、OpenAI token、GitHub token、真实公网地址或完整个人 SSH 配置。
  6. 先备份精确目标、检查有效配置,再安排需要重连的步骤。

仓库中的三个脚本只读取监听、环境和 ssh -G 结果,不修改配置、不结束进程、不建立持久连接。

前置检查

1. 记录现状,不改连接

先保存正在进行的 Codex 工作,并在另一个本地终端检查原 Host 的有效配置。把命令末尾替换成你当前使用的 Host 名:

ssh -G your-current-host | awk '
  $1 == "hostname" ||
  $1 == "remoteforward" ||
  $1 == "controlmaster" ||
  $1 == "controlpath" ||
  $1 == "controlpersist" ||
  $1 == "exitonforwardfailure"
'

ssh -G 是静态有效配置检查,不证明远端监听已经建立;它还可能触发个人配置中的 Match exec。先审阅自己的 SSH 配置,再运行仓库验证脚本。

2. 检查 Mac 代理

./scripts/diagnose-local.sh 7890

普通用户运行 lsof 可能看不到由特权代理核心持有的监听,因此脚本还会交叉检查 netstat 和 TCP 连通性。

3. 检查服务器端口

在服务器维护终端中只读检查候选端口:

ss -lnt | awk '$4 ~ /:17890$|:17892$/'

结果分三种情况:

  • 端口空闲:可作为新通道候选;
  • 17890 已由当前 Desktop 通道监听:保持不动,先只添加 VS Code 与维护别名;
  • 端口被无关进程占用:选择其他未占用端口,不要结束未知进程。

还需确认服务器 SSH 服务允许 TCP forwarding。若你无权查看或修改 sshd 配置,请联系管理员,不要自行绕过策略。

配置三别名

1. 备份精确配置文件

以下命令只创建一份带时间戳的备份,不会激活新配置:

backup_path="$HOME/.ssh/config.backup.$(date +%Y%m%d-%H%M%S)"
cp -p "$HOME/.ssh/config" "$backup_path"
printf 'Backup: %s\n' "$backup_path"

然后只从 examples/ssh-config.example 复制相关 Host 块,替换六个占位符。不要用模板覆盖整份 ~/.ssh/config

2. 保留原 Host,先增加两个安全入口

如果 Desktop 正在使用原 Host:

  1. 保留原 Host 块和活跃连接不变;
  2. 先增加 codex-vscode,只使用 <VSCODE_REMOTE_PORT>
  3. 增加 codex-shell,其中没有 RemoteForward,并设置 ClearAllForwardings yes
  4. 运行静态检查;
  5. 先通过 codex-shell 验证普通登录,再让 VS Code 连接 codex-vscode

若原 Host 名已经是 codex-desktop,不要在文件后面再添加同名块。OpenSSH 对重复选项通常采用先取得的值,后面的块未必能覆盖前面的值。请在安排好 Desktop 重连窗口后,精确更新原块。

3. Desktop 迁移放到维护窗口

完成 VS Code 独立通道验证后,再安排 Desktop 迁移:

  • 保存并确认 Desktop 工作已完成;
  • 精确修改 Desktop 对应 Host 块,使其只拥有 <DESKTOP_REMOTE_PORT>
  • 设置 ControlMaster noControlPath noneControlPersist no
  • 设置 ExitOnForwardFailure yes
  • 正常退出该应用或会话后再重连,不使用批量结束命令。

修改 ~/.ssh/config 不会自动改变已经建立的 SSH 进程;新设置只对后续连接生效。

4. 为什么新版不使用“不同 ControlPath”

旧式双通道方案可以给 Desktop 与 VS Code 分配不同 ControlPath,但仍需理解 master socket 的生命周期。三别名新版采用更直接的边界:

ControlMaster no
ControlPath none
ControlPersist no

三个别名都不复用已有 master,因此不存在跨别名借用 socket 的路径。若旧配置仍有 master socket,只在确认没有依赖会话、并保存工作后,使用精确 socket 进行检查和优雅退出:

ssh -S /exact/path/to/socket -O check your-current-host
ssh -S /exact/path/to/socket -O exit your-current-host

不要把路径换成通配符,也不要用进程名批量结束 SSH。若无法确认 socket 的归属,保留它,等待原会话正常结束。

5. 验证三个有效配置

替换端口参数后运行:

./scripts/verify-three-aliases.sh \
  codex-desktop codex-vscode codex-shell \
  7890 17890 17892

脚本要求:

  • Desktop 恰好一个回环 RemoteForward,指向 Mac 代理;
  • VS Code 恰好一个不同的回环 RemoteForward,指向同一 Mac 代理;
  • 维护别名没有转发且 ClearAllForwardings yes
  • 三个别名均不使用 SSH multiplexing;
  • 两个代理别名均设置 ExitOnForwardFailure yes

VS Code Server 专用代理环境

不要把代理变量直接追加到服务器的 ~/.bashrc~/.profile 或系统环境。把 examples/server-env-setup.example 中的标记块合并到服务器的:

~/.vscode-server/server-env-setup

用真实值替换 <VSCODE_REMOTE_PORT><SERVER_IP>,并保留:

unset ALL_PROXY
unset all_proxy

这样 VS Code Server 及其远端扩展使用 http://127.0.0.1:<VSCODE_REMOTE_PORT>,普通维护 shell 不会被教程主动注入代理。该文件可能包含你已有的环境设置,只合并标记块,不要整文件覆盖。

VS Code 本地与远程设置

examples/vscode-settings.example.json 是有效 JSON 的分组示例,不是可整体粘贴的 settings.json。把各内层字段合并到对应设置范围:

  • 本地 User 设置:为 codex-vscodecodex-shell 指定远程平台;
  • Remote [SSH: codex-vscode] 设置:如确有需要,令远端 VS Code HTTP 流量使用 http://127.0.0.1:<VSCODE_REMOTE_PORT>
  • 本地 VS Code 若不能自动使用 Mac 代理,可选用 http://127.0.0.1:<LOCAL_PROXY_PORT>
  • 只有遇到确切的 PendingMigrationErrornavigator 兼容性报错时,才在本地和远端设置 "extensions.supportNodeGlobalNavigator": true。问题消失且升级验证通过后可移除,不应把它视为永久必需项。

设置入口:命令面板中的 Preferences: Open User Settings (JSON);连接远端后使用 Preferences: Open Remote Settings (JSON)

重启顺序与验收

按照通道逐个验收,不要同时重启所有客户端。

  1. 保持现有 Desktop 连接不动。

  2. 使用 ssh codex-shell 验证维护登录;服务器上不应新增教程端口。

  3. 使用 VS Code 的 Remote-SSH: Connect to Host... 连接 codex-vscode

  4. 若修改了 server-env-setup,通过命令面板执行 Remote-SSH: Kill VS Code Server on Host...,只选择 codex-vscode,然后重连。

  5. 在 VS Code 远端终端执行只读诊断:

    ./scripts/diagnose-remote.sh 17892 https://github.com
  6. 检查服务器监听只在回环地址:

    ss -lnt | awk '$4 ~ /127\.0\.0\.1:17890$|127\.0\.0\.1:17892$/'
  7. 验证 VS Code 远端 Codex 能发起请求,并确认 Desktop 仍正常。

  8. 最后在计划窗口中迁移并重连 Desktop,再分别复测两个端口。

验收失败时先停止下一步,不要为了“清理”而结束其他 SSH 进程。参见 故障排查

文件与聊天会话的边界

Desktop 与 VS Code 可以在服务器上打开同一工作目录,因此磁盘文件通常可见于两端;但同时编辑同一文件仍可能互相覆盖,应依赖 Git、编辑器冲突提示或团队约定协调。

Codex 聊天、任务上下文、认证状态、扩展本地状态和未保存编辑器缓冲区不保证在 Desktop 与 VS Code 之间同步。SSH 通道隔离只解决网络和进程边界,不提供会话同步。

常见问题

为什么服务器端口监听了,但代理仍不可用?

RemoteForward 监听存在只说明 SSH 建立了端口。还要确认 Mac 的本地代理端口可达、代理协议是 HTTP、远端环境变量指向正确的 VS Code 端口。分别运行两个诊断脚本。

为什么 lsof 没输出,TCP 测试却成功?

macOS 上代理核心可能由不同权限的进程持有,普通用户的 lsof 会漏报。以 netstatnc 的交叉结果为准,不要因为单个空结果就修改代理。

为什么 ssh -G 显示额外的 RemoteForward

检查更早出现的通配 Host 块、Include 文件和重复别名。验证脚本故意要求代理别名只有一个转发,避免继承未知通道。

为什么设置了 ControlPath nonessh -G 没显示该行?

OpenSSH 可能在有效配置输出中省略值为 noneControlPath。验证脚本把“未解析出 ControlPath”与 ControlMasterControlPersist 都已禁用结合判断。

能让两个通道共用同一个服务器端口吗?

不能。第二个连接通常会因地址已占用而失败,或者误用另一个客户端的生命周期。选择两个不同端口。

可以让维护别名临时转发吗?

不要修改 codex-shell 的安全语义。临时需求应创建另一个明确命名、明确端口、可单独审计的 Host,而不是让维护入口变得含糊。

更多症状与检查顺序见 docs/troubleshooting.md

回滚

回滚入口是 docs/rollback.md。核心原则是按阶段撤回新增内容:先停止让 VS Code 建立新连接,再移除 VS Code Server 专用环境块和新增别名;不要触碰仍在工作的 Desktop 会话。若已经迁移 Desktop,则在维护窗口中从精确备份恢复其 Host 块,并正常重连验证。

来源与许可证

本教程不隶属于 OpenAI、Microsoft、Apple 或上游项目。产品行为可能随版本变化;涉及 Codex IDE 的当前能力,请以 OpenAI Codex IDE 官方文档 为准。

About

Three-alias SSH isolation guide for Codex Desktop, VS Code Remote-SSH, and maintenance shells.

Topics

Resources

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages