跳到主要内容

Podman 在无根环境中的基本设置和使用

在允许没有 root 权限的用户运行 Podman 之前,管理员必须安装或构建 Podman 并完成以下配置。

管理员操作

安装 Podman

关于 Podman 的安装,请参见 安装说明

构建 Podman

关于 Podman 的构建,请参见 构建说明

安装 slirp4netns

slirp4netns 包提供了无特权网络命名空间的用户模式网络,必须在机器上安装它,以便 Podman 可以在无根环境中运行。该包在大多数 Linux 发行版上可用,可以通过它们的包分发软件(如 yumdnfaptzypper 等)进行安装。如果包不可用,您可以从 GitHub 构建和安装 slirp4netns

/etc/subuid/etc/subgid 配置

无根 Podman 要求运行它的用户在 /etc/subuid/etc/subgid 文件中列出一个 UID 范围。不同的发行版上提供了 shadow-utilsnewuid 包来提供这些文件,并且它们必须安装在系统上。添加或更新这些文件中的条目需要 root 权限。以下是从 Dan Walsh 在 opensource.com 上发表的 How does rootless Podman work? 文章中的摘要:

对于每个将允许创建容器的用户,更新 /etc/subuid/etc/subgid 文件中该用户的字段,看起来应该像下面这样。请注意,每个用户的值必须唯一。如果存在重叠,一个用户就有可能使用另一个用户的命名空间并破坏它。

例如,如果您有一个用户名为 john,您可能会添加以下行:

john:100000:65536

这里,john 是用户名,100000 是起始 UID,65536 是 UID 范围的大小。这意味着 john 用户将能够使用 UID 100000165535 范围内的 UID 来创建和管理容器。

您需要为每个允许运行 Podman 的用户重复此过程,并确保为每个用户指定的 UID 范围不会与其他用户的范围重叠。此外,这些 UID 应在系统上不存在,以避免冲突。

完成这些配置后,无根用户应该能够使用 Podman 创建和管理容器,而无需使用 root 权限。但请注意,出于安全考虑,无根 Podman 可能受到某些限制,例如无法访问某些设备或执行某些特权操作。因此,在使用无根 Podman 时,请确保您了解这些限制并遵循最佳安全实践。

这个文件的格式是 USERNAME:UID:RANGE,其中:

  • USERNAME 是列在 /etc/passwd 文件中或 getpwent 输出中的用户名。
  • UID 是为用户分配的起始 UID。
  • RANGE 是为用户分配的 UID 范围的大小。

这意味着用户 johndoe 被分配了 UID 100000 到 165535,以及他们在 /etc/passwd 文件中的标准 UID。请注意,这目前不支持网络安装;这些文件必须对本地主机可用。无法使用 LDAP 或 Active Directory 进行配置。

直接更新文件并不是推荐的做法,可以使用 usermod 程序来为用户分配 UID 和 GID。

# usermod --add-subuids 100000-165535 --add-subgids 100000-165535 johndoe
grep johndoe /etc/subuid /etc/subgid
/etc/subuid:johndoe:100000:65536
/etc/subgid:johndoe:100000:65536

如果您更新了 /etc/subuid/etc/subgid 文件,您需要停止该用户拥有的所有正在运行的容器,并终止系统上为该用户运行的暂停进程。这可以通过以该用户身份运行 podman system migrate 命令来自动完成。

授予对额外组的访问权限

如果用户完全映射的附加组属于该用户,则用户可以将这些组映射到容器命名空间:

# usermod --add-subgids 2000-2000 johndoe
grep johndoe /etc/subgid

上述命令为 johndoe 用户添加了一个额外的子组 ID 范围,即从 2000 到 2000(尽管只有一个 ID,但格式仍然适用)。然后,我们使用 grep 命令来确认 /etc/subgid 文件中确实添加了新的条目。

请记住,每次修改 /etc/subuid/etc/subgid 后,都需要确保相关的用户和容器被适当地管理,以避免潜在的安全问题或资源冲突。这通常意味着需要停止并重新启动相关的容器,以确保它们在新的 UID 或 GID 映射下运行。

这意味着用户 johndoe 可以在容器内部“伪装”成组 2000。请注意,通常不建议将活动用户 ID 分配给其他用户,因为这可能会导致用户伪装。

johndoe 可以使用 --group-add keep-groups 来保留额外的组,并使用 --gidmap="+g102000:@2000" 将主机上的组 2000 映射到容器中的组 102000

$ podman run \
--rm \
--group-add keep-groups \
--gidmap="+g102000:@2000" \
--volume "$PWD:/data:ro" \
--workdir /data \
alpine ls -lisa

启用无特权 ping

(在现代发行版上,您很可能不需要执行此操作)。

在无特权容器中运行的用户可能无法使用容器内的 ping 工具。

如果需要这样做,管理员必须验证用户的 UID 是否是 /proc/sys/net/ipv4/ping_group_range 文件中范围的一部分。

要更改其值,管理员可以使用类似 sysctl -w "net.ipv4.ping_group_range=0 2000000" 的命令。

要使更改持久化,管理员需要在 /etc/sysctl.d 中添加一个 .conf 扩展名的文件,其中包含 net.ipv4.ping_group_range=0 $MAX_GID,其中 $MAX_GID 是运行容器的用户的最高可分配 GID。

用户操作

在无根环境中运行 Podman 的大部分工作都落在机器管理员的肩上。

