From afa8443118347a1f909941aec2732039d28a9034 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Tue, 17 Feb 2015 21:50:43 -0800 Subject: [PATCH] Remove userns sidecar process Move the network setup back into the standard init even for user namespaces now that mounts are fully supported and working. Signed-off-by: Michael Crosby --- error.go | 8 ---- generic_error.go | 11 +++-- linux_container.go | 1 - linux_factory.go | 4 +- linux_init.go | 14 +----- linux_process.go | 40 +--------------- linux_rootfs.go | 27 +++++------ linux_userns_init.go | 91 ------------------------------------ linux_userns_sidecar_init.go | 23 --------- nsinit/config.go | 9 ++-- nsinit/exec.go | 15 +++--- nsinit/tty.go | 5 +- 12 files changed, 40 insertions(+), 208 deletions(-) delete mode 100644 linux_userns_init.go delete mode 100644 linux_userns_sidecar_init.go diff --git a/error.go b/error.go index 85b0dcaf0..37e99366f 100644 --- a/error.go +++ b/error.go @@ -57,11 +57,3 @@ type Error interface { // Returns the error code for this error. Code() ErrorCode } - -type initError struct { - Message string `json:"message,omitempty"` -} - -func (i initError) Error() string { - return i.Message -} diff --git a/generic_error.go b/generic_error.go index ff614ee66..05ab0a9d3 100644 --- a/generic_error.go +++ b/generic_error.go @@ -11,8 +11,8 @@ import ( var errorTemplate = template.Must(template.New("error").Parse(`Timestamp: {{.Timestamp}} Code: {{.ECode}} -{{if .Err }} -Message: {{.Err.Error}} +{{if .Message }} +Message: {{.Message}} {{end}} Frames:{{range $i, $frame := .Stack.Frames}} --- @@ -28,6 +28,7 @@ func newGenericError(err error, c ErrorCode) Error { return &genericError{ Timestamp: time.Now(), Err: err, + Message: err.Error(), ECode: c, Stack: stacktrace.Capture(1), } @@ -41,6 +42,7 @@ func newSystemError(err error) Error { Timestamp: time.Now(), Err: err, ECode: SystemError, + Message: err.Error(), Stack: stacktrace.Capture(1), } } @@ -48,12 +50,13 @@ func newSystemError(err error) Error { type genericError struct { Timestamp time.Time ECode ErrorCode - Err error + Err error `json:"-"` + Message string Stack stacktrace.Stacktrace } func (e *genericError) Error() string { - return fmt.Sprintf("[%d] %s: %s", e.ECode, e.ECode, e.Err) + return fmt.Sprintf("[%d] %s: %s", e.ECode, e.ECode, e.Message) } func (e *genericError) Code() ErrorCode { diff --git a/linux_container.go b/linux_container.go index 01e34769a..52d9af87d 100644 --- a/linux_container.go +++ b/linux_container.go @@ -147,7 +147,6 @@ func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, c if cmd.SysProcAttr.Credential == nil { cmd.SysProcAttr.Credential = &syscall.Credential{} } - t = "_LIBCONTAINER_INITTYPE=userns" } cmd.Env = append(cmd.Env, t) cmd.SysProcAttr.Cloneflags = cloneFlags diff --git a/linux_factory.go b/linux_factory.go index 879ad4274..d0fca3b41 100644 --- a/linux_factory.go +++ b/linux_factory.go @@ -166,9 +166,7 @@ func (l *LinuxFactory) StartInitialization(pipefd uintptr) (err error) { // ensure that any data sent from the parent is consumed so it doesn't // receive ECONNRESET when the child writes to the pipe. ioutil.ReadAll(pipe) - if err := json.NewEncoder(pipe).Encode(initError{ - Message: err.Error(), - }); err != nil { + if err := json.NewEncoder(pipe).Encode(newSystemError(err)); err != nil { panic(err) } } diff --git a/linux_init.go b/linux_init.go index 4755edb08..37891102f 100644 --- a/linux_init.go +++ b/linux_init.go @@ -21,10 +21,8 @@ import ( type initType string const ( - initSetns initType = "setns" - initStandard initType = "standard" - initUserns initType = "userns" - initUsernsSetup initType = "userns_setup" + initSetns initType = "setns" + initStandard initType = "standard" ) type pid struct { @@ -67,14 +65,6 @@ func newContainerInit(t initType, pipe *os.File) (initer, error) { return &linuxSetnsInit{ config: config, }, nil - case initUserns: - return &linuxUsernsInit{ - config: config, - }, nil - case initUsernsSetup: - return &linuxUsernsSideCar{ - config: config, - }, nil case initStandard: return &linuxStandardInit{ config: config, diff --git a/linux_process.go b/linux_process.go index 83addf240..0fd33b92d 100644 --- a/linux_process.go +++ b/linux_process.go @@ -4,13 +4,11 @@ package libcontainer import ( "encoding/json" - "fmt" "io" "os" "os/exec" "syscall" - log "github.com/Sirupsen/logrus" "github.com/docker/libcontainer/cgroups" "github.com/docker/libcontainer/system" ) @@ -145,28 +143,12 @@ func (p *initProcess) start() error { if err := p.createNetworkInterfaces(); err != nil { return newSystemError(err) } - // Start the setup process to setup the init process - if p.cmd.SysProcAttr.Cloneflags&syscall.CLONE_NEWUSER != 0 { - parent, err := p.newUsernsSetupProcess() - if err != nil { - return newSystemError(err) - } - if err := parent.start(); err != nil { - if err := parent.terminate(); err != nil { - log.Warn(err) - } - return err - } - if _, err := parent.wait(); err != nil { - return newSystemError(err) - } - } if err := p.sendConfig(); err != nil { return newSystemError(err) } // wait for the child process to fully complete and receive an error message // if one was encoutered - var ierr *initError + var ierr *genericError if err := json.NewDecoder(p.parentPipe).Decode(&ierr); err != nil && err != io.EOF { return newSystemError(err) } @@ -229,26 +211,6 @@ func (p *initProcess) createNetworkInterfaces() error { return nil } -func (p *initProcess) newUsernsSetupProcess() (parentProcess, error) { - parentPipe, childPipe, err := newPipe() - if err != nil { - return nil, newSystemError(err) - } - cmd := exec.Command(p.cmd.Args[0], p.cmd.Args[1:]...) - cmd.ExtraFiles = []*os.File{childPipe} - cmd.Dir = p.cmd.Dir - cmd.Env = append(cmd.Env, - fmt.Sprintf("_LIBCONTAINER_INITPID=%d", p.pid()), - fmt.Sprintf("_LIBCONTAINER_INITTYPE=userns_setup"), - ) - return &setnsProcess{ - cmd: cmd, - childPipe: childPipe, - parentPipe: parentPipe, - config: p.config, - }, nil -} - func (p *initProcess) signal(s os.Signal) error { return p.cmd.Process.Signal(s) } diff --git a/linux_rootfs.go b/linux_rootfs.go index 74c711492..7aba21bdf 100644 --- a/linux_rootfs.go +++ b/linux_rootfs.go @@ -199,30 +199,27 @@ func createDevices(config *configs.Config) error { // Creates the device node in the rootfs of the container. func createDeviceNode(rootfs string, node *configs.Device) error { - var ( - dest = filepath.Join(rootfs, node.Path) - parent = filepath.Dir(dest) - ) - if err := os.MkdirAll(parent, 0755); err != nil { + dest := filepath.Join(rootfs, node.Path) + if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil { return err } if err := mknodDevice(dest, node); err != nil { if os.IsExist(err) { return nil } + if err != syscall.EPERM { + return err + } // containers running in a user namespace are not allowed to mknod // devices so we can just bind mount it from the host. - if err == syscall.EPERM { - f, err := os.Create(dest) - if err != nil { - if os.IsExist(err) { - return nil - } - return err - } - f.Close() - return syscall.Mount(node.Path, dest, "bind", syscall.MS_BIND, "") + f, err := os.Create(dest) + if err != nil && !os.IsExist(err) { + return err } + if f != nil { + f.Close() + } + return syscall.Mount(node.Path, dest, "bind", syscall.MS_BIND, "") } return nil } diff --git a/linux_userns_init.go b/linux_userns_init.go deleted file mode 100644 index 7f5806fe5..000000000 --- a/linux_userns_init.go +++ /dev/null @@ -1,91 +0,0 @@ -// +build linux - -package libcontainer - -import ( - "syscall" - - "github.com/docker/libcontainer/apparmor" - "github.com/docker/libcontainer/configs" - "github.com/docker/libcontainer/label" - "github.com/docker/libcontainer/system" -) - -type linuxUsernsInit struct { - config *initConfig -} - -func (l *linuxUsernsInit) Init() error { - // join any namespaces via a path to the namespace fd if provided - if err := joinExistingNamespaces(l.config.Config.Namespaces); err != nil { - return newSystemError(err) - } - consolePath := l.config.Config.Console - if consolePath != "" { - // We use the containerConsolePath here, because the console has already been - // setup by the side car process for the user namespace scenario. - console := newConsoleFromPath(consolePath) - if err := console.dupStdio(); err != nil { - return newSystemError(err) - } - } - if _, err := syscall.Setsid(); err != nil { - return newSystemError(err) - } - if consolePath != "" { - if err := system.Setctty(); err != nil { - return newSystemError(err) - } - } - if l.config.Cwd == "" { - l.config.Cwd = "/" - } - if err := setupRlimits(l.config.Config); err != nil { - return newSystemError(err) - } - // InitializeMountNamespace() can be executed only for a new mount namespace - if l.config.Config.Namespaces.Contains(configs.NEWNS) { - if err := setupRootfs(l.config.Config); err != nil { - return newSystemError(err) - } - } - if hostname := l.config.Config.Hostname; hostname != "" { - if err := syscall.Sethostname([]byte(hostname)); err != nil { - return newSystemError(err) - } - } - if err := apparmor.ApplyProfile(l.config.Config.AppArmorProfile); err != nil { - return newSystemError(err) - } - if err := label.SetProcessLabel(l.config.Config.ProcessLabel); err != nil { - return newSystemError(err) - } - for _, path := range l.config.Config.ReadonlyPaths { - if err := remountReadonly(path); err != nil { - return newSystemError(err) - } - } - for _, path := range l.config.Config.MaskPaths { - if err := maskFile(path); err != nil { - return newSystemError(err) - } - } - pdeath, err := system.GetParentDeathSignal() - if err != nil { - return newSystemError(err) - } - if err := finalizeNamespace(l.config); err != nil { - return newSystemError(err) - } - // finalizeNamespace can change user/group which clears the parent death - // signal, so we restore it here. - if err := pdeath.Restore(); err != nil { - return newSystemError(err) - } - // Signal self if parent is already dead. Does nothing if running in a new - // PID namespace, as Getppid will always return 0. - if syscall.Getppid() == 1 { - return syscall.Kill(syscall.Getpid(), syscall.SIGKILL) - } - return system.Execv(l.config.Args[0], l.config.Args[0:], l.config.Env) -} diff --git a/linux_userns_sidecar_init.go b/linux_userns_sidecar_init.go deleted file mode 100644 index 119abfabc..000000000 --- a/linux_userns_sidecar_init.go +++ /dev/null @@ -1,23 +0,0 @@ -// +build linux - -package libcontainer - -// linuxUsernsSideCar is run to setup mounts and networking related operations -// for a user namespace enabled process as a user namespace root doesn't -// have permissions to perform these operations. -// The setup process joins all the namespaces of user namespace enabled init -// except the user namespace, so it run as root in the root user namespace -// to perform these operations. -type linuxUsernsSideCar struct { - config *initConfig -} - -func (l *linuxUsernsSideCar) Init() error { - if err := setupNetwork(l.config); err != nil { - return err - } - if err := setupRoute(l.config.Config); err != nil { - return err - } - return nil -} diff --git a/nsinit/config.go b/nsinit/config.go index 7dbc8a78e..5663f5560 100644 --- a/nsinit/config.go +++ b/nsinit/config.go @@ -33,6 +33,7 @@ var createFlags = []cli.Flag{ cli.StringFlag{Name: "mount-label", Usage: "set the mount label"}, cli.StringFlag{Name: "rootfs", Usage: "set the rootfs"}, cli.IntFlag{Name: "userns-root-uid", Usage: "set the user namespace root uid"}, + cli.StringFlag{Name: "hostname", Value: "nsinit", Usage: "hostname value for the container"}, cli.StringFlag{Name: "net", Value: "", Usage: "network namespace"}, cli.StringFlag{Name: "ipc", Value: "", Usage: "ipc namespace"}, cli.StringFlag{Name: "pid", Value: "", Usage: "pid namespace"}, @@ -149,7 +150,7 @@ func modify(config *configs.Config, context *cli.Context) { if !config.Namespaces.Contains(value) { config.Namespaces.Add(value, "") } - if v == "net" { + if flag == "net" { config.Networks = []*configs.Network{ { Type: "loopback", @@ -158,6 +159,9 @@ func modify(config *configs.Config, context *cli.Context) { }, } } + if flag == "uts" { + config.Hostname = context.String("hostname") + } default: config.Namespaces.Remove(value) config.Namespaces.Add(value, v) @@ -218,8 +222,7 @@ func getTemplate() *configs.Config { AllowAllDevices: false, AllowedDevices: configs.DefaultAllowedDevices, }, - Devices: configs.DefaultAutoCreatedDevices, - Hostname: "nsinit", + Devices: configs.DefaultAutoCreatedDevices, MaskPaths: []string{ "/proc/kcore", }, diff --git a/nsinit/exec.go b/nsinit/exec.go index b8d210012..52f251440 100644 --- a/nsinit/exec.go +++ b/nsinit/exec.go @@ -36,18 +36,21 @@ func execAction(context *cli.Context) { if err != nil { fatal(err) } - tty, err := newTty(context) + config, err := loadConfig(context) + if err != nil { + fatal(err) + } + rootuid, err := config.HostUID() + if err != nil { + fatal(err) + } + tty, err := newTty(context, rootuid) if err != nil { fatal(err) } created := false container, err := factory.Load(context.String("id")) if err != nil { - config, err := loadConfig(context) - if err != nil { - tty.Close() - fatal(err) - } if tty.console != nil { config.Console = tty.console.Path() } diff --git a/nsinit/tty.go b/nsinit/tty.go index d0d343756..5d417b98d 100644 --- a/nsinit/tty.go +++ b/nsinit/tty.go @@ -9,10 +9,9 @@ import ( "github.com/docker/libcontainer" ) -func newTty(context *cli.Context) (*tty, error) { +func newTty(context *cli.Context, rootuid int) (*tty, error) { if context.Bool("tty") { - rootid := context.Int("userns-root-uid") - console, err := libcontainer.NewConsole(rootid, rootid) + console, err := libcontainer.NewConsole(rootuid, rootuid) if err != nil { return nil, err }