ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Podman `--uidmap` 完全指南:用户命名空间中的 UID 映射配置与实战

Podman `--uidmap` 完全指南:用户命名空间中的 UID 映射配置与实战 Podman--uidmap完全指南用户命名空间中的 UID 映射配置与实战【免费下载链接】podmanPodman: A tool for managing OCI containers and pods.项目地址: https://gitcode.com/gh_mirrors/po/podmanPodman 的--uidmap及其姊妹选项--gidmap允许你在新建的用户命名空间中按[flags]container_uid:from_uid[:amount]的形式精确控制宿主机 UID 与容器 UID 之间的映射关系是实现容器内用户隔离、权限控制与数据归属保持一致的核心工具。本文以 uidmap.container.md 为骨架结合 Podman 源码中--uidmap/--gidmap的实际解析与实现pkg/util/utils.go、pkg/domain/infra/runtime_libpod.go系统讲解 rootful 与 rootless 两种模式下的映射语义、宿主 ID 引用、扩展映射、u/g标记、无子 UID 场景以及 Pod 限制帮助你在真实环境中安全、准确地使用该选项。选项概述与适用范围--uidmap用于在新用户命名空间中运行容器时提供指定的 UID 映射。该选项由下列命令与配置形式共享uidmap.container.md 头部注释明确列出podman create与podman run的命令行选项--uidmapQuadlet 单元文件中的UIDMap键对应 pkg/systemd/quadlet/quadlet.goQuadlet 会把UIDMap逐条转换为--uidmap传给 Podman容器 systemd 单元文档podman-container.unit.5.md。其通用语法为--uidmap[flags]container_uid:from_uid[:amount]语法要点container_uid是容器命名空间内的起始 UIDfrom_uid是宿主机或中间命名空间侧的起始 UIDamount可选表示连续映射的 UID 个数省略时默认按1处理flags为可选前缀具体取值见后文“扩展标记”小节。关键约束来自文档首段--uidmap与--userns、--subuidname互斥。这一约束在源码中也得到了印证——cmd/podman/containers/create.go 中CreateInit检查到UIDMap/GIDMap/SubUIDName/SubGIDName任一被设置时若--userns同时被显式指定会直接报错--userns and --uidmap/--gidmap/--subuidname/--subgidname are mutually exclusive并把 userns 强制置为private。两种运行模式下的映射语义from_uid的取值取决于调用 Podman 的用户是 rootful特权还是 rootless非特权rootful 用户[flags]container_uid:host_uid[:amount]宿主机 UID 直接映射为容器 UIDrootless 用户[flags]container_uid:intermediate_uid[:amount]from_uid被解释为“中间 UID”。Rootful 映射直接映射当特权用户调用podman subcommand时--uidmap作为宿主机 UID 与容器 UID 之间的直接映射host UID - container UIDamount指定连续映射的 UID 数量。例如amount4时映射如下宿主机 UID容器 UIDfrom_uidcontainer_uidfrom_uid 1container_uid 1from_uid 2container_uid 2from_uid 3container_uid 3Rootless 映射两级映射当非特权用户rootless调用 Podman 时宿主机 UID 不会直接映射到容器 UID而是经过两步映射host UID - intermediate UID - container UID--uidmap只影响第二步中间 UID → 容器 UID第一步宿主 UID → 中间 UID由 Podman 依据/etc/subuid文件内容与调用 Podman 用户的 UID 自动推导。第一步映射的默认形式宿主机 UID中间 UIDPodman 用户的 UID0第 1 个下属 UID1第 2 个下属 UID2第 3 个下属 UID3第 n 个下属 UIDn要点要使用大于 0 的中间 UID用户必须在/etc/subuid中配置下属 UID参见subuid(5)手册。第二步映射由--uidmap配置。例如amount5时第二步映射如下中间 UID容器 UIDfrom_uidcontainer_uidfrom_uid 1container_uid 1from_uid 2container_uid 2from_uid 3container_uid 3from_uid 4container_uid 4Rootless 运行时Podman 会使用/etc/subuid文件中配置的全部范围。默认布局为当前用户 ID 映射到 rootless 用户命名空间中的 UID0之后按顺序追加每个额外范围宿主rootless 用户命名空间长度$UID011$FIRST_RANGE_ID$FIRST_RANGE_LENGTH1$FIRST_RANGE_LENGTH$SECOND_RANGE_ID$SECOND_RANGE_LENGTH引用父命名空间的宿主 ID语法作为 rootless 用户在--uidmap/--gidmap中给出的宿主 ID 默认是从中间命名空间由 Podman 生成映射而来的。有时你希望直接引用宿主命名空间的 ID此时可以手动转换podman unshare cat /proc/self/gid_map在输出中第二列是宿主 ID第一列是对应的中间 ID。找到目标宿主 ID 对应的中间 ID 后将其填入映射的from_uid位置即可。Podman 也提供了自动完成此转换的方式在映射的宿主 ID 前加符号。例如--gidmap 100000:2000:1表示 Podman 会查找与宿主 ID2000对应的中间 ID再将其映射到容器 ID100000。注意被引用的宿主 ID 必须已经被下属化即已映射进中间空间否则无法工作。当长度大于 1 时例如--gidmap 100000:2000:2Podman 会忽略中间映射的具体定义直接将宿主 ID2000、2001映射到容器 ID100000、100001。源码层面语法的处理位于 pkg/util/utils.go 的parseAutoTriple当第二个字段以开头时设置hidIsParent true并通过mapIDwithMapping在父中间映射中逐一对hidi查找对应的中间 ID长度大于 1 时则按 1 对 1 拆分每个映射sizes 1逐条加入从而保证无论中间映射如何定义目标宿主 ID 序列都被完整映射。扩展既有映射标记有时修改映射较为繁琐。例如用户最初使用--gidmap0:0:65000现在希望把父 ID1映射到容器 ID100000同时让容器 ID1保持未分配则对应的写法变为--gidmap0:0:1 --gidmap2:2:65534 --gidmap100000:1:1这种繁琐的写法可以用标记简化。放在容器 ID 之前会自动拆分break既有映射移除与给定映射冲突的赋值再追加新映射--gidmap0:0:65000 --gidmap100000:1:1标记说明标记示例说明100000:1:1扩展既有映射先拆掉冲突部分由于这种写法会产生分配空隙可以随后用另一个映射把空隙填上--gidmap0:0:65000 --gidmap100000:1:1 --gidmap1:65001:1典型场景rootless 用户可以用指定某个特定映射让 Podman 从 0 开始“填充空隙”把剩余的下属 ID 交由 Podman 自行映射。例如--gidmap100000:1:1适合“想把某个特定中间 ID 映射到某个容器 ID而其余下属 ID 由 Podman 随意映射”的需求。源码支撑与冲突拆分的核心逻辑在 pkg/util/utils.go 的breakInsert——它会先从既有映射中剔除与扩展映射在容器区间或宿主区间重叠的部分再把扩展映射追加进去随后addOneMappingpkg/util/utils.go根据标记决定映射去向最终ParseIDMappkg/util/utils.go还会调用sortAndMergeConsecutiveMappings对结果按容器 ID 排序并合并连续区间。只传--uidmap或--gidmap其一时的复制行为通常情况下下属用户 ID 与下属组 ID 是同时分配的且对同一用户而言两者数值一致。为方便起见若只给出--uidmap或--gidmap其中之一Podman 会假定该映射同时适用于 UID 和 GID并把它同时应用到两者如果只想改变其中一种映射中应带上u或g标记明确只作用于 UID 或 GID从而避免被复制到另一边。标记说明标记示例说明uu20000:2000:1该映射只作用于 UIDsgg10000:1000:1该映射只作用于 GIDs例如给定命令podman subcommand --gidmap 0:0:1000 --gidmap g2000:2000:1由于没有给出--uidmapPodman 会把--gidmap复制到--uidmap等价于podman subcommand --gidmap 0:0:1000 --gidmap 2000:2000:1 --uidmap 0:0:1000而--gidmap g2000:2000:1因带g标记不会被复制到--uidmap。这一复制行为在 pkg/domain/infra/runtime_libpod.go 的ParseIDMapping中有明确实现subgid与subuid、gidMapSlice与uidMapSlice各自成对补齐一方为空则复制另一方而u/g标记的过滤逻辑则位于 pkg/util/utils.go 的addOneMapping——解析 UID 映射时跳过不带u标记flags.UserMap为假的条目解析 GID 映射时跳过不带g标记flags.GroupMap为假的条目这正是“带标记的映射不会被复制”的底层原因。Rootless 映射额外的宿主 GIDrootless 用户可能只想把某个已经下属化到/etc/subgid的特定宿主组映射进容器而不指定其余映射。此时可以用--gidmap gcontainer_gid:host_gid组合语义如下宿主 GID 通过符号给出g标记保证该映射不会被复制到--uidmap标记保证其余容器 ID 从 0 到 n 由剩余的已下属化 GID 填充。示例假设用户属于组2000且该组已通过usermod --add-subgids 2000-2000 $USER下属化给该用户则可以这样映射--gidmapg100000:2000若再配合--group-addkeep-groupspodman run --group-addkeep-groups --gidmapg100000:2000 ...容器内的进程将属于组100000宿主机上属于组2000的文件在容器内会显示为属于组100000。没有下属 UID 时如何使用即使某用户没有在/etc/subuid中配置任何下属 UID--uidmap仍可用于把该用户的普通 UID 映射为容器 UIDpodman subcommand --uidmap $container_uid:0:1 --user $container_uid ...这里把中间 UID即0映射为容器 UID$container_uid再配合--user让容器以该 UID 运行。Pod 场景限制--uidmap不能与--pod同时使用处于 Pod 中时无法在容器级别单独设置 uidmap。这是因为 Pod 整体共享同一个用户命名空间容器级映射与 Pod 的命名空间布局相冲突。底层实现补充命令行层create/run共用 cmd/podman/containers/create.go 中的CreateInit负责--userns互斥校验与 userns 强制private。解析层--uidmap/--gidmap字符串由 pkg/util/utils.go 的ParseIDMap逐段解析支持[ug]uint32:[]uint32[:uint32]完整语法两段形式自动补amount1见 pkg/util/utils.go并经addOneMapping、breakInsert、sortAndMergeConsecutiveMappings处理标记、冲突拆分与区间合并。组装层ParseIDMappingpkg/domain/infra/runtime_libpod.go完成 uid/gid 复制、默认映射rootless 且未指定映射时默认0:uid:1以及subuidname/subgidname展开最终把映射写入libpod用户命名空间配置经 pkg/specgen/namespaces.go 转换为内核要求的 UID/GID mapping 写入用户命名空间。Quadlet 侧UIDMap/GIDMap/SubUIDMap键在 pkg/systemd/quadlet/quadlet.go 中被转换为--uidmap/--gidmap/--subuidname参数。小结--uidmap是 Podman 用户命名空间功能的核心入口rootful 下它是宿主到容器的直接映射rootless 下它只控制“中间 UID → 容器 UID”这第二步第一步由/etc/subuid自动推导。熟练掌握引用宿主 ID、扩展并拆分旧映射、u/g限定映射方向、仅传其一自动复制等语义并牢记与--userns/--subuidname/--pod的互斥关系即可在容器隔离、权限映射与数据归属场景下精准驾驭这一选项。【免费下载链接】podmanPodman: A tool for managing OCI containers and pods.项目地址: https://gitcode.com/gh_mirrors/po/podman创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进