一旦管理员在机器上完成了设置,并为用户在 /etc/subuid/etc/subgid 中完成了配置,用户就可以开始使用他们想要的任何 Podman 命令了。

用户配置文件

Podman 的配置文件对于 root 用户通常位于 /usr/share/containers,并且可以通过 /etc/containers 中的文件进行覆盖。在无根环境中,这些配置文件位于 ${XDG_CONFIG_HOME}/containers,并且每个用户都拥有自己的配置文件。

注意:在没有 XDG 环境变量的环境中,Podman 内部会设置以下默认值:

-$XDG_CONFIG_HOME = $HOME/.config -$XDG_DATA_HOME = $HOME/.local/share -$XDG_RUNTIME_DIR = -/run/user/$UID 在使用 systemd 的环境中 -$TMPDIR/podman-run-$UID 在其他情况下

主要的三个配置文件是 containers.confstorage.confregistries.conf。用户可以根据自己的需要修改这些文件。

containers.conf

Podman 会按照以下顺序读取配置文件:

  1. /usr/share/containers/containers.conf
  2. /etc/containers/containers.conf
  3. ${XDG_CONFIG_HOME}/containers/containers.conf

如果存在这些文件,每个文件都可以覆盖之前文件中特定字段的设置。

storage.conf

storage.conf 的读取顺序如下:

  1. /etc/containers/storage.conf
  2. ${XDG_CONFIG_HOME}/containers/storage.conf

在无根 Podman 中,/etc/containers/storage.conf 中的某些字段将被忽略。这些字段包括:

graphroot=""
# 容器存储图目录(默认: "/var/lib/containers/storage")
# 容器存储程序创建的所有可写内容的默认目录。

runroot=""
# 容器存储运行目录(默认: "/run/containers/storage")
# 容器存储程序创建的所有临时可写内容的默认目录。
```bash

在无根 Podman 中,这些字段的默认值如下:

```bash
graphroot="${XDG_DATA_HOME}/containers/storage"
runroot="${XDG_RUNTIME_DIR}/containers"
```bash

[\$XDG_RUNTIME_DIR](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables) 在大多数系统上默认为 `/run/user/$UID`

#### registries.conf
注册表的配置读取顺序如下:

1. `/etc/containers/registries.conf`
2. `/etc/containers/registries.d/*`
3. `${XDG_CONFIG_HOME}/containers/registries.conf`

用户目录下的这些文件应该用于根据个人需求配置无根 Podman。这些文件默认情况下不会被创建。用户可以复制 `/usr/share/containers``/etc/containers` 目录下的文件并进行修改。

#### 授权文件
`podman login``podman logout` 命令使用的默认授权文件是 `${XDG_RUNTIME_DIR}/containers/auth.json`

### 使用卷

无根 Podman 并不是,也永远不会是 root;它不是 `setuid` 二进制文件,运行时也不会获得任何特权。相反,Podman 使用用户命名空间来转换一组在主机上获得访问权限的用户(通过 `newuidmap``newgidmap` 可执行文件)以及 Podman 创建的容器中您自己的用户的 UID 和 GID。

如果您的容器以 root 用户身份运行,那么容器中的 `root` 实际上是主机上的您的用户。UID/GID 1 是您在 `/etc/subuid``/etc/subgid` 等文件中指定的映射中的第一个 UID/GID。如果您以无根用户身份将主机上的目录挂载到容器中,并在容器中作为 root 创建文件,您会发现该文件实际上是由主机上的您的用户所拥有的。

例如:

```bash
host$ whoami
john

# 一个空的文件夹
host$ ls /home/john/folder
host$ podman run -it -v /home/john/folder:/container/volume mycontainer /bin/bash

# 现在我在容器内
root@container# whoami
root
root@container# touch /container/volume/test
root@container# ls -l /container/volume
total 0
-rw-r--r-- 1 root root 0 May 20 21:47 test
root@container# exit

# 我再次检查
host$ ls -l /home/john/folder
total 0
-rw-r--r-- 1 john john 0 May 20 21:47 test

在这个例子中,尽管文件是在容器中以 root 用户的身份创建的,但它实际上在主机上是由用户 john 所拥有的。这是因为 Podman 使用用户命名空间将容器内的 root 用户映射到主机上的 john 用户。这种行为是无根 Podman 安全模型的一部分,它确保容器内的进程无法以高于您在主机上的权限运行。

我们确实认识到这不符合许多人打算如何使用无根 Podman 的方式——他们希望容器内外的 UID 匹配。因此,我们提供了 --userns=keep-id 标志,它确保您的用户在容器内部被映射到它自己的 UID 和 GID。

区分以无根用户身份运行 Podman 和构建为无根运行的容器也很重要。如果您尝试运行的容器具有非 root 的 USER,那么在挂载卷时必须使用 --userns=keep-id。这是因为容器用户无法变成 root 并访问挂载的卷。

关于卷,还有另一个需要考虑的方面:

-提供您想要绑定挂载的目录路径时,路径需要以绝对路径的形式提供,或者以 .(点)开头的相对路径,否则该字符串将被解释为命名卷的名称。

更多信息

如果您在无根环境中运行 Podman 时仍然遇到问题,请参阅无根 Podman 的不足页面,其中列出了已知问题和此环境中已知问题的解决方案。

有关 Podman 及其子命令的更多信息,请查看主 README.md 页面上的链接或访问 podman.io 网站。