乐于分享
好东西不私藏

一次VSCode Remote SSH插件排查:本地能用,远程却403

一次VSCode Remote SSH插件排查:本地能用,远程却403
最近遇到一个问题。

本地VSCode里的ChatGPT/Codex插件可以正常使用,浏览器也能打开。但是只要在远程vscode里面使用chatGPT插件就一直报403。

我的链路大概是这样的:

VSCode打开Remote SSH远程窗口        ↓插件启用在远程环境        ↓登录请求从远程服务器发出        ↓远程服务器访问目标认证服务失败        ↓本地电脑正常,不代表远程窗口正常        ↓把远程环境的网络链路重新梳理        ↓确认插件所在进程拿到了正确配置        ↓登录恢复正常

最开始我改的是本地VSCode代理配置:

"http.proxy":"http://127.0.0.1:<local_port>","http.proxySupport":"override"

看起来没毛病,但我忽略了一行状态:

Extension is enabled on 'SSH: xxx'

这句话很关键,它说明插件启用在远程端。

这时候真正发起登录请求的,不是我本地电脑,而是远程服务器。

所以只改本地配置,完全没用。

Remote SSH场景里容易误判的地方就在这里:你在本地操作界面,但实际执行逻辑在远程机器上。

所以第一步不是继续改插件,而是先在远程服务器上确认网络链路:

curl -I https://your-target-service.example

如果远程服务器这里就失败,说明问题不在VSCode界面,也不在账号本身,而是远程环境访问目标服务这条路有问题。

后来我又用本地测试环境做了链路验证:

curl -x http://127.0.0.1:<remote_port> -I https://your-target-service.example

如果能看到类似:

HTTP/1.1 200 Connection established

说明远程环境已经能通过指定端口建立连接。

下一步要让远程服务器上有一个可用的本地端口,然后让远程环境里的请求走这个端口。

通用命令可以写成这样:

ssh -N-R <remote_port>:127.0.0.1:<local_port> user@remote-host

这条命令的意思是:

远程服务器 127.0.0.1:<remote_port>        ↓通过SSH反向端口        ↓转发到本地电脑 127.0.0.1:<local_port>

注意,这个终端窗口不能关,一关,远程服务器上的这个端口就断了。

然后在远程服务器上配置环境变量:

export http_proxy=http://127.0.0.1:<remote_port>export https_proxy=http://127.0.0.1:<remote_port>export HTTP_PROXY=http://127.0.0.1:<remote_port>export HTTPS_PROXY=http://127.0.0.1:<remote_port>export no_proxy=localhost,127.0.0.1,::1export NO_PROXY=localhost,127.0.0.1,::1

验证一下:

env | grep -i proxy

能看到对应变量,说明远程终端这层已经拿到配置了。

但这里还有一个坑,env有配置,但插件未必有

远程终端里执行:

env | grep -i proxy

输出正常,但插件还是可能失败。

原因是:终端环境好了,不代表VSCode Extension Host环境好了。

插件不是跑在你当前这个bash终端里。它跑在VSCode Remote的Extension Host进程里。

所以真正要查的是:

Extension Host这个进程有没有拿到对应环境变量。

一开始我用了这个命令:

ps eww -p $(pgrep -f "extensionHost" | head -n 1) | tr' ''\n' | grep -i proxy

结果查出来不对,我以为Extension Host一直没有配置。

后来发现,问题不在配置,而在我查错了进程。

我这台远程服务器是多人共用的。

上面有很多人的extensionHost进程。

我用:

pgrep -f "extensionHost" | head -n 1

拿到的很可能不是自己的进程,而是别人的进程。

这个判断一错,后面所有分析都会出问题。

正确做法是只查自己的进程:

for p in $(pgrep -u "$USER" -f extensionHost); doecho"========== PID=$p =========="  ps -o pid,ppid,args -p "$p"echo"--- env proxy ---"tr'\0''\n' < /proc/$p/environ | grep -iE 'proxy|<remote_port>' || echo"NO_PROXY_ENV"done

最后我查到属于自己的Extension Host进程,里面已经能看到对应环境变量。

到这里,链路才算闭环。

不是终端有配置就行,是插件所在的进程也要有配置。

每次手动export太麻烦,可以写进远程服务器的~/.bashrc:

cat >> ~/.bashrc <<'EOF'# Remote development proxy configexport http_proxy=http://127.0.0.1:<remote_port>export https_proxy=http://127.0.0.1:<remote_port>export HTTP_PROXY=http://127.0.0.1:<remote_port>export HTTPS_PROXY=http://127.0.0.1:<remote_port>export no_proxy=localhost,127.0.0.1,::1export NO_PROXY=localhost,127.0.0.1,::1EOFsource ~/.bashrc

然后确认:

env | grep -i proxy

如果远程窗口重连后还能看到这些变量,说明终端这层已经没问题。

我中间也试过Remote Settings。它对某些插件可能有用,但这次真正的判断标准不是设置页里有没有写配置,而是自己的Extension Host进程里有没有真正拿到配置。

以后再遇到类似问题,可以从这四步进行排查。

第一步,本地保持远程端口映射:

ssh -N-R <remote_port>:127.0.0.1:<local_port> user@remote-host

第二步,远程服务器测试链路:

curl -x http://127.0.0.1:<remote_port> -I https://your-target-service.example

第三步,确认远程终端有配置:

env | grep -i proxy

第四步,只查自己的Extension Host:

for p in $(pgrep -u "$USER" -f extensionHost); doecho"========== PID=$p =========="  ps -o pid,ppid,args -p "$p"echo"--- env proxy ---"tr'\0''\n' < /proc/$p/environ | grep -iE 'proxy|<remote_port>' || echo"NO_PROXY_ENV"done

这四步能确认三件事:

远程到本地端口通不通。远程终端有没有配置。真正运行插件的Extension Host有没有配置。