拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Podman 用户命名空间 GID 映射选项 `--gidmap` 完全解析:从 pod create 到内核映射实现

Podman 用户命名空间 GID 映射选项--gidmap完全解析从 pod create 到内核映射实现【免费下载链接】podmanPodman: A tool for managing OCI containers and pods.项目地址: https://gitcode.com/gh_mirrors/po/podman--gidmap是 Podman 在创建 Podpodman pod create与克隆 Podpodman pod clone时为整个 Pod 的用户命名空间指定宿主机 GID → 容器 GID 映射的核心选项。本文以 gidmap.pod.md 为主线结合仓库源码逐层剖析其参数格式、与--userns的冲突语义、底层解析链路以及实测验证方式帮助你在需要精细化权限隔离、多租户资源映射的场景下正确使用该选项。选项速览一句话理解 --gidmap--gidmap让 Podman 将 Pod 中所有容器运行在一个全新的用户命名空间user namespace内并使用你提供的 GID 映射关系把宿主机上的组 ID 翻译为容器内的组 ID。其完整定义位于 gidmap.pod.md--gidmapcontainer_gid:from_gid[:amount]三个核心约束需要牢记与--userns冲突二者不可同时指定违反时命令直接报错面向整个 Pod映射作用于 Pod 的共享用户命名空间加入该 Pod 的所有容器均受其约束可重复传递多次传入--gidmap可以映射多个互不重叠的范围最终合并为一张完整的映射表。参数格式详解三元组的语义每个--gidmap条目由三段组成中间以冒号分隔字段含义说明container_gid容器命名空间内的起始 GID从该 ID 开始容器内可见的 GIDfrom_gid宿主机命名空间内的起始 GID被映射进容器的宿主机 GID 起点amount可选映射的 ID 数量缺省时按1处理即只映射单个 GID例如--gidmap0:200000:65536的含义是把宿主机上200000到265535这 65536 个 GID映射为容器内的0到65535从而在容器内拥有完整的“root 组 普通组”视图而宿主机侧却完全不暴露真实 GID。这也正是用户命名空间隔离的价值所在。省略 amount 时的默认行为从解析实现看两段式的写法是合法的。在 pkg/util/utils.go 中ParseIDMap对输入先按:切分idSpec : strings.Split(idMapSpec, :) // if its a length-2 list assume the size is 1: if len(idSpec) 2 { idSpec append(idSpec, 1) }也就是说--gidmap1000:50000等价于--gidmap1000:50000:1只映射一个 ID随后代码要求切分后的段数必须是 3 的整数倍否则报错initializing ID mappings: %s setting is malformed expected [\[ug]uint32:[]uint32[:uint32]\]。进阶语法引用父命名空间 ID源码还支持在from_gid位置使用前缀见 parseAutoTriple 中的HostID syntax注释--gidmap101001:1001:1含义是“取父命名空间对 rootless 而言即你的可用子 ID 映射池中的 ID 1001映射到容器内的 101001”。系统会根据宿主机可用的 ID 映射池来自/etc/subgid等自动解析该引用适合在不确定具体子 ID 起始值时使用。进阶语法填充剩余子 ID在from_gid位置使用前缀如--gidmap0:1000时Podman 会从宿主机可用的子 ID 范围中取一段将剩余的可用 ID 全部填充进映射。解析器内部通过fillMap标志跟踪这一行为并在 ParseIDMap 末尾调用fillIDMap完成填充if fillMap { availableRanges : getAvailableIDRangesFromMappings(idmap, parentMapping) idmap fillIDMap(idmap, availableRanges) }最终所有映射条目还会经过sortAndMergeConsecutiveMappingspkg/util/utils.go按容器侧 GID 排序并将首尾相接的连续片段合并得到紧凑的映射表。在 pod create / pod clone 中使用根据 gidmap.pod.md 头部注释该选项文件同时服务于podman pod create与podman pod clone两个命令仓库中对应的命令定义见 cmd/podman/pods/create.go 与 cmd/podman/pods/clone.go。pod create 示例# 创建名为 dev-pod 的 Pod映射两组 GID 范围 podman pod create \ --name dev-pod \ --gidmap0:200000:65536 \ --gidmap100000:300000:1000 # 查看 Pod 是否按预期创建 podman pod ps首次映射让容器内以 GID 0 起连续 65536 个组 ID 可见第二次映射额外补充一段不连续的组范围。Pod 创建后podman pod start dev-pod启动的所有容器都会运行在同一个带该 GID 映射的用户命名空间中。pod clone 示例podman pod clone用于复制一个已有 Pod 及其内部容器克隆时同样可以通过--gidmap覆盖生成新 Pod 的用户命名空间配置克隆入口调用registry.ContainerEngine().PodClone见 cmd/podman/pods/clone.gopodman pod clone --name dev-pod-copy --gidmap0:100000:10000 dev-pod与 --userns 的冲突一条硬性规则原文档明确声明--gidmap与--userns互斥这条规则在 CLI 解析层有直接实现。在 cmd/podman/containers/create.go 的CreateInit函数中if len(vals.UIDMap) 0 || len(vals.GIDMap) 0 || vals.SubUIDName ! || vals.SubGIDName ! { if c.Flag(userns).Changed { return vals, errors.New(--userns and --uidmap/--gidmap/--subuidname/--subgidname are mutually exclusive) } // force userns flag to private vals.UserNS private }也就是说一旦检测到--gidmap或--uidmap、--subuidname、--subgidname被显式传入且用户同时手动指定了--userns命令会直接失败若未指定--usernsPodman 会自动把用户命名空间模式强制为private确保手工映射真正生效而不是被其他 userns 模式覆盖。podman pod create与podman pod clone均复用common.DefineCreateFlags注册该选项并走同一校验路径见 cmd/podman/common/create.go 中gidmap标志的定义。底层实现GID 映射的完整调用链从命令行参数到内核映射表--gidmap经历了三级处理均可在本仓库源码中追踪CLI 收集cmd/podman/common/create.go 将--gidmap定义为StringSliceVar绑定到cf.GIDMap[]string帮助文本为GID map to use for the user namespace在 Pod 创建选项实体中对应字段GIDMap []stringpkg/domain/entities/pods.go。解析与默认值pkg/domain/infra/runtime_libpod.go 的ParseIDMapping承担关键职责若只给了--uidmap而未给--gidmap会将 UID 映射列表复制为 GID 映射if len(gidMapSlice) 0 len(uidMapSlice) ! 0 { gidMapSlice uidMapSlice }若两者都未指定且当前用户非 root会生成默认单点映射0:当前gid:1最后调用util.ParseIDMap(gidMapSlice, GID, parentGIDMap)完成三元组解析并把结果写入options.GIDMap同时将HostGIDMapping置为false。写入 OCI 规范pkg/specgen/namespaces.go 将解析后的GIDMap逐条转换为 Linux 规范中的g.AddLinuxGIDMapping(hostID, containerID, size)调用最终由运行时runc / crun应用为内核的/proc/pid/gid_map。GID 5 的特殊检查使用自定义用户命名空间后容器内可见的组集合完全取决于映射表。为此 Podman 在 pkg/specgen/generate/oci_linux.go 中增加了一处防御性检查当存在自定义 GID 映射时会遍历映射表确认GID 5tty组是否被映射进容器// When using a different user namespace, check that the GID 5 is mapped inside // the container. if gid5Available (s.IDMappings ! nil len(s.IDMappings.GIDMap) 0) { mappingFound : false for _, r : range s.IDMappings.GIDMap { if r.ContainerID 5 5 r.ContainerIDr.Size { mappingFound true break } } ... }这是因为容器内进程的 supplementary groups 处理依赖/etc/group中 GID 5 的解析映射缺失可能导致tty组相关的权限与设备访问行为异常。设计映射表时建议至少覆盖0root与5tty这两个关键组。与 --uidmap 的协同UID/GID 成对映射GID 映射通常与 UID 映射uidmap.pod.md成对使用二者语法完全一致container_id:from_id[:amount]共同决定容器内进程和文件的完整身份视图podman pod create \ --name secure-pod \ --uidmap0:100000:65536 \ --gidmap0:200000:65536上述命令把宿主机100000起的 UID 段与200000起的 GID 段分别映射为容器内的0起身份段容器内的 root 在宿主机侧只是两个高位普通 ID极大降低了提权风险。注意上一节提到的“只给 UID 映射时 GID 映射自动复制”的规则如果你只想自定义 GID 而保留默认 UID请务必同时显式给出--uidmap否则 UID 也会被一并改掉。rootless 场景下的注意事项--gidmap与 rootless 模式结合时映射受限于用户可用的子 ID 范围由/etc/subgid定义。从 runtime_libpod.go 可以看到Podman 会读取内核提供的父映射rootless.GetAvailableIDMaps作为解析语法与默认填充的基准如果内核不支持用户命名空间这些文件不存在相关功能将不可用。因此映射的from_gid起始值应落在你的子 GID 范围内若不确定起始值优先使用或语法让 Podman 自动选取映射的amount总量不应超过/etc/subgid中的可用额度。实测验证inspect 与测试用例通过 inspect 验证映射Pod 创建后可借助podman inspect反查容器配置中的 GID 映射。仓库中 libpod/define/container_inspect.go 定义了相应结构type InspectIDMappings struct { UIDMap []string json:UidMap GIDMap []string json:GidMap }podman pod create --name verify-pod --gidmap0:200000:65536 podman run --pod verify-pod alpine id -G podman inspect container-id --format {{.Config.IDMappings.GidMap}}容器内执行id -G可确认组身份符合映射预期inspect 输出则会列出实际生效的 GID 映射条目。仓库测试用例佐证本仓库的 e2e 测试直接验证了--gidmap的行为test/e2e/run_userns_test.go 的podman uidmapping and gidmapping用例以run --uidmap0:100:5000 --gidmap0:200:5000 alpine echo hello验证映射后容器可正常运行test/e2e/create_test.go 的podman create --uid/gidmap --pod conflict test验证了边界场景当容器要加入一个带 infra 容器的已有 Pod 时--gidmap会因无法修改共享的用户命名空间而报错cannot set user namespace mode when joining pod with infra containertest/e2e/quadlet/remap-manual.pod 展示 Quadlet 单元中RemapUsersmanual会展开为等价的--gidmap 0:10000:10等参数说明该选项在 systemd 声明式场景下的对应关系。典型报错与排查错误信息原因处理方式--userns and --uidmap/--gidmap/--subuidname/--subgidname are mutually exclusive同时指定了--userns与--gidmap去掉--userns仅使用--gidmap/--uidmapcannot set user namespace mode when joining pod with infra container向已有 Pod 添加容器时试图自定义映射在podman pod create阶段就设置--gidmap或使用不带 infra 的 Pod 并自行配置initializing ID mappings: GID setting is malformed三元组格式错误段数非 3 的倍数、空字段或非法数字检查container_gid:from_gid[:amount]每段均为非负整数rootless 下映射不生效from_gid超出/etc/subgid可用范围改用/语法或调整子 ID 配置小结--gidmap是 Podman 精细控制 Pod 用户命名空间组身份的核心开关格式上支持单点、连续段、父映射引用与自动填充四种写法语义上覆盖整个 Pod 且与--userns严格互斥实现上贯穿 CLI 定义cmd/podman/common/create.go→ 解析合并pkg/domain/infra/runtime_libpod.go→ OCI 规范写入pkg/specgen/namespaces.go的完整链路。实际使用时记住三个要点即可在pod create/pod clone阶段设定、与--uidmap成对规划、确保覆盖 GID 0 与 GID 5 等关键组。【免费下载链接】podmanPodman: A tool for managing OCI containers and pods.项目地址: https://gitcode.com/gh_mirrors/po/podman创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门