linux 的 unshare 命令

最近有一个需求,就是要在服务器端执行用户传过来的命令。为了防止用户运行一些恶意指令,我们需要构造一个沙箱来运行。这种隔离的需求第一反应就想到了之前写过的 使用 overlayfs 和 chroot 搭建临时环境,但是这需要 root 权限。有没有办法在不需要 root 的情况运行 mountchroot 呢?简单搜了下就找到了想要的答案。

unshare 是 linux 下的一个命令,通过构造一个新的 namespace 来和现有的环境隔离。基本用法很简单,就是一些选项,然后再加上要运行的命令即可,看起来有点像一个精简版的 docker。而且和 docker 一样的是,只要运行的命令退出了,在命令运行期间做的操作都会被自动清除。这个完美符合我们对沙箱的要求。

先来做个测试。通过 unshare 把当前用户映射成 root,并创建一个新的 mount namespace

unshare -rm /bin/bash

运行之后发现我们的用户变成了 root!另外一个不容易发现的内容就是,在我们所创建的这个 mount namespace 中进行 mountumount 操作,不会影响外面的真实的文件系统。也就是说如果我们在这个环境中 unmount 了一个挂载点,退出进程(也就是我们这里的 bash)之后这个挂载点还在,并没有被 unmount。但是除此之外的其它操作(例如增删文件)都会被保留下来。这也是 unshare 这个名字的由来:只有指定的选项才会被 unshare,没有指定的都还是 share 的。

稍微解释下 -rm 这两个选项。-r 表示将当前用户映射成 root 用户,-m 就是刚提到的 mount namespace

又因为是沙箱环境,我们不希望用户发现系统中正在运行的其它进程,所以我们可以创建一个 pid namespace

unshare -rpf /bin/bash

这里我们多加了 -f 选项,表示使用 fork() 来运行我们的程序(这里的 /bin/bash),如果不加这个选项会报错:

bash: fork: Cannot allocate memory

这个错误在 pid_namespaces(7) 中有说明。

我们使用 -r 选项把自己映射成了 root,但只在新创建的 namespace 中有效。如果我们在隔离出来的 namespace 中尝试去删掉一个外部 root 权限的文件时仍然会报错,因为这两个不是同一个 namespace 内的 root,就相当于一个 namespace 中的用户不能操作另一个 namespace 中的文件,这样才是隔离的效果。

参考资料

[1] Overlay Mounting as non-root

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注