无根 Podman 如何工作?

了解 Podman 如何利用用户命名空间以无根模式运行。
203 位读者喜欢这篇文章。
An introduction to GNU Screen

Opensource.com

在我之前关于用户命名空间和 Podman文章中,我讨论了如何使用 Podman 命令启动具有不同用户命名空间的不同容器,从而更好地分隔容器。 Podman 还利用用户命名空间以无根模式运行。 基本上,当非特权用户运行 Podman 时,该工具会设置并加入用户命名空间。 Podman 在用户命名空间内成为 root 用户后,Podman 可以挂载某些文件系统并设置容器。 请注意,这里没有权限提升,只有用户可用的额外 UID,如下所述。

Podman 如何创建用户命名空间?

shadow-utils

大多数当前的 Linux 发行版都包含一个版本的 shadow-utils,它使用 /etc/subuid/etc/subgid 文件来确定用户在用户命名空间中可用的 UID 和 GID。

$ cat /etc/subuid
dwalsh:100000:65536
test:165536:65536
$ cat /etc/subgid
dwalsh:100000:65536
test:165536:65536

useradd 程序会自动为添加到系统的每个用户分配 65536 个 UID。 如果系统上存在现有用户,则需要自行分配 UID。 这些文件的格式为 username:STARTUID:TOTALUIDS。 意味着在我的例子中,dwalsh 被分配了从 100000 到 165535 的 UID 以及我的默认 UID,恰好是 /etc/passwd 中定义的 3265。 在分配这些 UID 范围时,您需要小心,不要与系统上的任何真实 UID 重叠。 如果您有一个用户列为 UID 100001,那么现在我 (dwalsh) 将能够成为此 UID,并且可能会读取/写入/执行 UID 拥有的文件。

Shadow-utils 还添加了两个 setuid 程序(或 setfilecap)。 在 Fedora 上,我有

$ getcap /usr/bin/newuidmap
/usr/bin/newuidmap = cap_setuid+ep
$ getcap /usr/bin/newgidmap
/usr/bin/newgidmap = cap_setgid+ep

Podman 执行这些文件以设置用户命名空间。 您可以通过检查无根容器内的 /proc/self/uid_map 和 /proc/self/gid_map 来查看映射。

$ podman run alpine cat /proc/self/uid_map /proc/self/gid_map
     	0   	3267      	1
     	1 	100000  	65536
     	0   	3267      	1
     	1 	100000  	65536

如上所示,Podman 默认将容器中的 root 映射到您当前的 UID (3267),然后将 /etc/subuid 和 /etc/subgid 中分配的 UID/GID 范围从 1 开始映射。这意味着在我的示例中,容器中的 UID=1 是 UID 100000,UID=2 是 UID 100001,一直到 65536,即 165535。

用户命名空间之外的任何由未映射到用户命名空间中的 UID 或 GID 拥有的项目,都属于在 kernel.overflowuid sysctl 中配置的用户,默认值为 35534,我的 /etc/passwd 文件说该名称为 nobody。 由于您的进程无法以未映射的 ID 运行,因此所有者和组权限不适用,因此您只能根据其“其他”权限访问这些文件。 这包括系统上运行的容器上的真实 root 用户拥有的所有文件,因为 root 未映射到用户命名空间中。

Buildah 命令有一个很棒的功能,buildah unshare。 这会将您置于 Podman 运行的同一用户命名空间中,但无需进入容器的文件系统,因此您可以列出主目录的内容。

$ ls -ild /home/dwalsh
8193 drwx--x--x. 290 dwalsh dwalsh 20480 Jan 29 07:58 /home/dwalsh
$ buildah unshare ls -ld /home/dwalsh
drwx--x--x. 290 root root 20480 Jan 29 07:58 /home/dwalsh

请注意,在用户命名空间之外列出主目录属性时,内核报告所有者为 dwalsh,而在用户命名空间内,它报告该目录由 root 拥有。 这是因为主目录由 3267 拥有,并且在用户命名空间内,我们将该 UID 视为 root。

在设置用户命名空间之后,Podman 中接下来会发生什么?

Podman 使用 containers/storage 来提取容器镜像,并且 containers/storage 足够智能,可以将镜像中 root 拥有的所有文件映射到用户命名空间的 root,并将其他 UID 拥有的任何其他文件映射到其用户命名空间 UID。 默认情况下,此内容会被写入 ~/.local/share/containers/storage。 容器存储在无根模式下可以使用 vfs 模式或 Overlay。 注意:只有安装了 fuse-overlayfs 可执行文件才支持 Overlay。

内核只允许用户命名空间 root 挂载某些类型的文件系统; 目前,它允许挂载 procfs、sysfs、tmpfs、fusefs 和绑定挂载(只要源和目标由运行 Podman 的用户拥有。 OverlayFS 尚不支持,但内核团队正在努力允许它)。

然后,Podman 会挂载容器的存储,如果它使用 fuse-overlayfs; 如果存储驱动程序使用 vfs,则不需要挂载。 但是,vfs 上的 Podman 需要大量空间,因为每个容器都会复制整个底层文件系统。

然后,Podman 会挂载 /proc 和 /sys 以及一些 tmpfs 并在容器中创建设备。

为了使用主机网络之外的网络,Podman 使用 slirp4netns 程序来设置 用于非特权网络命名空间的用户模式网络。 Slirp4netns 允许 Podman 将容器中的端口暴露给主机。 请注意,内核仍然不允许非特权进程绑定到小于 1024 的端口。 绑定到端口需要 Podman-1.1 或更高版本。

无根 Podman 可以使用用户命名空间进行容器隔离,但您只能访问 /etc/subuid 文件中定义的 UID。

结论

Podman 工具使人们能够在不牺牲系统安全性的情况下构建和使用容器; 您可以在不给他们 root 权限的情况下为您的开发人员提供他们需要的访问权限。

当您将容器投入生产时,您可以利用用户命名空间提供的额外安全性来使工作负载彼此隔离。

标签
User profile image.
Daniel Walsh 在计算机安全领域工作了近 30 年。 Dan 于 2001 年 8 月加入 Red Hat。

1 条评论

Creative Commons License本作品采用 Creative Commons Attribution-Share Alike 4.0 International License 授权。
© . All rights reserved.