1

我的问题的背景是我的Linux 内核命名空间发现 Go 包 lxkns的一组测试用例,其中我在测试容器中创建了一个新的子用户命名空间以及一个新的子 PID 命名空间。然后我需要重新挂载/proc,否则我会看到错误的进程信息并且无法查找正确的进程相关信息,例如新的子用户+PID命名空间内的测试进程的命名空间(不诉诸游击战术)。

测试工具/测试设置基本上是这样并且没有失败--privileged(我正在简化为所有大写并关闭 seccomp 和 apparmor 以切入真正的肉):

docker run -it --rm --name closedboxx --cap-add ALL --security-opt seccomp=unconfined --security-opt apparmor=unconfined busybox unshare -Umpfr mount -t proc /proc proc
mount: permission denied (are you root?)

当然,阻力最小且美观最少的路径是使用--privileged,这将完成工作,因为这是一个一次性的测试容器(也许完全缺乏它是​​美丽的)。

最近,我开始意识到 Docker 的,它(afaik)在生成的 OCI/runc 容器规范中--security-opt systempaths=unconfined转换为空的readonlyPaths 。以下 Docker 运行命令根据需要成功,它只是在示例中静默返回,因此正确执行:

docker run -it --rm --name closedboxx --cap-add ALL --security-opt seccomp=unconfined --security-opt apparmor=unconfined --security-opt systempaths=unconfined busybox unshare -Umpfr mount -t proc /proc proc

如果设置失败,在不带--privilege和不带的情况下运行时--security-opt systempaths=unconfined,子用户内的挂载和容器内的 PID 命名空间如下所示:

docker run -it --rm --name closedboxx --cap-add ALL --security-opt seccomp=unconfined --security-opt apparmor=unconfined busybox unshare -Umpfr cat /proc/1/mountinfo
693 678 0:46 / / rw,relatime - overlay overlay rw,lowerdir=/var/lib/docker/overlay2/l/AOY3ZSL2FQEO77CCDBKDOPEK7M:/var/lib/docker/overlay2/l/VNX7PING7ZLTIPXRDFSBMIOKKU,upperdir=/var/lib/docker/overlay2/60e8ad10362e49b621d2f3d603845ee24bda62d6d77de96a37ea0001c8454546/diff,workdir=/var/lib/docker/overlay2/60e8ad10362e49b621d2f3d603845ee24bda62d6d77de96a37ea0001c8454546/work,xino=off
694 693 0:50 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw
695 694 0:50 /bus /proc/bus ro,relatime - proc proc rw
696 694 0:50 /fs /proc/fs ro,relatime - proc proc rw
697 694 0:50 /irq /proc/irq ro,relatime - proc proc rw
698 694 0:50 /sys /proc/sys ro,relatime - proc proc rw
699 694 0:50 /sysrq-trigger /proc/sysrq-trigger ro,relatime - proc proc rw
700 694 0:51 /null /proc/kcore rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
701 694 0:51 /null /proc/keys rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
702 694 0:51 /null /proc/latency_stats rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
703 694 0:51 /null /proc/timer_list rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
704 694 0:51 /null /proc/sched_debug rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
705 694 0:56 / /proc/scsi ro,relatime - tmpfs tmpfs ro
706 693 0:51 / /dev rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755
707 706 0:52 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666
708 706 0:49 / /dev/mqueue rw,nosuid,nodev,noexec,relatime - mqueue mqueue rw
709 706 0:55 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,size=65536k
710 706 0:52 /0 /dev/console rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666
711 693 0:53 / /sys ro,nosuid,nodev,noexec,relatime - sysfs sysfs ro
712 711 0:54 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755
713 712 0:28 /docker/eebfacfdc6e0e34c4e62d9f162bdd7c04b232ba2d1f5327eaf7e00011d0235c0 /sys/fs/cgroup/systemd ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,xattr,name=systemd
714 712 0:31 /docker/eebfacfdc6e0e34c4e62d9f162bdd7c04b232ba2d1f5327eaf7e00011d0235c0 /sys/fs/cgroup/cpuset ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,cpuset
715 712 0:32 /docker/eebfacfdc6e0e34c4e62d9f162bdd7c04b232ba2d1f5327eaf7e00011d0235c0 /sys/fs/cgroup/net_cls,net_prio ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,net_cls,net_prio
716 712 0:33 /docker/eebfacfdc6e0e34c4e62d9f162bdd7c04b232ba2d1f5327eaf7e00011d0235c0 /sys/fs/cgroup/memory ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,memory
717 712 0:34 /docker/eebfacfdc6e0e34c4e62d9f162bdd7c04b232ba2d1f5327eaf7e00011d0235c0 /sys/fs/cgroup/perf_event ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,perf_event
718 712 0:35 /docker/eebfacfdc6e0e34c4e62d9f162bdd7c04b232ba2d1f5327eaf7e00011d0235c0 /sys/fs/cgroup/devices ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,devices
719 712 0:36 /docker/eebfacfdc6e0e34c4e62d9f162bdd7c04b232ba2d1f5327eaf7e00011d0235c0 /sys/fs/cgroup/blkio ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,blkio
720 712 0:37 /docker/eebfacfdc6e0e34c4e62d9f162bdd7c04b232ba2d1f5327eaf7e00011d0235c0 /sys/fs/cgroup/pids ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,pids
721 712 0:38 / /sys/fs/cgroup/rdma ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,rdma
722 712 0:39 /docker/eebfacfdc6e0e34c4e62d9f162bdd7c04b232ba2d1f5327eaf7e00011d0235c0 /sys/fs/cgroup/freezer ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,freezer
723 712 0:40 /docker/eebfacfdc6e0e34c4e62d9f162bdd7c04b232ba2d1f5327eaf7e00011d0235c0 /sys/fs/cgroup/cpu,cpuacct ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,cpu,cpuacct
724 711 0:57 / /sys/firmware ro,relatime - tmpfs tmpfs ro
725 693 8:2 /var/lib/docker/containers/eebfacfdc6e0e34c4e62d9f162bdd7c04b232ba2d1f5327eaf7e00011d0235c0/resolv.conf /etc/resolv.conf rw,relatime - ext4 /dev/sda2 rw,stripe=256
944 693 8:2 /var/lib/docker/containers/eebfacfdc6e0e34c4e62d9f162bdd7c04b232ba2d1f5327eaf7e00011d0235c0/hostname /etc/hostname rw,relatime - ext4 /dev/sda2 rw,stripe=256
1352 693 8:2 /var/lib/docker/containers/eebfacfdc6e0e34c4e62d9f162bdd7c04b232ba2d1f5327eaf7e00011d0235c0/hosts /etc/hosts rw,relatime - ext4 /dev/sda2 rw,stripe=256
  1. 究竟是什么机制阻止了procfson的新安装/proc
  2. 是什么阻止我卸载/proc/kcore等?
