From 157d99a72786c454dfaad8b0800914cc80879aa8 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 29 Oct 2013 16:16:51 +0100 Subject: [PATCH] lxc: Work around lxc-start need for private mounts lxc-start requires / to be mounted private, otherwise the changes it does inside the container (both mounts and unmounts) will propagate out to the host. We work around this by starting up lxc-start in its own namespace where we set / to rshared. Unfortunately go can't really execute any code between clone and exec, so we can't do this in a nice way. Instead we have a horrible hack that use the unshare command, the shell and the mount command... --- container.go | 27 +++++++++++++++++++++++---- utils.go | 15 +++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/container.go b/container.go index 50bf2ec674..ac21119d1a 100644 --- a/container.go +++ b/container.go @@ -863,7 +863,13 @@ func (container *Container) Start() (err error) { return err } + var lxcStart string = "lxc-start" + if container.hostConfig.Privileged && container.runtime.capabilities.AppArmor { + lxcStart = path.Join(container.runtime.config.Root, "lxc-start-unconfined") + } + params := []string{ + lxcStart, "-n", container.ID, "-f", container.lxcConfigPath(), "--", @@ -956,11 +962,24 @@ func (container *Container) Start() (err error) { params = append(params, "--", container.Path) params = append(params, container.Args...) - var lxcStart string = "lxc-start" - if container.hostConfig.Privileged && container.runtime.capabilities.AppArmor { - lxcStart = path.Join(container.runtime.config.Root, "lxc-start-unconfined") + if RootIsShared() { + // lxc-start really needs / to be non-shared, or all kinds of stuff break + // when lxc-start unmount things and those unmounts propagate to the main + // mount namespace. + // What we really want is to clone into a new namespace and then + // mount / MS_REC|MS_SLAVE, but since we can't really clone or fork + // without exec in go we have to do this horrible shell hack... + shellString := + "mount --make-rslave /; exec " + + utils.ShellQuoteArguments(params) + + params = []string{ + "unshare", "-m", "--", "/bin/sh", "-c", shellString, + } } - container.cmd = exec.Command(lxcStart, params...) + + container.cmd = exec.Command(params[0], params[1:]...) + // Setup logging of stdout and stderr to disk if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil { return err diff --git a/utils.go b/utils.go index 81715881ae..22d83d6bee 100644 --- a/utils.go +++ b/utils.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/dotcloud/docker/namesgenerator" "github.com/dotcloud/docker/utils" + "io/ioutil" "strconv" "strings" ) @@ -301,6 +302,20 @@ func parseLink(rawLink string) (map[string]string, error) { return utils.PartParser("name:alias", rawLink) } +func RootIsShared() bool { + if data, err := ioutil.ReadFile("/proc/self/mountinfo"); err == nil { + for _, line := range strings.Split(string(data), "\n") { + cols := strings.Split(line, " ") + if len(cols) >= 6 && cols[4] == "/" { + return strings.HasPrefix(cols[6], "shared") + } + } + } + + // No idea, probably safe to assume so + return true +} + type checker struct { runtime *Runtime }