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 <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby
2015-02-17 21:50:43 -08:00
parent 339edce03e
commit afa8443118
12 changed files with 40 additions and 208 deletions

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -23,8 +23,6 @@ type initType string
const (
initSetns initType = "setns"
initStandard initType = "standard"
initUserns initType = "userns"
initUsernsSetup initType = "userns_setup"
)
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,

View File

@@ -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)
}

View File

@@ -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
}
// 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
}
if err != syscall.EPERM {
return err
}
f.Close()
return syscall.Mount(node.Path, dest, "bind", syscall.MS_BIND, "")
// containers running in a user namespace are not allowed to mknod
// devices so we can just bind mount it from the host.
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
}

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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)
@@ -219,7 +223,6 @@ func getTemplate() *configs.Config {
AllowedDevices: configs.DefaultAllowedDevices,
},
Devices: configs.DefaultAutoCreatedDevices,
Hostname: "nsinit",
MaskPaths: []string{
"/proc/kcore",
},

View File

@@ -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()
}

View File

@@ -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
}