4

1 回答 1

1

更多的挖掘发现了“关于在新创建的安装命名空间中安装和卸载继承的安装”的答案,它指向正确的方向,但需要额外的解释(尤其是基于关于安装命名空间是分层的误导性段落来自 Michael Kerrisk 前段时间修复的手册页)。

我们的起点是在runc设置(测试)容器时,为了屏蔽系统路径,尤其是在容器的未来/proc树中,它会创建一组新的挂载来/dev/null使用tmpfs. 这导致procfs安装在/proc以及其他子安装座上。

现在测试容器启动并且在某个时候一个进程取消共享到一个新的用户命名空间。请记住,这个新的用户命名空间(再次)属于 UID 为 0 的(真实)root 用户,因为默认的 Docker 安装不会启用在新用户命名空间中运行的容器。

接下来,测试过程也unshares到一个新的挂载命名空间,所以这个新的挂载命名空间属于新创建的用户命名空间,但不属于初始用户命名空间。根据mount_namespaces(7)中的“对挂载命名空间的限制”部分:

如果新命名空间和从中复制挂载点列表的命名空间属于不同的用户命名空间,则新的挂载命名空间被认为是特权较少的。

请注意这里的判据是:“捐赠者”挂载命名空间和新挂载命名空间有不同的用户命名空间;他们是否拥有相同的所有者用户 (UID) 并不重要。

现在重要的线索是:

来自特权更高的挂载命名空间的作为单个单元的挂载被锁定在一起,并且不能在特权较低的挂载命名空间中分开。(unshare(2) CLONE_NEWNS 操作将原始挂载命名空间中的所有挂载作为一个单元,并且在挂载命名空间之间传播的递归挂载作为一个单元传播。)

由于现在无法再分离/proc挂载点和掩蔽子挂载,因此无法(重新)挂载/proc(问题 1)。在同样的意义上, unmount 是不可能的/proc/kcore,因为这将允许取消屏蔽(问题 2)。

--security-opt systempaths=unconfined现在,当使用此方法部署测试容器时,只会产生一个 /proc挂载,而没有任何掩蔽子挂载。因此,根据上面引用的手册页规则,只有一个挂载允许我们(重新)挂载,这取决于CAP_SYS_ADMIN包括挂载在内的能力(除了大量其他有趣的功能)。

请注意,当仍在原始(=初始)用户命名空间中并且拥有(毫不奇怪)时,可以卸载容器内的屏蔽/proc/CAP_SYS_ADMIN路径。(b)lock 只在一个单独的用户命名空间中启动,因此一些项目努力在他们自己的新用户命名空间中部署容器(不幸的是,这不仅对容器网络产生了影响)。

于 2021-01-30T15:18:20.247 回答