用 FRP 打通云服务器与本地 Ubuntu,让 Codex 远程调试本地硬件 也就是说Codex 可以方便地访问云服务器上的项目代码Petoi 机器狗和 STM32H747I-DISCO 却通过 USB 连接在本地 Ubuntu 上本地 Ubuntu 没有公网 IP云服务器无法直接 SSH 到它。如果不解决这个问题调试流程就会变成下面这样Codex 给出命令我复制命令粘贴到本地 Ubuntu 执行复制命令输出粘贴回 Codex 分析Codex 给出下一步这个流程非常痛苦。对于普通软件项目这样手动来回复制几次也许还能忍。但对于硬件调试来说情况完全不同。硬件调试往往需要频繁执行命令、观察输出、调整脚本、重新烧录、读取串口或 OpenOCD 状态。如果每一步都要手动复制粘贴效率会非常低也很容易出错。后来我通过 FRP 打通了云服务器与本地 Ubuntu 之间的反向 SSH 通道使 Codex 可以通过云服务器直接登录到本地 Ubuntu从而直接调试连接在本地 Ubuntu 上的硬件设备。这篇文章记录一下这个过程。问题背景我的实际开发环境大致如下Windows PC 可以通过 Codex / SSH 访问云服务器云服务器有公网 IP本地 Ubuntu 在局域网内没有公网 IPPetoi 机器狗和 STM32H747I-DISCO 通过 USB 连接在本地 Ubuntu 上。其中Windows PC 可以访问云服务器云服务器也有公网 IP。但是本地 Ubuntu 位于局域网中没有公网 IP。云服务器不能直接 SSH 到本地 Ubuntu。这就导致 Codex 虽然可以在云服务器上帮我分析代码、构建固件、生成命令但无法直接操作连接在本地 Ubuntu 上的硬件。而硬件调试又恰恰需要直接访问本地 Ubuntu例如lsusb ls /dev/ttyACM* openocd ... arduino-cli ... python scripts/xxx.py如果 Codex 无法直接执行这些命令整个调试体验就会退化成“人工搬运命令和输出”。为什么选择 FRP为了解决这个问题我需要一种方式让云服务器能够访问本地 Ubuntu。常见方案有几种给本地 Ubuntu 配置公网 IP在路由器上做端口映射使用 Tailscale / ZeroTier 这类组网工具使用 FRP 做反向隧道。我的场景下FRP 是一个很合适的选择。原因是云服务器有公网 IP可以作为 FRP server本地 Ubuntu 虽然没有公网 IP但可以主动连接云服务器一旦本地 Ubuntu 主动连上云服务器就可以把本地 SSH 端口反向暴露到云服务器Codex 已经可以访问云服务器因此也就间接获得了访问本地 Ubuntu 的能力。最终效果是Codex 仍然只操作云服务器但云服务器上的127.0.0.1:REMOTE_SSH_PORT会通过 FRP 转发到本地 Ubuntu 的 SSH 服务。从 Codex 的角度看它只需要在云服务器上执行ssh -p REMOTE_SSH_PORT ubuntu127.0.0.1就能登录到本地 Ubuntu。整体架构整个链路可以画成这样SSH访问 127.0.0.1:REMOTE_SSH_PORT主动连接云服务器转发到 127.0.0.1:LOCAL_SSH_PORTUSB / 串口 / OpenOCDWindows / Codex云服务器frpsfrpc本地 Ubuntu SSHPetoi 机器狗 / STM32H747I-DISCO其中frps运行在云服务器frpc运行在本地 Ubuntu本地 Ubuntu 主动连接云服务器云服务器监听一个本地端口例如REMOTE_SSH_PORT访问云服务器的127.0.0.1:REMOTE_SSH_PORT实际会被转发到本地 Ubuntu 的 SSH 服务。这里有一点很重要云服务器上的反向 SSH 入口只绑定到127.0.0.1而不是0.0.0.0。这样外部网络无法直接访问这个 SSH 隧道只有云服务器本机上的进程可以访问它安全性更好。云服务器上的 frps 配置云服务器上运行frps配置大致如下bindAddr 0.0.0.0 bindPort FRP_SERVER_PORT proxyBindAddr 127.0.0.1 auth.method token auth.token REPLACE_WITH_YOUR_TOKEN说明bindPort FRP_SERVER_PORT本地 Ubuntu 的frpc会连接云服务器的这个端口proxyBindAddr 127.0.0.1反向暴露出来的 SSH 端口只监听云服务器本机auth.token认证 token不能提交到 Git也不要写进公开博客。启动frps./frps -c frps.toml启动成功后云服务器会等待本地 Ubuntu 上的frpc连接。本地 Ubuntu 上的 frpc 配置本地 Ubuntu 上运行frpc配置大致如下serverAddr YOUR_CLOUD_PUBLIC_IP serverPort FRP_SERVER_PORT auth.method token auth.token REPLACE_WITH_YOUR_TOKEN [[proxies]] name local-ubuntu-ssh type tcp localIP 127.0.0.1 localPort LOCAL_SSH_PORT remotePort REMOTE_SSH_PORT说明serverAddr是云服务器公网 IPserverPort对应云服务器上的frps监听端口localPort LOCAL_SSH_PORT是本地 Ubuntu 的 SSH 端口remotePort REMOTE_SSH_PORT是云服务器上暴露出来的反向 SSH 端口由于云服务器上配置了proxyBindAddr 127.0.0.1所以REMOTE_SSH_PORT只会绑定在云服务器本机。启动frpc./frpc -c frpc.toml连接成功后云服务器上的frps日志里会出现类似信息client login info ... new proxy [local-ubuntu-ssh] type [tcp] success tcp proxy listen port [REMOTE_SSH_PORT]验证 SSH 隧道在云服务器上执行ssh -p REMOTE_SSH_PORT ubuntu127.0.0.1如果能登录到本地 Ubuntu就说明链路已经打通。也可以执行一个简单命令验证ssh -p REMOTE_SSH_PORT ubuntu127.0.0.1 hostname whoami pwd这样 Codex 就可以在云服务器上直接操作本地 Ubuntu 了。这对 Codex 调试硬件有什么帮助打通这条链路之后调试体验发生了很大变化。之前和现在的差异可以概括成下面这张图打通之后CodexSSH 到本地 Ubuntu直接执行命令直接读取输出继续分析并执行下一步打通之前Codex 生成命令我手动复制本地 Ubuntu 手动执行我复制输出粘贴给 Codex 分析这对硬件调试尤其重要。例如Codex 可以直接执行lsusb确认 STM32H747I-DISCO 是否被识别STMicroelectronics STLINK-V3也可以直接检查串口设备ls -l /dev/ttyACM*可以直接调用 OpenOCD 烧录 STM32openocd -f interface/stlink.cfg -f target/stm32h7x.cfg ...这样 Codex 不再只是“给建议”而是可以真正参与调试闭环。一个实际例子调试 STM32H747I-DISCO在这个项目里我曾经需要把一个 STM32H747I-DISCO 的 demo 烧录到板子上。板子连接在本地 Ubuntu 上但构建和代码分析主要在云服务器上完成。通过 FRP 打通之后Codex 可以直接在云服务器上编译固件把 ELF 或 BIN 传到本地 Ubuntu调用本地 OpenOCD烧录 STM32H747I-DISCO读取 OpenOCD 输出判断烧录是否成功如果程序没跑起来再读取 PC、寄存器、fault 状态继续分析。例如烧录成功时 OpenOCD 会输出Programming Finished Verified OK Resetting Target如果没有这条远程链路每一次烧录和排错都需要我手动在两个环境之间来回搬运信息。一个实际例子调试 Petoi 机器狗Petoi 机器狗也是类似情况。它连接在本地 Ubuntu 上云服务器无法直接访问串口或 USB 设备。打通 FRP 后Codex 可以直接在本地 Ubuntu 上检查设备、执行脚本、读取返回结果。这让调试变成了真正的远程协作观察设备状态、修改或执行脚本、分析输出、决定下一步动作都可以在同一条 SSH 链路里连续完成。对我来说这比手动复制粘贴命令高效太多。安全注意事项这种方案虽然方便但安全问题必须重视。我采用了几个基本原则第一FRP token 不提交到 Git。auth.token REPLACE_WITH_YOUR_TOKEN真实 token 只放在本地配置文件或环境变量中。第二云服务器上的反向 SSH 入口只绑定到本机。proxyBindAddr 127.0.0.1这样外部机器不能直接访问云服务器的REMOTE_SSH_PORT端口。第三本地 Ubuntu 的 SSH 仍然使用密钥认证。不要为了方便而开启弱密码登录。这件事给我的启发这次经历让我更深刻地意识到AI 编程助手在硬件项目里的能力强烈依赖于它能否进入真实调试闭环。如果 Codex 只能“看代码”和“给命令”那它更像一个高级问答工具。但如果它可以直接运行命令直接读取设备状态直接烧录固件直接分析 OpenOCD 或串口输出直接根据结果调整下一步那它就不再只是提供建议而是在真正参与工程调试。对于嵌入式和机器人项目来说这一点非常关键。因为这类项目的问题往往不只在代码里还在USB 设备是否识别