把本机代理稳定地借给内网实验室服务器
实验室服务器只能在校园网里访问,也没有直接出网的路。可我又希望它能装依赖、更新 VS Code 扩展,以及运行 Codex。最后用的办法并不复杂:让服务器把请求沿着 SSH 连接送回我的电脑,再由电脑上的 Clash/mihomo 代理转出去。
这篇记录的是最终能长期用的版本。最开始我只是临时开一条转发,关掉终端就断;后来又遇到 VS Code 还在访问旧端口、npm 看似卡死、已有隧道残留在服务器上等问题。把端口职责分开、让本机定时补齐隧道以后,事情才安静下来。
最后的结构
实验室服务器
Codex / npm / VS Code Server
|
| HTTP(S) 代理: 127.0.0.1:38081
v
SSH 反向端口转发 (-R)
|
v
Windows 本机 [::1]:7890
Clash Verge / mihomo
|
v
外网
这里故意用了两个不同端口:
- 本机
7890:Clash/mihomo 的代理端口。这个端口只留给本机代理程序。 - 服务器
38081:SSH 反向转发在服务器上创建的入口。服务器上的程序只访问它。
不要在服务器上直接写 127.0.0.1:7890。对服务器来说,这指向它自己,而不是我的 Windows 电脑。过去出现的 ECONNREFUSED 127.0.0.1:7890,就是这个问题。
先把最短路径跑通
假设本机 Clash 的 HTTP 代理监听在 [::1]:7890。在本机执行:
ssh -N -T \
-o ExitOnForwardFailure=yes \
-o ServerAliveInterval=30 \
-o ServerAliveCountMax=3 \
-R 38081:[::1]:7890 \
lab-server
lab-server 是写在本机 ~/.ssh/config 里的别名。真实服务器地址、用户名和跳板方式都不应该出现在公开文章里。
连上服务器后,先确认入口真的存在:
ss -ltn | grep 38081
curl -v -x http://127.0.0.1:38081 -I https://api.openai.com --max-time 20
看到 HTTP/1.1 200 Connection established,就说明代理隧道已经连通。对 api.openai.com 做 HEAD 请求时,后面出现 421 也不代表隧道坏了;前面的 CONNECT 成功和 TLS 握手成功才是这里要看的东西。
让服务器的命令都走 38081
我把下面内容放在服务器的 ~/use-codex-proxy.sh。这样不需要每次手敲一长串变量。
export HTTP_PROXY=http://127.0.0.1:38081
export HTTPS_PROXY=http://127.0.0.1:38081
export ALL_PROXY=http://127.0.0.1:38081
export http_proxy=$HTTP_PROXY
export https_proxy=$HTTPS_PROXY
export all_proxy=$ALL_PROXY
export NO_PROXY=localhost,127.0.0.1,::1
export no_proxy=$NO_PROXY
可以在 ~/.bashrc 里加一个小判断:只有 38081 正在监听时才加载这些变量。这样隧道没起来时,终端不会假装自己能出网。
if ss -ltn 2>/dev/null | grep -Eq '127\.0\.0\.1:38081|\[::1\]:38081'; then
source "$HOME/use-codex-proxy.sh"
fi
Codex 我另外包了一层命令,固定使用自己的 npm 全局安装目录和这份代理配置:
#!/usr/bin/env bash
set -e
export PATH="$HOME/.npm-global/bin:$PATH"
source "$HOME/use-codex-proxy.sh"
exec "$HOME/.npm-global/bin/codex" "$@"
把它保存为 ~/.local/bin/codex-server 并赋予执行权限后,日常直接运行:
codex-server
codex-server --version
这样也避开了服务器上可能同时存在多个 codex 的情况。更新后,正在运行的旧会话不会自动变成新版本,退出后再重新启动一次即可。
本机不打开终端,也能让隧道活着
手动运行 SSH 的问题很朴素:终端一关,服务器的代理入口就没了。我的做法是让 Windows 计划任务每分钟执行一次检查脚本:本机代理存在、而对应的 ssh -R 进程不存在时,才在后台启动一条新的连接。
检查脚本里真正重要的是这组参数:
ssh -N -T \
-o BatchMode=yes \
-o ConnectTimeout=8 \
-o ExitOnForwardFailure=yes \
-o ServerAliveInterval=30 \
-o ServerAliveCountMax=3 \
-R 38081:[::1]:7890 \
lab-server
ExitOnForwardFailure=yes 很重要。它能让脚本在端口已被占用或转发没有建成功时立即失败,而不是留下一条看似还活着、实际上已经不能用的 SSH 连接。
计划任务用隐藏方式启动 PowerShell 或 VBS 启动器即可。这样不需要常驻终端窗口,也不会隔一会儿弹出一个黑框。脚本只负责补齐缺失的隧道,不去碰 VS Code 自己建立的 SSH 连接。
VS Code 和 npm 也要改掉旧代理
这次最绕的一点在这里:终端里的 Codex 能连上,并不表示 VS Code Server 也会连上。VS Code Server 有自己的设置;npm 也可能在 ~/.npmrc 里保留旧地址。它们还指向 7890 时,会反复报错并拖慢扩展或 CLI 更新。
服务器上的 VS Code 设置文件通常在:
~/.vscode-server/data/Machine/settings.json
把里面的代理改成服务器端口:
{
"http.proxy": "http://127.0.0.1:38081"
}
npm 同样要检查:
npm config set proxy http://127.0.0.1:38081
npm config set https-proxy http://127.0.0.1:38081
npm config get proxy
npm config get https-proxy
改完后断开并重新连接 VS Code Remote,让已经启动的远程服务重新读取设置。日志里再出现 connect ECONNREFUSED 127.0.0.1:7890,就继续搜索这两个配置文件,不要去抢占本机的 7890。
如何判断问题在哪一层
服务器上 38081 没有监听
ss -ltn | grep 38081
没有输出时,先检查本机的 Clash 是否在运行,再检查后台 SSH 进程或计划任务日志。此时改服务器代理变量没有用,因为入口根本不存在。
能连到 38081,但 CONNECT 一直不返回
这种情况常见于一个残留的旧隧道还占着端口。不要急着杀掉所有 sshd 进程,尤其是在 VS Code 正连着服务器的时候。换一个新的服务器端口,例如 38081,并把服务器配置统一改过去,通常比误断正在工作的远程会话稳妥。
npm 看起来没有任何进度
npm 下载原生二进制包时可能安静很久。先看日志和版本,而不是立刻反复 Ctrl+C:
ls -t ~/.npm/_logs/*-debug-0.log | head -n 1
npm ls -g @openai/codex --depth=0
codex-server --version
我这次更新时,大文件下载用了两分多钟,最后 npm 仍然是正常退出。安装成功后,新开的 Codex 会话才会用到新版本。
收尾:一份小检查表
# 本机:先确认 Clash 还在监听
curl.exe -x http://127.0.0.1:7890 -I https://api.openai.com --max-time 15
# 服务器:确认反向入口和外网代理
ss -ltn | grep 38081
curl -I -x http://127.0.0.1:38081 https://registry.npmjs.org/ --max-time 20
# 服务器:确认 Codex 与 npm
codex-server --version
npm config get proxy
这套方案的边界也很清楚:只让服务器访问自己的 127.0.0.1:38081,不把代理开放到内网;不在公开文档写真实服务器地址、账号、认证文件或本机代理订阅。它只是把我已经能访问的本机网络,借给一台我有权限使用的内网服务器。