bigywater.tech
Docker · 48

用户命名空间重映射:容器里的 root 是宿主机的谁

容器里的 root 在宿主机上是谁?userns-remap 想解决什么问题?我不能修改这台 Docker 的守护进程配置,所以这篇没有测 userns-remap 本身,而是读 uid_map、再手动建一个用户命名空间,把「里面的 root、外面的普通用户」这件事的机制测出来。

0 0 4294967295
默认容器的 uid_map:没有重映射,容器 root 就是虚拟机的 root
65534
用户命名空间里没有映射的 uid(包括真实属主 root 的文件)显示成的 uid
0 → 1000
命名空间里的 uid 0 在外面的真实 uid

默认没有重映射

实测 · 本机执行输出
  docker info SecurityOptions:[name=seccomp,profile=builtin name=cgroupns]
  容器里 /proc/self/uid_map:0          0 4294967295
  容器里 /proc/self/gid_map:0          0 4294967295
  虚拟机里看这个容器的主进程(PID 17650):root         0 17650 sleep 60
  虚拟机里 /proc/17650/uid_map:0          0 4294967295

docker info 里没有 userns,容器里 /proc/self/uid_map 是恒等映射 0 0 4294967295,在虚拟机里看容器的主进程就是 root。所以默认情况下,逃出容器的 root 进程在虚拟机里仍然是 root(这句话是对恒等映射的直接推论,我没有做逃逸实验)。

映射的机制

实测 · 本机执行输出
  进入前:uid=1000 euid=1000,/proc/self/uid_map='0          0 4294967295'
  unshare(CLONE_NEWUSER) 之后(还没写映射):uid=65534
  写入 uid 映射 0 1000 1、gid 映射 0 0 1 之后:uid=0,uid_map='0       1000          1',gid_map='0          0          1'
    stat /etc/hosts:显示的属主 uid=65534
    stat /tmp/f:显示的属主 uid=0
    stat /etc/passwd:显示的属主 uid=65534
    chown /tmp/f 0 0 成功
    chown /tmp/f 5 5 失败(5 没有映射): Invalid argument
    CapEff: 000001ffffffffff
  虚拟机里看这个进程(容器里的 PID 1):1000      1000 17954 python -u /s.py(容器里它看自己是 uid 0)

手动建的用户命名空间展示了 userns-remap 背后的机制:里面的 uid 0 对应外面的普通 uid(这里是 1000),外面没映射的 uid(如 root 的文件)在里面是 65534,不能 chown 到未映射的 uid,命名空间里有的 capability 只对命名空间自己管辖的资源有效。Docker 的 userns-remap 需要在守护进程配置里打开(userns-remap),它会为容器统一分配一段 subuid 范围,具体的行为和对 volume、镜像存储的影响,我没有验证。

分步演示

用户命名空间
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Docker Desktop(Engine 29.8.2,容器跑在 Docker Desktop 自带的 Linux 虚拟机里,内核与宿主机的 macOS 不同);镜像为 linux/arm64;需要看虚拟机内部时用特权容器 + nsenter;计时用 time / date +%s%N,数字每次运行会有小幅抖动。没有修改守护进程配置,也没有启用 userns-remap;手动用户命名空间用 Python 3.12 的 os.unshare(CLONE_NEWUSER) 并写 /proc/self/{setgroups,uid_map,gid_map};容器需要 --security-opt seccomp=unconfined 才能 unshare;宿主侧(虚拟机)的进程信息用特权容器加 nsenter 读取(方法见 c19);实验后已删除容器
    • 没有验证:真正的 userns-remap(守护进程配置、subuid / subgid 范围、镜像和卷的属主变化、与 --privileged 和 --net=host 的冲突)、rootless Docker、Kubernetes 的 user namespace、多个 uid 范围的映射
    京ICP备2026067502号-1京公网安备11010502063487号