这份案例说明本仓库为什么会出现,以及我实际怎样使用它。
它是经过脱敏的个人单用户案例,不是要求读者照抄的生产模板。这里不会记录真实服务器 IP、域名、OAuth URL、token、Telegram 消息、Codex thread、私密配置或命令返回正文。
两条链路虽然入口不同,最后都会抵达服务器上的同一个核心:
MCPServer
↓
exec_vps(command, timeout)
↓
subprocess.Popen(command, shell=True, cwd="/root")
↓
VPS root shell
宿主 MCP 由 systemd 以 root 身份运行。它不是 SSH 的封装,也不是 Docker socket 的封装;它就是一个把 MCP 工具调用转换成宿主 shell 命令的薄层。
最初部署时,这个核心文件大约只有 1 KB。本仓库只抽取 exec_vps 本身,不包含实际系统里的其他工具。
实际路径:
Telegram 用户消息
↓
个人 AI gateway
↓
容器内 Codex app-server
↓ MCP server key: vps-admin(示例名)
Docker 私网 private-mcp(示例名)
↓
宿主 Docker bridge gateway:8798/mcp
↓
宿主 root MCPServer service
↓
exec_vps
个人 AI gateway 和 Codex 运行在同一个非 root 容器里。这个容器有自己的 workspace、数据卷和 Codex home,但没有得到下面这些宿主能力:
没有挂载宿主根目录
没有挂载 Docker socket
没有挂载 SSH 私钥
没有挂载整个宿主 home
容器进程不是 root
这样,普通工程任务仍受容器边界约束。只有当 Codex 明确调用 vps-admin.exec_vps 时,请求才会沿 MCP 私网抵达宿主 root 服务。
相比直接挂载 /、Docker socket 或 SSH key,这种方式让高权限入口保持为一个显式工具:可以单独启用、禁用、审计和撤销。
实际部署使用专门的 Docker bridge 网络。MCP 服务监听宿主 8798,但防火墙只允许该 bridge 和对应私网子网访问;没有为了容器调用增加公网端口。
Codex container
↓ allowed private bridge/subnet
host bridge gateway:8798
public Internet
↓ blocked
host:8798
教程里出现的网段只能作为示意。真实施工必须先运行 docker network inspect,确认 bridge、gateway 和 subnet,再生成精确防火墙规则。
这套个人环境只允许指定 Telegram 用户进入 gateway,并且其 Codex 运行策略允许工具直接执行。使用者已经明确理解并接受:一条允许进入系统的消息,可能最终触发真实 root 命令。
这不是普通读写权限,也不是“容器里随便跑跑”。Telegram allowlist、AI 提示词和 AGENTS 规则都不能把 root shell 变成低权限工具。它们只是入口控制和协作规则。
同样,allowlist 只能证明消息入口属于指定用户,不能让 Agent 后续读取的网页、Issue、仓库、日志或文档自动变成可信指令。外部内容里的文字只能作为数据;只有用户明确授权的任务范围可以触发 root 操作。
如果是多人系统、公开机器人或不受信任输入,不应直接复制这个组合。
Codex 容器和 MCP 服务在同一台宿主机。让请求先离开服务器、经过公网域名和 OAuth,再回到同一台机器,会增加不必要的反向代理、认证和外部网络依赖。
因此云端链路使用内部 Streamable HTTP endpoint;公网连接方式保留给真正位于服务器之外的客户端。
部署时没有用破坏性命令证明 root 权限,而是分层验收:
codex mcp list能看到示例 MCP server enabled;- 同一 gateway 容器中的一次临时 Codex 任务产生真实
mcp_tool_call; exec_vps执行只产生预先约定固定输出的无害命令,并核对结构化exit_code、stdout、stderr、timed_out与truncated;- gateway 整容器重启后恢复 healthy;
- 宿主和容器 health 均成功;
- gateway 的普通 HTTP 服务仍只绑定宿主 loopback;
- 最后再由真实 Telegram 消息进行一次无害人工冒烟。
无害冒烟可以使用:
id -u
pwd
printf 'mcp_probe_ok\n'不要用删除文件、重启 Docker、修改防火墙或触碰数据库来证明链路可用。
实际路径:
Windows 上的 Codex Desktop
↓
已安装的云服务器插件
↓
Desktop 托管的连接与认证
↓
远程 MCP 入口
↓
服务器上的 exec_vps
↓
VPS root shell
在 Codex 中,这个工具会显示为插件提供的 exec_vps(command, timeout)。除此之外,个人插件还可以暴露其他明确工具;它们与 root shell 共用同一插件入口,但每个工具仍有独立名称和参数。
本机链路不是把 VPS 的 SSH 私钥、root 密码或生产配置复制给每个 Codex 任务。连接信息和认证由 Codex Desktop / plugin connection 托管,任务只看到已经注册的工具定义。
因此公开教程中不需要、也不应该出现:
真实远程 MCP URL
OAuth client secret
access token / refresh token
Cookie
VPS root 密码
SSH 私钥
README 使用 SSH tunnel 作为公开仓库的默认入门路径,因为它不要求读者先搭建公网 OAuth MCP,也不会把无认证的 8798 暴露到互联网。
我自己的 Desktop 链路使用已经配置好的远程 plugin,是更完整的个人集成。二者的 MCP 工具语义相同:都是把 command 和 timeout 传给服务器的 exec_vps。
教程默认:Codex -> localhost -> SSH tunnel -> root MCP
个人本机:Codex Desktop -> plugin -> authenticated remote entry -> root MCP
如果读者只想让自己的 Codex 管理自己的 VPS,没有必要为了模仿个人案例而先建设插件和 OAuth。SSH tunnel 已经足够完成教程目标。
我通常会把任务分成两种:
诊断:先只读检查,给出证据和原因,不修改服务器
施工:明确允许修改的服务、文件和验证方式,不顺手碰其他系统
需要人工的地方,Agent 应停下来清楚说明:
- 人需要登录哪个官方页面;
- 人需要确认哪个目标或风险;
- 人只应提供什么非秘密信息;
- 完成后怎样告诉 Agent 继续;
- 如果失败,怎样保留恢复入口。
Agent 不应把“请把 root 密码或 token 发给我”当作方便的部署步骤。
| 场景 | 推荐路径 | 原因 |
|---|---|---|
| Codex 与 MCP 在同一台 VPS | Docker 私网 | 不需要绕公网,入口边界明确 |
| 个人电脑临时使用 | SSH tunnel | 部署最少,不公开 MCP endpoint |
| 个人电脑长期、产品化使用 | 受认证的 plugin / remote MCP | 客户端体验更完整,但需要额外认证与发布体系 |
无论选哪条,最终权限都没有变化:exec_vps 仍是任意 root shell。传输路径更私密、认证更完整,并不会降低命令本身的权限。
值得复制的不是某个固定端口或网段,而是下面的拆分:
普通 Agent runtime 保持受限
+
高权限能力通过一个显式 MCP 工具进入
+
网络入口单独限制
+
先做无害真实调用,再做端到端验收
+
秘密和生产地址不进入公开仓库
是否愿意让这个显式工具拥有 root 权限,是使用者必须自己作出的决定。