diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go index dd52713dadf..87afe8e57dc 100644 --- a/libcontainer/rootfs_linux.go +++ b/libcontainer/rootfs_linux.go @@ -809,24 +809,46 @@ func pivotRoot(rootfs string) error { } func msMoveRoot(rootfs string) error { + // Before we move the root and chroot we have to mask all "full" sysfs and + // procfs mounts which exist on the host. This is because while the kernel + // has protections against mounting procfs if it has masks, when using + // chroot(2) the *host* procfs mount is still reachable in the mount + // namespace and the kernel permits procfs mounts inside --no-pivot + // containers. + // + // Users shouldn't be using --no-pivot except in exceptional circumstances, + // but to avoid such a trivial security flaw we apply a best-effort + // protection here. The kernel only allows a mount of a pseudo-filesystem + // like procfs or sysfs if there is a *full* mount (the root of the + // filesystem is mounted) without any other locked mount points covering a + // subtree of the mount. + // + // So we try to unmount (or mount tmpfs on top of) any mountpoint which is + // a full mount of either sysfs or procfs (since those are the most + // concerning filesystems to us). mountinfos, err := mountinfo.GetMounts(func(info *mountinfo.Info) (skip, stop bool) { - skip = false - stop = false - // Collect every sysfs and proc file systems, except those under the container rootfs - if (info.FSType != "proc" && info.FSType != "sysfs") || strings.HasPrefix(info.Mountpoint, rootfs) { + // Collect every sysfs and procfs filesystem, except for those which + // are non-full mounts or are inside the rootfs of the container. + if info.Root != "/" || + (info.FSType != "proc" && info.FSType != "sysfs") || + strings.HasPrefix(info.Mountpoint, rootfs) { skip = true - return } return }) if err != nil { return err } - for _, info := range mountinfos { p := info.Mountpoint // Be sure umount events are not propagated to the host. if err := unix.Mount("", p, "", unix.MS_SLAVE|unix.MS_REC, ""); err != nil { + if err == unix.ENOENT { + // If the mountpoint doesn't exist that means that we've + // already blasted away some parent directory of the mountpoint + // and so we don't care about this error. + continue + } return err } if err := unix.Unmount(p, unix.MNT_DETACH); err != nil { @@ -841,6 +863,8 @@ func msMoveRoot(rootfs string) error { } } } + + // Move the rootfs on top of "/" in our mount namespace. if err := unix.Mount(rootfs, "/", "", unix.MS_MOVE, ""); err != nil { return err }