动态设置vscode remote的代理

背景

最近在尝试vscode的远程开发,远程主机是NUC,操作系统是Ubuntu。NUC会移动在不同地点使用,有的网络环境能自由访问外网,有的不能。日常开发go程序时,go get在后一种场景就需要设置代理。vscode可以设置远程服务的http_proxy,但是就缺乏灵活性,在前一种场景又得手动去掉代理。

所以如何自动判断要不要设置代理呢?

Linux环境变量

Linux下设置系统代理是通过设置http_proxy和https_proxy两个环境变量,进程在启动时如果这两个环境变量有值,进程访问网络时就会走代理。单独go get的场景来看,可以设置GOPROXY解决问题。但是每一个程序都单独设置代理,问题无穷无尽,而且难以维护。

正常情况下如何设置http_proxy和https_proxy大家都知道去哪设置,.bashrc或者.zshrc。但是对于vscode remote-ssh的情况下该去哪里设置呢?这里就涉及一个小知识点,进程树,Linux下进程之间有父子关系,子进程会继承父进程export的环境变量。所以只需要找到vscode在NUC上启动的父进程,在启动的过程中export就可以了。

ssh到NUC用以下命令可以找到vscode的进程,可以看到vscode的根进程是sh server.sh。用系统sh启动了一个脚本,后续的进程都是fork出来的子进程,比如go的language server,gopls。

$:ps axjf | less

bash的启动过程

Ubuntu上的sh不是bash而是dash,dash和bash的语法并不完全一致,但是不管是bash还是dash在执行时都会去执行/etc/profile文件。于是问题就很简单了,只需要加入shell脚本判断当前的IP网段是否是处于需要代理的环境,是就设置相应的环境变量。下面是脚本

IP=$(ip addr show wlo1 | grep -Eo inet (addr:)?([0-9]*\.){3}[0-9]* | grep -Eo ([0-9]*\.){3}[0-9]* | grep -v 127.0.0.1) if [[ ${IP%%.*} = "192" ]]; then export http_proxy=":7890" export https_proxy=":7890" else export http_proxy="" export https_proxy="" fi

进程的环境变量

脚本有了,如何确认功能正常,从表象上来看在受限网络环境下确实可以正常访问外网了。但是如何确认是环境变量设置成功了呢?Linux的/proc/$PID/environ可以看到正在运行中的进程的环境变量。为了好看一点可以用以下命令格式化

sed s:\x0:\n:g /proc/$PID/environ

经过我反复确认找不bash进程的https_proxy,而子进程gopls是有的,这就非常奇怪了。最后询问SRE大佬才明白,/proc/$PID/environ里只记录进程启动时的环境变量,而bash后来执行shell脚本export的环境变量时不在这个里面的。所以方法确实是符合预期的。要确认进程当前的环境变量有哪些,方法比较黑魔法,具体我就没做实验了。

参考

Profiles and RC Files | Linux Journal

proc(5) - Linux manual page (man7.org)