diff --git a/container_test.go b/container_test.go index d1792fd12..3c0c79902 100644 --- a/container_test.go +++ b/container_test.go @@ -1219,9 +1219,9 @@ func TestContainerExitedAtSet(t *testing.T) { } status := <-statusC - code, _, _ := status.Result() + code, _, err := status.Result() if code != 0 { - t.Errorf("expected status 0 but received %d", code) + t.Errorf("expected status 0 but received %d (err: %v)", code, err) } if s, err := task.Status(ctx); err != nil { diff --git a/docs/ops.md b/docs/ops.md index 6f1457736..8f3a14dbd 100644 --- a/docs/ops.md +++ b/docs/ops.md @@ -212,4 +212,7 @@ The linux runtime allows a few options to be set to configure the shim and the r no_shim = false # display shim logs in the containerd daemon's log output shim_debug = true + # do not put the shim in its own mount namespace + # (this only need to be set on kernel < 3.18) + shim_no_newns = true ``` diff --git a/linux/bundle.go b/linux/bundle.go index 3c6f1d2fc..3dbfef98d 100644 --- a/linux/bundle.go +++ b/linux/bundle.go @@ -73,10 +73,10 @@ type bundle struct { type shimOpt func(*bundle, string, *runcopts.RuncOptions) (client.Config, client.ClientOpt) // ShimRemote is a shimOpt for connecting and starting a remote shim -func ShimRemote(shim, daemonAddress, cgroup string, debug bool, exitHandler func()) shimOpt { +func ShimRemote(shim, daemonAddress, cgroup string, nonewns, debug bool, exitHandler func()) shimOpt { return func(b *bundle, ns string, ropts *runcopts.RuncOptions) (client.Config, client.ClientOpt) { return b.shimConfig(ns, ropts), - client.WithStart(shim, b.shimAddress(ns), daemonAddress, cgroup, debug, exitHandler) + client.WithStart(shim, b.shimAddress(ns), daemonAddress, cgroup, nonewns, debug, exitHandler) } } diff --git a/linux/runtime.go b/linux/runtime.go index b9960a2d5..91d94be2f 100644 --- a/linux/runtime.go +++ b/linux/runtime.go @@ -77,6 +77,17 @@ type Config struct { NoShim bool `toml:"no_shim"` // Debug enable debug on the shim ShimDebug bool `toml:"shim_debug"` + // ShimNoMountNS prevents the runtime from putting shims into their own mount namespace. + // + // Putting the shim in its own mount namespace ensure that any mounts made + // by it in order to get the task rootfs ready will be undone regardless + // on how the shim dies. + // + // NOTE: This should only be used in kernel older than 3.18 to avoid shims + // from causing a DoS in their parent namespace due to having a copy of + // mounts previously there which would prevent unlink, rename and remove + // operations on those mountpoints. + ShimNoMountNS bool `toml:"shim_no_newns"` } // New returns a configured runtime @@ -180,7 +191,8 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts } cgroup = v.(*runcopts.CreateOptions).ShimCgroup } - shimopt = ShimRemote(r.config.Shim, r.address, cgroup, r.config.ShimDebug, func() { + exitHandler := func() { + log.G(ctx).WithField("id", id).Info("shim reaped") t, err := r.tasks.Get(ctx, id) if err != nil { // Task was never started or was already sucessfully deleted @@ -209,7 +221,9 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts "namespace": namespace, }).Warn("failed to clen up after killed shim") } - }) + } + shimopt = ShimRemote(r.config.Shim, r.address, cgroup, + r.config.ShimNoMountNS, r.config.ShimDebug, exitHandler) } s, err := bundle.NewShimClient(ctx, namespace, shimopt, ropts) diff --git a/linux/shim/client.go b/linux/shim/client.go index b748a2d37..eae946bed 100644 --- a/linux/shim/client.go +++ b/linux/shim/client.go @@ -31,7 +31,7 @@ import ( type ClientOpt func(context.Context, Config) (shim.ShimClient, io.Closer, error) // WithStart executes a new shim process -func WithStart(binary, address, daemonAddress, cgroup string, debug bool, exitHandler func()) ClientOpt { +func WithStart(binary, address, daemonAddress, cgroup string, nonewns, debug bool, exitHandler func()) ClientOpt { return func(ctx context.Context, config Config) (_ shim.ShimClient, _ io.Closer, err error) { socket, err := newSocket(address) if err != nil { @@ -44,7 +44,7 @@ func WithStart(binary, address, daemonAddress, cgroup string, debug bool, exitHa } defer f.Close() - cmd := newCommand(binary, daemonAddress, debug, config, f) + cmd := newCommand(binary, daemonAddress, nonewns, debug, config, f) ec, err := reaper.Default.Start(cmd) if err != nil { return nil, nil, errors.Wrapf(err, "failed to start shim") @@ -84,7 +84,7 @@ func WithStart(binary, address, daemonAddress, cgroup string, debug bool, exitHa } } -func newCommand(binary, daemonAddress string, debug bool, config Config, socket *os.File) *exec.Cmd { +func newCommand(binary, daemonAddress string, nonewns, debug bool, config Config, socket *os.File) *exec.Cmd { args := []string{ "--namespace", config.Namespace, "--workdir", config.WorkDir, @@ -109,7 +109,7 @@ func newCommand(binary, daemonAddress string, debug bool, config Config, socket // make sure the shim can be re-parented to system init // and is cloned in a new mount namespace because the overlay/filesystems // will be mounted by the shim - cmd.SysProcAttr = &atter + cmd.SysProcAttr = getSysProcAttr(nonewns) cmd.ExtraFiles = append(cmd.ExtraFiles, socket) if debug { cmd.Stdout = os.Stdout diff --git a/linux/shim/client_linux.go b/linux/shim/client_linux.go index de1dba0c2..515e88c47 100644 --- a/linux/shim/client_linux.go +++ b/linux/shim/client_linux.go @@ -10,9 +10,14 @@ import ( "github.com/pkg/errors" ) -var atter = syscall.SysProcAttr{ - Cloneflags: syscall.CLONE_NEWNS, - Setpgid: true, +func getSysProcAttr(nonewns bool) *syscall.SysProcAttr { + attr := syscall.SysProcAttr{ + Setpgid: true, + } + if !nonewns { + attr.Cloneflags = syscall.CLONE_NEWNS + } + return &attr } func setCgroup(cgroupPath string, cmd *exec.Cmd) error { diff --git a/linux/shim/client_unix.go b/linux/shim/client_unix.go index 719b3e6be..d478f3dd7 100644 --- a/linux/shim/client_unix.go +++ b/linux/shim/client_unix.go @@ -7,8 +7,10 @@ import ( "syscall" ) -var atter = syscall.SysProcAttr{ - Setpgid: true, +func getSysProcAttr(nonewns bool) *syscall.SysProcAttr { + return &syscall.SysProcAttr{ + Setpgid: true, + } } func setCgroup(cgroupPath string, cmd *exec.Cmd) error {