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