引言
在不断发展的容器技术领域,Podman 的最新版本 v4.6.0 包含了一项有望彻底改变系统管理员工作方式的功能:Podmansh。作为一款利用 Podman 强大容器管理能力的登录 Shell,Podmansh 将重新定义用户与系统交互的方式,同时确保管理员获得最大的安全性和控制权。
为何选择 Podmansh?
管理对资源的访问是系统管理员的一项关键任务。现在越来越需要一种机制,能够将用户限制在预定义的边界内。`podmansh` 命令通过允许系统管理员在用户登录系统时,在容器内执行用户 Shell 来解决此问题。
工作原理
管理员配置系统上的用户使用 `/usr/bin/podmansh` 命令,而不是像 `/bin/bash` 这样的普通 Shell 命令。当用户以这种方式登录系统时,Podmansh 命令将在一个名为 `podmansh` 的无根容器中执行用户会话。当用户会话启动时,Systemd 会自动启动 Podman 容器 `podmansh`,并持续运行直到所有用户会话退出。
用户登录的容器通过 Quadlet 文件定义。管理员可以通过 Quadlet 文件中为它们指定的卷来控制容器中主机系统的可见性。管理员还可以通过 Quadlet 设置限制登录用户的访问权限、安全特权以及资源限制。
管理员在 `/etc/containers/systemd/users` 目录中创建 Quadlet 文件。Systemd 随后会在用户登录时为所有用户初始化它。例如,管理员可以创建一个名为 `Podmansh` 的特定 Quadlet,并允许用户使用登录 Shell `/usr/bin/podmansh`。这些用户登录 Shell 将通过 Podman 自动在 Podmansh 容器内执行。
为了实现更精细的控制,管理员可以为每个用户在 `/etc/containers/systemd/users/${USER_ID}` 目录中放置 Quadlet 文件。因此,只有指定的 USER_ID 在该用户登录时才会执行这些 Quadlet 服务。
设置
首先,以 root 身份运行 `useradd` 创建一个用户登录会话,然后创建 `/etc/containers/systemd/users/${USER_ID}` 中的 Quadlet 文件。
示例 1:完全锁定的容器,无法访问主机操作系统。
`# useradd -s /usr/bin/podmansh lockedu` `# grep lockedu /etc/passwd` `lockedu:x:4008:4008::/home/lockedu:/usr/bin/podmansh` `# USER_ID=$(id -u lockedu)` `# mkdir -p /etc/containers/systemd/users/${USER_ID}` `# cat > /etc/containers/systemd/users/${USER_ID}/podmansh.container << _EOF` `[Unit]` `Description=The Podmansh container` `After=local-fs.target` `[Container]` `Image=registry.fedoraproject.org/fedora` `ContainerName=podmansh` `RemapUsers=keep-id` `RunInit=yes` `DropCapability=all` `NoNewPrivileges=true` `Exec=sleep infinity` `[Install]` `RequiredBy=default.target` `_EOF` |
注意 Quadlet 文件中 [Container] 部分的一些字段。
- `Image=registry.fedoraproject.org/fedora`
此选项告诉 Podman 下载 fedora 镜像以在其中运行容器。管理员可以选择特定的容器镜像来启动用户,这些镜像可能与主机系统不同。 - ContainerName=podmansh
容器名称必须是 `podmansh`,因为 `/usr/bin/podmansh` 总是对 `podmansh` 容器执行 `podman exec`。 - RemapUsers=keep-id
此选项会将用户的 USER_ID 映射为容器内的自身。如果没有此字段,用户将以用户命名空间的 root 身份登录。 - RunInit=yes
这告诉 podman 在容器内启动一个 init 进程,该进程将清理容器内的任何僵尸进程。 - `DropCapability=all`
此选项告诉 Podman 丢弃容器内的所有能力,并阻止容器进程在系统上获得任何能力,即使它设法逃脱容器。
- NoNewPrivileges=true
此选项更进一步,阻止容器除了能力之外获得任何新的特权。 - `Exec=sleep infinity`
最后一个要查看的字段是 Exec 命令。大多数情况下,我们建议用户会话在 Podmansh 容器中进入睡眠状态。这可以防止会话使用系统资源。由于用户会话在容器中执行,它们将在容器镜像内运行默认的 `/bin/sh`。
如果您真的想将用户锁定在容器中,您甚至可以添加一个标志,例如
Network=none。此选项将从容器内的进程中移除所有网络访问权限。这意味着容器进程无法通过网络访问其他节点,从它的角度来看,盒子上没有网络。您还可以将容器设置为 ReadOnly=true,这将阻止用户除了 `/run` 和 `/tmp` 中的 tmpfs 目录外,在任何地方写入。
当用户会话退出时,Quadlet 容器将从系统中移除,这意味着用户写入容器镜像的任何内容都将在用户会话终止时移除。
下一个示例将允许用户将其内容保存在其主目录中。
示例 2:或者,以 root 身份运行,创建一个 Quadlet,允许用户在用户命名空间内成为 root。他们还可以从其主目录中永久读/写内容,该主目录是来自实际主机用户帐户的卷挂载,而不是在容器内部。
`# useradd -s /usr/bin/podmansh confinedu` `# grep confinedu /etc/passwd` `confinedu:x:4009:4009::/home/confinedu:/usr/bin/podmansh` `# USER_ID=$(id -u confinedu)` `# mkdir -p /etc/containers/systemd/users/${USER_ID}` `# cat > /etc/containers/systemd/users/${USER_ID}/podmansh.container << _EOF` `[Unit]` `Description=The Podmansh container` `After=local-fs.target` `[Container]` `Image=registry.fedoraproject.org/fedora` `ContainerName=podmansh` `RemapUsers=keep-id` `RunInit=yes` `Volume=%h/data:%h:Z` `Exec=sleep infinity` `[Service]` `ExecStartPre=/usr/bin/mkdir -p %h/data` `[Install]` `RequiredBy=default.target` `_EOF` |
请注意,在此示例中,[Container] 结构中的前四个字段与上一个示例相同。Exec 命令仍然是 `sleep infinity`。但是我们移除了 DropCapabilities 和 NoNewPrivileges 的安全锁定。没有这些限制,用户会话可以在其用户命名空间内成为 root,并可以执行某些特权活动,例如安装软件包(尽管它们目前不会持久化)。请注意,这是一个无根容器,这意味着用户进程在主机系统上并非真正的 root (uid=0)。
Quadlet 中添加了几行。
- Volume=%h/data:%h:Z
此行告诉 Podman 将用户主目录中的数据目录卷挂载到容器内的其主目录中。`:Z` 告诉 SELinux 系统标记数据目录以匹配容器的标签。`%h` 告诉 systemd 在调用 Podman 时将此字段扩展为用户的主目录。由于 `data` 目录卷挂载到容器中,因此它将保留在容器外部,供用户下次登录系统时使用。 - `[Service]`
`ExecStartPre=/usr/bin/mkdir -p %h/data`
此部分告诉 systemd 在启动容器之前,在主机上的用户主目录中预创建数据目录。
示例 3:或者,以 root 身份运行,创建一个 Quadlet,允许用户在用户命名空间内成为 root。他们还可以从其主目录中永久读/写内容,该主目录是来自实际主机用户帐户的卷挂载,而不是在容器内部。
`# useradd -s /usr/bin/podmansh volumeu` `# grep volumeu /etc/passwd` `volumeu:x:4010:4010::/home/volumeu:/usr/bin/podmansh` `# USER_ID=$(id -u volumeu)` `# mkdir -p /etc/containers/systemd/users/${USER_ID}` `# cat > /etc/containers/systemd/users/${USER_ID}/podmansh.container << _EOF` `[Unit]` `Description=The Podmansh container` `After=local-fs.target` `[Container]` `Image=registry.access.redhat.com/ubi9` `ContainerName=podmansh` `RemapUsers=keep-id` `RunInit=yes` `Device=/dev/nvidia123Volume=/secret/data:/secret:ro` `Exec=sleep infinity` `[Install]` `RequiredBy=default.target` `_EOF` |
请注意,在这个最后一个示例中,添加了两个字段以允许对主机上的特定卷进行只读访问,并且将一个 nVidia 设备添加到了容器中。镜像更改为 ubi9,以突出显示您可以拥有多个 Podmansh Quadlet,每个用户一个,并且这些 Quadlet 可以使用不同的镜像。
Quadlet 中添加了几行。
- `Device=/dev/nvidia123`
设备行将此设备添加到容器中供用户会话使用。
- Volume=/secret/data:/secret:ro
此行告诉 Podman 将 `/secret/data` 目录作为只读挂载挂载到容器内的 `/secret`。这里的想法是允许容器镜像内的应用程序使用该设备处理 `/secret` 目录中的数据。
最后一个示例将允许管理员根据用户的安全级别向不同的用户公开系统的不同部分。
安全与保密
用户被限制在其容器环境中,并具备所有安全机制,包括 SELinux。容器内可用的任何系统信息都严格限制为挂载到容器中的卷,从而确保高水平的安全性和隔离性。
无缝操作
一旦用户会话启动,systemd 就会自动创建关联的容器。随着所有与用户会话的连接被移除,systemd 会关闭容器。这意味着用户可以多次登录系统,每个会话连接到同一个容器,提供无缝的用户体验。
此外,管理员可以通过卷将主机系统中的特定主机数据暴露给用户。这可以在不将用户暴露给系统其他部分的情况下完成,从而维护整个系统的完整性和安全性。
总结
总而言之,Podman v4.6 的新功能 Podmansh 将彻底改变系统管理员管理用户访问和控制的方式,同时保持令人印象深刻的安全性和保密性。这是容器技术领域的一个突破性进展,为用户与系统交互提供了一种新方式。
发表回复