# Podman Rootless 技巧：利用 --Group-Add Keep-Groups 解决卷组权限难题


对于使用 Podman Rootless 模式的用户来说，在容器中访问宿主机上通过组权限管理的目录，是一个棘手的问题。本文将介绍一个实用技巧，通过在启动容器时使用 `--group-add keep-groups` 参数，优雅地解决这一权限难题。

## 场景设定

示例场景：

- **运行环境**: 你正在以非 root 用户（例如 `nite`，UID 为 1000）的身份运行 Podman Rootless 容器。
- **宿主机用户组**: 你的用户 `nite` 属于 `git` 组。
- **共享目录**: 宿主机上有一个名为 `repos` 的目录，其权限设置为允许 `git` 组的成员进行读写访问。
- **容器需求**: 你希望在 Podman 容器中也能顺利访问和操作 `repos` 目录。

当你尝试将 `repos` 目录作为卷挂载到容器中时，常常会遇到权限问题。一个典型的现象是，在容器内部查看该目录的所有权时，会显示为 `nobody:nobody` 且访问时得到 `Permission denied` 错误。

## Rootless 容器的用户命名空间

要理解这个问题的根源，我们需要了解 Podman Rootless 模式的核心机制：用户命名空间（User Namespaces）。

在 Rootless 模式下，Podman 会创建一个用户命名空间，将宿主机上运行容器的普通用户（例如 `nite`，UID 1000）映射为容器内的 `root` 用户（UID 0）。 同样，宿主机上的用户组 GID 也会被映射。这种映射机制是 Rootless 容器安全性的基石，它确保了即使用户在容器内拥有 `root` 权限，在宿主机上依然只是一个普通用户。

但当涉及到宿主机上通过附加组（supplementary group）授予权限的目录时，问题就出现了。默认情况下，启动容器时，容器内的进程不会继承宿主机用户的附加组关系。 这就是为什么即便宿主机上的 `nite` 用户因为属于 `git` 组而可以访问 `repos` 目录，容器内的进程却无法访问的原因。

## 解决方案：`--group-add keep-groups`

为了解决这个问题，Podman 提供了一个强大的参数：`--group-add keep-groups`。

这个参数的作用是，在启动容器时，让容器内的进程保持（keep）其父进程（即在宿主机上运行 `podman run` 命令的用户）的附加组。

具体来说，当使用此参数时，Podman 会指示底层的 OCI 运行时（如 crun）在创建容器进程时不调用 `setgroups` 系统调用。 `setgroups` 通常会重置进程的组信息为容器内定义的组，从而丢弃从父进程继承的组。通过跳过这一步，容器内的进程就“继承”了宿主机用户的组关系。

所以你的启动命令会是这样：

```bash
podman run -it --rm \
  -v /path/to/your/repos:/repos \
  --group-add keep-groups \
  your-image:tag
```

## “神秘”的 `nobody:nobody`

一个有趣且可能令人困惑的现象是，即使使用了 `--group-add keep-groups` 参数后，你在容器内执行 `ls -l` 查看挂载的 `repos` 目录，其所有者和所属组可能仍然显示为 `nobody:nobody`。

这是因为宿主机上 `repos` 目录的 GID（`git` 组的 GID）并未被映射到容器内的用户命名空间中。 因此，容器无法将这个 GID 解析为一个已知的组名，只能将其显示为 `nobody` 或一个溢出的 GID。

但这并不影响实际的访问权限。由于容器内的进程已经通过 `keep-groups` 继承了宿主机 `git` 组的成员资格，因此内核会正确地判断该进程拥有访问此目录的权限。


---

> 作者: Nite  
> URL: https://www.nite07.com/zh-cn/posts/podman-group-share/  

