别把本机 SSH 交给远程 Agent
远程跑 Codex,最烦的不是模型慢,而是断线重连后 Git 认证失效。ChatGPT Desktop 与 Codex CLI 的解法是把本机 SSH Agent 套接字续接到远端常驻进程。本文拆开这套机制,说明它为什么方便,也说明它把哪些权限放大了,最后给一套可复现的收口步骤。
原文:https://hifox.com/blog/chatgpt-desktop-codex-ssh-agent-forwarding-security-risk/
问题:断线之后,常驻进程还拿着旧套接字
标准 OpenSSH 的 Agent Forwarding 会在每次会话建立时,在远端生成临时 Unix 套接字,并把 SSH_AUTH_SOCK 指向它。首次拉起的 codex app-server 和它重建环境的子进程,会一直记住这个路径。
笔记本休眠、网络抖动、SSH 会话退出,临时套接字随之销毁。你再连上去,环境变量是新的,常驻进程手里的路径是旧的,git fetch 和 git push 全部失败。
PR #28131 增加的 --forward-ssh-agent,就是为解决这个摩擦:代理每次重连时检查当前 SSH_AUTH_SOCK 是否为存活套接字,然后通过临时软链加 std::fs::rename 原子替换到固定路径 ~/.codex/app-server-control/forwarded-ssh-agent.sock。桌面端拉起远端服务时,直接挂载这个稳定路径。
SSH_AUTH_SOCK=~/.codex/app-server-control/forwarded-ssh-agent.sock
nohup codex app-server --listen unix://
代价是:原本只在人工终端会话期间有效的签名通道,变成了远端自治 Agent 随时可调用的长期入口。
图 1:本机 ssh-agent 经固定软链接续接到远程常驻进程图 1:签名入口如何续接到远端常驻进程
三类越权,不只是「多推一个分支」
私钥没有落盘,不代表风险不存在。攻击者要的不是私钥文件,而是「让 ssh-agent 替我签名」的能力。
攻击面触发方式后果命令黑名单跨仓库篡改仓库内 Issue、README、构建脚本植入提示词注入用本机身份向名下任意私有仓库强推难拦截,可绕脚本绕过审阅直推Agent 自行决定推送分支未过 Code Review 就改写远端分支无法拦截横向移动多人共用开发机、多容器共驻、宿主机高权进程同 UID 进程冒用身份登录内网其他机器完全无效
第三类最容易被忽略。它不经过 git,也不经过 shell 命令,而是直接连套接字。只维护一份「禁止 ssh」「禁止 force push」的黑名单,拦不住这种调用。
四步收口:从全局身份退到单仓短效凭证
图 3:四步收口顺序与暴露面逐步收窄示意图 3:四步收口,暴露面逐步收窄
第一步:默认禁转,隔离专用 agent
先确认最终生效配置,而不是只看配置文件里写了什么:
ssh -G your-remote-devbox | grep -i forwardagent
若输出 yes,在 ~/.ssh/config 末尾锁死默认值;确需转发时,只给跳板机开一个受限实例:
Host bastion-jump
HostName jump.internal.example.com
ForwardAgent yes
IdentityAgent ~/.ssh/agent-restricted.sock
IdentitiesOnly yes
Host *
ForwardAgent no
IdentitiesOnly yes
第二步:让签名必须经过人
用 OpenSSH 8.2+ 的 FIDO2 硬件密钥,加载时加 -c 启用确认约束;macOS 上可以用 Secretive 把密钥放进 Secure Enclave,绑定 Touch ID。每次签名都要物理确认,后台静默推送就失去了土壤。
第三步:远端只拿单仓、短效凭证
在远端生成专用 Deploy Key,只填到目标仓库的 Deploy keys,按需设只读或单仓可写。需要写权限时,用 GitHub App 的 Installation Token,默认 60 分钟过期,只授 contents:write。这样即使凭证泄露,爆炸半径也是一个仓库、一小时。
第四步:把检出与推送移出 Agent 沙箱
前三步在减少暴露面,但没有回答一个问题:如果远端机器本身就是共享的,凭证放在哪里都不安全。
更彻底的做法是把「代码托管凭据」和「Agent 执行环境」分开。HiFox 的做法是:任务跑在专用远程电脑上,hifox repo checkout 在本地服务缓存中检出独立 worktree,Agent 只在这个目录里改代码;完成后 hifox repo push 把专用分支推回远端,再由人审阅合并。Agent 不需要个人全局 SSH_AUTH_SOCK,也拿不到组织级托管凭据。
这不是装一个插件就完事,落地时至少要做三件事:
给共享任务分配专用远程电脑,不要复用个人开发机。
每个任务一个 worktree 和分支,避免多个 Agent 互相覆盖。
把「推送后等人审阅」写进流程,禁止 Agent 直接合入主分支。
图 4:HiFox 电脑页展示每台电脑的 Runtime 与并发任务数图 4:HiFox 电脑页的 Runtime 与并发任务
验收清单
ssh -G | grep -i forwardagent输出为no。远端
echo $SSH_AUTH_SOCK不再指向个人 agent;ssh-add -L列不出个人全部公钥。远端只有目标仓库的 Deploy Key 或 60 分钟令牌。
推送落在专用分支,合并前有人审阅。
小结
这套机制真实解决了长连接场景的认证摩擦,但它的默认值把「一次会话授权」变成了「常驻全局授权」。最稳的顺序是:先关默认转发,再上逐次确认,再收窄到单仓短效凭证,最后把凭据移出 Agent 沙箱。便利可以保留,签名权不必一起交出去。
