diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go index 51bfd12b9af..7bff07b5916 100644 --- a/libcontainer/rootfs_linux.go +++ b/libcontainer/rootfs_linux.go @@ -215,6 +215,18 @@ func prepareRootfs(pipe *syncSocket, iConfig *initConfig) (err error) { return fmt.Errorf("error jailing process inside rootfs: %w", err) } + // Apply root mount propagation flags. + // This must be done after pivot_root/chroot because the mount propagation flag is applied + // to the current root ("/"), and not to the old rootfs before it becomes "/". Applying the + // flag in prepareRoot would affect the host mount namespace if the container's + // root mount is shared. + // MS_PRIVATE is skipped as rootfsParentMountPrivate() is already called. + if config.RootPropagation != 0 && config.RootPropagation&unix.MS_PRIVATE == 0 { + if err := mount("", "/", "", uintptr(config.RootPropagation), ""); err != nil { + return fmt.Errorf("unable to apply root propagation flags: %w", err) + } + } + if setupDev { if err := reOpenDevNull(); err != nil { return fmt.Errorf("error reopening /dev/null inside container: %w", err) diff --git a/tests/integration/mounts_propagation.bats b/tests/integration/mounts_propagation.bats new file mode 100644 index 00000000000..909b0d9d514 --- /dev/null +++ b/tests/integration/mounts_propagation.bats @@ -0,0 +1,22 @@ +#!/usr/bin/env bats + +load helpers + +function setup() { + requires root + setup_debian +} + +function teardown() { + teardown_bundle +} + +@test "runc run [rootfsPropagation shared]" { + update_config ' .linux.rootfsPropagation = "shared" ' + + update_config ' .process.args = ["findmnt", "--noheadings", "-o", "PROPAGATION", "/"] ' + + runc run test_shared_rootfs + [ "$status" -eq 0 ] + [ "$output" = "shared" ] +}