userns: prevent /sys/kernel/* paths in the container

when we run in a user namespace, there are cases where we have not
enough privileges to mount a fresh sysfs on /sys.  To circumvent this
limitation, we rbind /sys from the host.  This carries inside of the
container also some mounts we probably don't want to.  We are also
limited by the kernel to use rbind instead of bind, as allowing a bind
would uncover paths that were not previously visible.

This is a slimmed down version of the intermediate mount namespace
logic we had before, where we only set /sys to slave, so the umounts
done to the storage by the cleanup process are propagated back to the
host.  We also don't setup any new directory, so there is no
additional cleanup to do.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
This commit is contained in:
Giuseppe Scrivano
2019-04-11 11:51:02 +02:00
parent 6cd6eb6768
commit b7800889fb
2 changed files with 61 additions and 0 deletions

View File

@ -3,15 +3,20 @@
package libpod
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"syscall"
"github.com/containerd/cgroups"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/utils"
pmount "github.com/containers/storage/pkg/mount"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
@ -91,6 +96,54 @@ func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string, restor
return err
}
}
// if we are running a non privileged container, be sure to umount some kernel paths so they are not
// bind mounted inside the container at all.
if !ctr.config.Privileged && !rootless.IsRootless() {
ch := make(chan error)
go func() {
runtime.LockOSThread()
err := func() error {
fd, err := os.Open(fmt.Sprintf("/proc/%d/task/%d/ns/mnt", os.Getpid(), unix.Gettid()))
if err != nil {
return err
}
defer fd.Close()
// create a new mountns on the current thread
if err = unix.Unshare(unix.CLONE_NEWNS); err != nil {
return err
}
defer unix.Setns(int(fd.Fd()), unix.CLONE_NEWNS)
// don't spread our mounts around. We are setting only /sys to be slave
// so that the cleanup process is still able to umount the storage and the
// changes are propagated to the host.
err = unix.Mount("/sys", "/sys", "none", unix.MS_REC|unix.MS_SLAVE, "")
if err != nil {
return errors.Wrapf(err, "cannot make /sys slave")
}
mounts, err := pmount.GetMounts()
if err != nil {
return err
}
for _, m := range mounts {
if !strings.HasPrefix(m.Mountpoint, "/sys/kernel") {
continue
}
err = unix.Unmount(m.Mountpoint, 0)
if err != nil {
return errors.Wrapf(err, "cannot unmount %s", m.Mountpoint)
}
}
return r.createOCIContainer(ctr, cgroupParent, restoreOptions)
}()
ch <- err
}()
err := <-ch
return err
}
}
return r.createOCIContainer(ctr, cgroupParent, restoreOptions)
}

View File

@ -31,4 +31,12 @@ echo $rand | 0 | $rand
done < <(parse_table "$tests")
}
@test "podman run - uidmapping has no /sys/kernel mounts" {
run_podman $expected_rc run --uidmapping 0:100:10000 $IMAGE mount | grep /sys/kernel
is "$output" "" "podman run $cmd - output"
run_podman $expected_rc run --net host --uidmapping 0:100:10000 $IMAGE mount | grep /sys/kernel
is "$output" "" "podman run $cmd - output"
}
# vim: filetype=sh