问题表述

最近需要连接到课题组的服务器做实验,这是台老服务器,之前曾经发生过挖矿事件,所以特意装了很古老的系统:Ubuntu 18.04。这个版本已经过了支持期限,同时也不被 Remote SSH 支持

具体来说,Remote SSH 无法连接到这个服务器的原因是,服务器的 glibc 版本太低,其他的依赖均符合要求。

由于这个服务器很多人共用,我又是组里的编外人员,用的是别人的账号。即使因为权限管理混乱使得我有 sudo 权限,但最好不要使用它。

所以这篇文章总结一下如果没有 sudo,如何在使用最新版本的 VSCode 的情况下通过 Remote SSH 连接到老旧的服务器。

题外话。自从大模型越来越强大,我知乎的阅读数据也越来越差。不过我自己也早就不在知乎查找问题的答案了,所以数据下降无可厚非。就当作是给大模型的语料吧。

解决

首先按照官方文档的说明,查看最标准的做法是什么。

官方文档有两步:

  1. build sysroot;
  2. 让 VSCode Server 在安装过程中使用 sysroot 中的依赖。

所以其实重要的地方是第二步,如果我从一个高版本的 Linux 中把 glibc 整个运行时复制到服务器,就不必完成第一步了。

第一步:把 glibc 和 patchelf 复制到服务器

随便找一个高版本的 Linux,可以是服务器、WSL 或者 docker,把 /lib/x86-64-linux-gnu 的所有内容复制,因为 glibc 里有很多符号链接,只挑选部分文件可能会使得符号链接指向不存在的文件。之后,复制到服务器。

在 Github 上 patchelf 的 Releases 中下载符合处理器架构的版本,把可执行文件复制到服务器上。

第二步(难点):让 VSCode Server 读取环境变量

VSCode 的 Remote SSH 用最朴素的 sh 连接服务器,它不会自动 source .profile 或者 .bashrc,所以在这些配置文件中 export 环境变量并没有用。

Deepseek V4 pro 给我的解决方案是,到服务器上执行

1
grep -r "user_readenv" /etc/pam.d/ 2>/dev/null

如果输出中有 user_readenv=1,直接创建 ~/.pam_environment 即可。

好消息是,确实有预期的输出

1
2
3
4
5
6
7
8
9
10
11
12
13
$ grep -r "user_readenv" /etc/pam.d/ 2>/dev/null
/etc/pam.d/lightdm-autologin:session required pam_env.so readenv=1 user_readenv=1 envfile=/etc/default/locale
/etc/pam.d/lightdm-greeter:session required pam_env.so readenv=1 user_readenv=1 envfile=/etc/default/locale
/etc/pam.d/lightdm:session required pam_env.so readenv=1 user_readenv=1 envfile=/etc/default/locale
/etc/pam.d/sudo:session required pam_env.so readenv=1 user_readenv=0
/etc/pam.d/sudo:session required pam_env.so readenv=1 envfile=/etc/default/locale user_readenv=0
/etc/pam.d/polkit-1:session required pam_env.so readenv=1 user_readenv=0
/etc/pam.d/polkit-1:session required pam_env.so readenv=1 envfile=/etc/default/locale user_readenv=0
/etc/pam.d/sshd:session required pam_env.so user_readenv=1 envfile=/etc/default/locale
/etc/pam.d/gdm-password:session required pam_env.so readenv=1 user_readenv=1 envfile=/etc/default/locale
/etc/pam.d/gdm-autologin:session required pam_env.so readenv=1 user_readenv=1 envfile=/etc/default/locale
/etc/pam.d/gdm-launch-environment:session required pam_env.so readenv=1 user_readenv=1 envfile=/etc/default/locale
/etc/pam.d/gdm-fingerprint:session required pam_env.so readenv=1 user_readenv=1 envfile=/etc/default/locale

那么创建 .pam_environment 文件,内容指向标准库和 patchelf

1
2
3
VSCODE_SERVER_CUSTOM_GLIBC_LINKER=/home/xialing/sysroot/lib/ld-linux-x86-64.so.2
VSCODE_SERVER_CUSTOM_GLIBC_PATH=/home/xialing/sysroot/lib
VSCODE_SERVER_PATCHELF_PATH=/home/xialing/patchelf/bin/patchelf

之后便大功告成了。

原理

具体的原理,是一些 AIGC:

  1. PAM(Pluggable Authentication Modules) 在用户通过 SSH 认证时就会介入,它发生在任何 shell 启动之前。
  2. 服务器的 /etc/pam.d/sshd 配置里有这一行:session required pam_env.so user_readenv=1 envfile=/etc/default/locale
  3. pam_env.so 是一个 PAM 模块,user_readenv=1 表示它允许读取用户家目录下的 ~/.pam_environment 文件。
  4. 当用户 SSH 登录时,pam_env.so 会解析 ~/.pam_environment,把里面定义的变量注入到当前会话的环境变量中。这个过程不依赖任何 shell 配置文件,纯由 PAM 在认证阶段完成。
  5. 之后 VSCode Server 进程从该会话 fork 出来时,自然就能继承到这些环境变量。