使用 chroot 和 PAM 在 HPC 集群上对用户隐藏目录

 

需求分析

最近需要把组里的集群计算环境提供给不完全可信的第三方使用,需要进行一些隔离(最主要是 /home 下的用户数据),但还需要提供正常的运行环境(包括 GPU、Infiniband、SLURM 作业提交、工具链管理等)。思考了一下,现有的一些方案都不太合适:

  • POSIX 权限控制:把 /home 下面的各个文件夹改成 0750 就能简单快速地防止其他用户读写。但组里的同学有很频繁的互访需求,改掉之后比较麻烦。
  • 容器隔离:通常用容器可以把大多数东西都很好地隔开,但在 HPC 环境下比较麻烦,需要额外的配置才能支持 GPU 和 IB 卡。更何况我们使用了 SLURM 来提交任务,使得容器的配置更加复杂(和灵车?)。
  • SELinux 控制:可以做得很精细,但我从来没有在 HPC 集群上部署 SELinux 的经验,完全不知道会踩什么坑,也没有找到相关的文章。为了毕业,还是不瞎折腾了。

进一步思考后,其实对于没有 root 权限的用户,能对其隐藏其他人的家目录就能满足此类 HPC 场景的需求了。因此我想到了使用 chroot 来实现这个需求,此外还找到了 libpam-chroot 这个模块能够自动在用户登录时完成切换。

Debian 配置

在 Debian bullseye 中,配置如下:

首先准备一个用户和隔离用环境:

useradd -m foo 
mkdir -p /home/jailed/rootfs
cd /home/jailed/rootfs

binds="bin dev dev/shm dev/pts etc lib lib32 lib64 opt proc run sbin srv sys tmp usr var home/$user home/spack home/intel"

for d in $binds; do
  mkdir -p $d
  if mount | grep $(realpath $d/); then
    echo $d already mounted;
  else
    mount --bind /$d $d && echo $d mounted;
  fi
done

其中 binds 指定了所有对该用户可见的目录,可以按需调整,如挂载更多用户的 home(上面的 spackintel 都是公用的软件环境),或者单独挂载空的 tmpfs

此后安装 libpam-chroot

apt install libpam-chroot
ln -v -s -r -t /usr/lib/x86_64-linux-gnu/security/ /usr/lib/x86_64-linux-gnu/pam_chroot.so

后一行是由于 Debian 打包的 bug (可见 [1], [2]),目前还需要手动修复文件位置,否则 PAM 模块无法找到。

然后修改 PAM 本身和模块的配置(保留原有内容,添加到最后):

--- /etc/pam.d/common-session
+++ /etc/pam.d/common-session
+session optional pam_chroot.so

--- /etc/chroot.conf
+++ /etc/chroot.conf
+foo /home/jailed/rootfs

然后尝试使用此用户登录即可观察到 /home 只有脚本中挂载的三个目录,集群各类软硬件使用均不受影响。

SLURM 支持

为了使得 SLURM 的计算结点也能实现隐藏,除了上面的所有配置外,还要(在所有结点)增加如下配置:

--- /etc/slurm/slurm.conf
+++ /etc/slurm/slurm.conf
-UsePAM=0
+UsePAM=1

--- /dev/null
+++ /etc/pam.d/slurm
+session required pam_permit.so
+session optional pam_chroot.so

上述的 slurm PAM 服务是最小化的写法,可根据自己的需要增加其它项目。

此外,如果 SLURM 使用了 cgroup 管理任务,还需额外挂载 /sys/fs/cgroup/sys/fs/cgroup/freezer,否则启动任务会导致 slurmd 卡住。

附注

OpenSSH 也支持在 sshd_config 中对某个用户直接进行 chroot:

Match User foo
    ChrootDirectory /home/jailed/rootfs

但我并不推荐这样做,因为 PAM 是更通用的做法,如在其他本地用户切换成这些身份时也能获得一致的体验。还有一个重要原因是 SLURM 完全不使用 SSH(而是由自己的进程直接 fork & exec),因此在计算结点上这样做是无效的。

libpam-chroot 有多个不同版本,Debian 自带的是 gpjt/pam-chroot 需要读取 chroot.conf 的配置文件。FreeBSD 也有同名模块,可以通过 passwd 中的家目录项来配置 chroot 的根目录和工作目录,感觉用起来会更方便一些。

最后,如果使用这一方案,同一个集群上多个需要隔离的用户可以共享一个 rootfs,只需要各自的家目录恰当配置了权限,无法互访即可。chroot.conf 中的配置可以使用通配符和正则表达式,还可以实现更复杂和强大的功能(如每个用户不同的 rootfs 目录)。