Skip to content
This repository was archived by the owner on Dec 13, 2018. It is now read-only.

Make usernamespaces work without sidecar process #385

Merged
merged 2 commits into from
Feb 19, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
11 changes: 7 additions & 4 deletions generic_error.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
---
Expand All @@ -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),
}
Expand All @@ -41,19 +42,21 @@ func newSystemError(err error) Error {
Timestamp: time.Now(),
Err: err,
ECode: SystemError,
Message: err.Error(),
Stack: stacktrace.Capture(1),
}
}

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 {
Expand Down
20 changes: 8 additions & 12 deletions linux_console.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,9 @@ import (
"github.com/docker/libcontainer/label"
)

const (
containerConsolePath string = "/dev/console"
)

// NewConsole returns an initalized console that can be used within a container by copying bytes
// from the master side to the slave that is attached as the tty for the container's init process.
func NewConsole() (Console, error) {
func NewConsole(uid, gid int) (Console, error) {
master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
if err != nil {
return nil, err
Expand All @@ -30,6 +26,12 @@ func NewConsole() (Console, error) {
if err := unlockpt(master); err != nil {
return nil, err
}
if err := os.Chmod(console, 0600); err != nil {
return nil, err
}
if err := os.Chown(console, uid, gid); err != nil {
return nil, err
}
return &linuxConsole{
slavePath: console,
master: master,
Expand Down Expand Up @@ -78,16 +80,10 @@ func (c *linuxConsole) Close() error {
func (c *linuxConsole) mount(rootfs, mountLabel string, uid, gid int) error {
oldMask := syscall.Umask(0000)
defer syscall.Umask(oldMask)
if err := os.Chmod(c.slavePath, 0600); err != nil {
return err
}
if err := os.Chown(c.slavePath, uid, gid); err != nil {
return err
}
if err := label.SetFileLabel(c.slavePath, mountLabel); err != nil {
return err
}
dest := filepath.Join(rootfs, containerConsolePath)
dest := filepath.Join(rootfs, "/dev/console")
f, err := os.Create(dest)
if err != nil && !os.IsExist(err) {
return err
Expand Down
1 change: 0 additions & 1 deletion linux_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 1 addition & 3 deletions linux_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down
14 changes: 2 additions & 12 deletions linux_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down
40 changes: 1 addition & 39 deletions linux_process.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
75 changes: 40 additions & 35 deletions linux_rootfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,52 +35,49 @@ var baseMounts = []*configs.Mount{
Destination: "/dev/pts",
Device: "devpts",
Flags: syscall.MS_NOSUID | syscall.MS_NOEXEC,
Data: "newinstance,ptmxmode=0666,mode=620,gid=5",
Data: "newinstance,ptmxmode=0666,mode=0620,gid=5",
},
}

// setupRootfs sets up the devices, mount points, and filesystems for use inside a
// new mount namespace.
func setupRootfs(config *configs.Config) (err error) {
if err := prepareRoot(config); err != nil {
return err
return newSystemError(err)
}
for _, m := range append(baseMounts, config.Mounts...) {
if err := mount(m, config.Rootfs, config.MountLabel); err != nil {
return err
return newSystemError(err)
}
}
if err := createDevices(config); err != nil {
return err
return newSystemError(err)
}
if err := setupPtmx(config); err != nil {
return err
return newSystemError(err)
}
// stdin, stdout and stderr could be pointing to /dev/null from parent namespace.
// Re-open them inside this namespace.
// FIXME: Need to fix this for user namespaces.
if !config.Namespaces.Contains(configs.NEWUSER) {
if err := reOpenDevNull(config.Rootfs); err != nil {
return err
}
// re-open them inside this namespace.
if err := reOpenDevNull(config.Rootfs); err != nil {
return newSystemError(err)
}
if err := setupDevSymlinks(config.Rootfs); err != nil {
return err
return newSystemError(err)
}
if err := syscall.Chdir(config.Rootfs); err != nil {
return err
return newSystemError(err)
}
if config.NoPivotRoot {
err = msMoveRoot(config.Rootfs)
} else {
err = pivotRoot(config.Rootfs, config.PivotDir)
}
if err != nil {
return err
return newSystemError(err)
}
if config.Readonlyfs {
if err := setReadonly(); err != nil {
return err
return newSystemError(err)
}
}
syscall.Umask(0022)
Expand Down Expand Up @@ -202,13 +199,32 @@ 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we pass a host bind flag here instead of relying on EPERM?

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
}

func mknodDevice(dest string, node *configs.Device) error {
fileMode := node.FileMode
switch node.Type {
case 'c':
Expand All @@ -218,13 +234,10 @@ func createDeviceNode(rootfs string, node *configs.Device) error {
default:
return fmt.Errorf("%c is not a valid device type for device %s", node.Type, node.Path)
}
if err := syscall.Mknod(dest, uint32(fileMode), node.Mkdev()); err != nil && !os.IsExist(err) {
return fmt.Errorf("mknod %s %s", node.Path, err)
}
if err := syscall.Chown(dest, int(node.Uid), int(node.Gid)); err != nil {
return fmt.Errorf("chown %s to %d:%d", node.Path, node.Uid, node.Gid)
if err := syscall.Mknod(dest, uint32(fileMode), node.Mkdev()); err != nil {
return err
}
return nil
return syscall.Chown(dest, int(node.Uid), int(node.Gid))
}

func prepareRoot(config *configs.Config) error {
Expand All @@ -251,16 +264,8 @@ func setupPtmx(config *configs.Config) error {
return fmt.Errorf("symlink dev ptmx %s", err)
}
if config.Console != "" {
uid, err := config.HostUID()
if err != nil {
return err
}
gid, err := config.HostGID()
if err != nil {
return err
}
console := newConsoleFromPath(config.Console)
return console.mount(config.Rootfs, config.MountLabel, uid, gid)
return console.mount(config.Rootfs, config.MountLabel, 0, 0)
}
return nil
}
Expand Down
Loading