了解如何使用 Podman 在单独的用户空间运行容器。
Podman 是 libpod 库的一部分,使用户能够管理 pod、容器和容器镜像。在我的上一篇文章中,我写过将 Podman 作为一种更安全的运行容器的方式。在这里,我将解释如何使用 Podman 在单独的用户命名空间中运行容器。
作为分离容器的一个很棒的功能,我一直在思考用户命名空间,它主要是由 Red Hat 的 Eric Biederman 开发的。用户命名空间允许你指定用于运行容器的用户标识符(UID)和组标识符(GID)映射。这意味着你可以在容器内以 UID 0 运行,在容器外以 UID 100000 运行。如果容器进程逃逸出了容器,内核会将它们视为以 UID 100000 运行。不仅如此,任何未映射到用户命名空间的 UID 所拥有的文件对象都将被视为 nobody 所拥有(UID 是 65534 , 由 kernel.overflowuid 指定),并且不允许容器进程访问,除非该对象可由“其他人”访问(即世界可读/可写)。
如果你拥有一个权限为 660 的属主为“真实” root 的文件,而当用户命名空间中的容器进程尝试读取它时,会阻止它们访问它,并且会将该文件视为 nobody 所拥有。
示例
以下是它是如何工作的。首先,我在 root 拥有的系统中创建一个文件。
$ sudo bash -c "echo Test > /tmp/test" $ sudo chmod 600 /tmp/test $ sudo ls -l /tmp/test -rw-------. 1 root root 5 Dec 17 16:40 /tmp/test
接下来,我将该文件卷挂载到一个使用用户命名空间映射 0:100000:5000 运行的容器中。
$ sudo podman run -ti -v /tmp/test:/tmp/test:Z --uidmap 0:100000:5000 fedora sh # id uid=0(root) gid=0(root) groups=0(root) # ls -l /tmp/test -rw-rw----. 1 nobody nobody 8 Nov 30 12:40 /tmp/test # cat /tmp/test cat: /tmp/test: Permission denied
上面的 --uidmap 设置告诉 Podman 在容器内映射一系列的 5000 个 UID,从容器外的 UID 100000 开始的范围(100000-104999)映射到容器内 UID 0 开始的范围(0-4999)。在容器内部,如果我的进程以 UID 1 运行,则它在主机上为 100001。
由于实际的 UID=0 未映射到容器中,因此 root 拥有的任何文件都将被视为 nobody 所拥有。即使容器内的进程具有 CAP_DAC_OVERRIDE 能力,也无法覆盖此种保护。DAC_OVERRIDE 能力使得 root 的进程能够读/写系统上的任何文件,即使进程不是 root 用户拥有的,也不是全局可读或可写的。
用户命名空间的功能与宿主机上的功能不同。它们是命名空间的功能。这意味着我的容器的 root 只在容器内具有功能 —— 实际上只有该范围内的 UID 映射到内用户命名空间。如果容器进程逃逸出了容器,则它将没有任何非映射到用户命名空间的 UID 之外的功能,这包括 UID=0 。即使进程可能以某种方式进入另一个容器,如果容器使用不同范围的 UID,它们也不具备这些功能。
请注意,SELinux 和其他技术还限制了容器进程破开容器时会发生的情况。
使用 podman top 来显示用户名字空间
我们在 podman top 中添加了一些功能,允许你检查容器内运行的进程的用户名,并标识它们在宿主机上的真实 UID。
让我们首先使用我们的 UID 映射运行一个 sleep 容器。
$ sudo podman run --uidmap 0:100000:5000 -d fedora sleep 1000
现在运行 podman top :
$ sudo podman top --latest user huser USER HUSER root 100000 -
$ ps -ef | grep sleep 100000 21821 21809 0 08:04 ? 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1000
注意 podman top 报告用户进程在容器内以 root 身份运行,但在宿主机(HUSER )上以 UID 100000 运行。此外,ps 命令确认 sleep 过程以 UID 100000 运行。
现在让我们运行第二个容器,但这次我们将选择一个单独的 UID 映射,从 200000 开始。
$ sudo podman run --uidmap 0:200000:5000 -d fedora sleep 1000 $ sudo podman top --latest user huser USER HUSER root 200000 -
$ ps -ef | grep sleep 100000 21821 21809 0 08:04 ? 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1000 200000 23644 23632 1 08:08 ? 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1000
请注意,podman top 报告第二个容器在容器内以 root 身份运行,但在宿主机上是 UID=200000。
另请参阅 ps 命令,它显示两个 sleep 进程都在运行:一个为 100000,另一个为 200000。
这意味着在单独的用户命名空间内运行容器可以在进程之间进行传统的 UID 分离,而这从一开始就是 Linux/Unix 的标准安全工具。
用户名字空间的问题
几年来,我一直主张用户命名空间应该作为每个人应该有的安全工具,但几乎没有人使用过。原因是没有任何文件系统支持,也没有一个移动文件系统。
(编辑:ASP站长网)
|