diff --git a/hack/vendor.sh b/hack/vendor.sh index f5df649cde..0246f03cc4 100755 --- a/hack/vendor.sh +++ b/hack/vendor.sh @@ -75,7 +75,7 @@ rm -rf src/github.com/docker/distribution mkdir -p src/github.com/docker/distribution mv tmp-digest src/github.com/docker/distribution/digest -clone git github.com/docker/libcontainer a6044b701c166fe538fc760f9e2dcea3d737cd2a +clone git github.com/docker/libcontainer c8512754166539461fd860451ff1a0af7491c197 # see src/github.com/docker/libcontainer/update-vendor.sh which is the "source of truth" for libcontainer deps (just like this file) rm -rf src/github.com/docker/libcontainer/vendor eval "$(grep '^clone ' src/github.com/docker/libcontainer/update-vendor.sh | grep -v 'github.com/codegangsta/cli' | grep -v 'github.com/Sirupsen/logrus')" diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/fs/apply_raw.go b/vendor/src/github.com/docker/libcontainer/cgroups/fs/apply_raw.go index c771245da5..0a2d76bcd4 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/fs/apply_raw.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/fs/apply_raw.go @@ -220,16 +220,16 @@ func getCgroupData(c *configs.Cgroup, pid int) (*data, error) { }, nil } -func (raw *data) parent(subsystem string) (string, error) { +func (raw *data) parent(subsystem, mountpoint string) (string, error) { initPath, err := cgroups.GetInitCgroupDir(subsystem) if err != nil { return "", err } - return filepath.Join(raw.root, subsystem, initPath), nil + return filepath.Join(mountpoint, initPath), nil } func (raw *data) path(subsystem string) (string, error) { - _, err := cgroups.FindCgroupMountpoint(subsystem) + mnt, err := cgroups.FindCgroupMountpoint(subsystem) // If we didn't mount the subsystem, there is no point we make the path. if err != nil { return "", err @@ -240,7 +240,7 @@ func (raw *data) path(subsystem string) (string, error) { return filepath.Join(raw.root, subsystem, raw.cgroup), nil } - parent, err := raw.parent(subsystem) + parent, err := raw.parent(subsystem, mnt) if err != nil { return "", err } diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_systemd.go b/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_systemd.go index f353640697..85ee5db06d 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_systemd.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/systemd/apply_systemd.go @@ -43,6 +43,10 @@ var subsystems = map[string]subsystem{ "freezer": &fs.FreezerGroup{}, } +const ( + testScopeWait = 4 +) + var ( connLock sync.Mutex theConn *systemd.Conn @@ -86,16 +90,41 @@ func UseSystemd() bool { } } + // Ensure the scope name we use doesn't exist. Use the Pid to + // avoid collisions between multiple libcontainer users on a + // single host. + scope := fmt.Sprintf("libcontainer-%d-systemd-test-default-dependencies.scope", os.Getpid()) + testScopeExists := true + for i := 0; i <= testScopeWait; i++ { + if _, err := theConn.StopUnit(scope, "replace"); err != nil { + if dbusError, ok := err.(dbus.Error); ok { + if strings.Contains(dbusError.Name, "org.freedesktop.systemd1.NoSuchUnit") { + testScopeExists = false + break + } + } + } + time.Sleep(time.Millisecond) + } + + // Bail out if we can't kill this scope without testing for DefaultDependencies + if testScopeExists { + return hasStartTransientUnit + } + // Assume StartTransientUnit on a scope allows DefaultDependencies hasTransientDefaultDependencies = true ddf := newProp("DefaultDependencies", false) - if _, err := theConn.StartTransientUnit("docker-systemd-test-default-dependencies.scope", "replace", ddf); err != nil { + if _, err := theConn.StartTransientUnit(scope, "replace", ddf); err != nil { if dbusError, ok := err.(dbus.Error); ok { if strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.PropertyReadOnly") { hasTransientDefaultDependencies = false } } } + + // Not critical because of the stop unit logic above. + theConn.StopUnit(scope, "replace") } return hasStartTransientUnit } diff --git a/vendor/src/github.com/docker/libcontainer/container_linux.go b/vendor/src/github.com/docker/libcontainer/container_linux.go index c44c8daccc..54d40617e7 100644 --- a/vendor/src/github.com/docker/libcontainer/container_linux.go +++ b/vendor/src/github.com/docker/libcontainer/container_linux.go @@ -193,12 +193,13 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe, func (c *linuxContainer) newInitConfig(process *Process) *initConfig { return &initConfig{ - Config: c.config, - Args: process.Args, - Env: process.Env, - User: process.User, - Cwd: process.Cwd, - Console: process.consolePath, + Config: c.config, + Args: process.Args, + Env: process.Env, + User: process.User, + Cwd: process.Cwd, + Console: process.consolePath, + Capabilities: process.Capabilities, } } diff --git a/vendor/src/github.com/docker/libcontainer/init_linux.go b/vendor/src/github.com/docker/libcontainer/init_linux.go index aa95423e57..0468b2e936 100644 --- a/vendor/src/github.com/docker/libcontainer/init_linux.go +++ b/vendor/src/github.com/docker/libcontainer/init_linux.go @@ -40,13 +40,14 @@ type network struct { // initConfig is used for transferring parameters from Exec() to Init() type initConfig struct { - Args []string `json:"args"` - Env []string `json:"env"` - Cwd string `json:"cwd"` - User string `json:"user"` - Config *configs.Config `json:"config"` - Console string `json:"console"` - Networks []*network `json:"network"` + Args []string `json:"args"` + Env []string `json:"env"` + Cwd string `json:"cwd"` + Capabilities []string `json:"capabilities"` + User string `json:"user"` + Config *configs.Config `json:"config"` + Console string `json:"console"` + Networks []*network `json:"network"` } type initer interface { @@ -99,7 +100,12 @@ func finalizeNamespace(config *initConfig) error { if err := utils.CloseExecFrom(3); err != nil { return err } - w, err := newCapWhitelist(config.Config.Capabilities) + + capabilities := config.Config.Capabilities + if config.Capabilities != nil { + capabilities = config.Capabilities + } + w, err := newCapWhitelist(capabilities) if err != nil { return err } diff --git a/vendor/src/github.com/docker/libcontainer/integration/exec_test.go b/vendor/src/github.com/docker/libcontainer/integration/exec_test.go index b68cb739c3..4afff77ded 100644 --- a/vendor/src/github.com/docker/libcontainer/integration/exec_test.go +++ b/vendor/src/github.com/docker/libcontainer/integration/exec_test.go @@ -4,6 +4,7 @@ import ( "bytes" "io/ioutil" "os" + "strconv" "strings" "testing" @@ -395,6 +396,90 @@ func TestProcessEnv(t *testing.T) { } } +func TestProcessCaps(t *testing.T) { + if testing.Short() { + return + } + root, err := newTestRoot() + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(root) + + rootfs, err := newRootfs() + if err != nil { + t.Fatal(err) + } + defer remove(rootfs) + + config := newTemplateConfig(rootfs) + + factory, err := libcontainer.New(root, libcontainer.Cgroupfs) + if err != nil { + t.Fatal(err) + } + + container, err := factory.Create("test", config) + if err != nil { + t.Fatal(err) + } + defer container.Destroy() + + processCaps := append(config.Capabilities, "NET_ADMIN") + + var stdout bytes.Buffer + pconfig := libcontainer.Process{ + Args: []string{"sh", "-c", "cat /proc/self/status"}, + Env: standardEnvironment, + Capabilities: processCaps, + Stdin: nil, + Stdout: &stdout, + } + err = container.Start(&pconfig) + if err != nil { + t.Fatal(err) + } + + // Wait for process + waitProcess(&pconfig, t) + + outputStatus := string(stdout.Bytes()) + if err != nil { + t.Fatal(err) + } + + lines := strings.Split(outputStatus, "\n") + + effectiveCapsLine := "" + for _, l := range lines { + line := strings.TrimSpace(l) + if strings.Contains(line, "CapEff:") { + effectiveCapsLine = line + break + } + } + + if effectiveCapsLine == "" { + t.Fatal("Couldn't find effective caps: ", outputStatus) + } + + parts := strings.Split(effectiveCapsLine, ":") + effectiveCapsStr := strings.TrimSpace(parts[1]) + + effectiveCaps, err := strconv.ParseUint(effectiveCapsStr, 16, 64) + if err != nil { + t.Fatal("Could not parse effective caps", err) + } + + var netAdminMask uint64 + var netAdminBit uint + netAdminBit = 12 // from capability.h + netAdminMask = 1 << netAdminBit + if effectiveCaps&netAdminMask != netAdminMask { + t.Fatal("CAP_NET_ADMIN is not set as expected") + } +} + func TestFreeze(t *testing.T) { if testing.Short() { return diff --git a/vendor/src/github.com/docker/libcontainer/nsinit/README.md b/vendor/src/github.com/docker/libcontainer/nsinit/README.md new file mode 100644 index 0000000000..f321e22719 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/nsinit/README.md @@ -0,0 +1,57 @@ +## nsinit + +`nsinit` is a cli application which demonstrates the use of libcontainer. +It is able to spawn new containers or join existing containers. + +### How to build? + +First to add the `libcontainer/vendor` into your GOPATH. It's because something related with this [issue](https://github.com/docker/libcontainer/issues/210). + +``` +export GOPATH=$GOPATH:/your/path/to/libcontainer/vendor +``` + +Then get into the nsinit folder and get the imported file. Use `make` command to make the nsinit binary. + +``` +cd libcontainer/nsinit +go get +make +``` + +We have finished compiling the nsinit package, but a root filesystem must be provided for use along with a container configuration file. + +Choose a proper place to run your container. For example we use `/busybox`. + +``` +mkdir /busybox +curl -sSL 'https://github.com/jpetazzo/docker-busybox/raw/buildroot-2014.11/rootfs.tar' | tar -xC /busybox +``` + +Then you may need to write a configure file named `container.json` in the `/busybox` folder. +Environment, networking, and different capabilities for the container are specified in this file. +The configuration is used for each process executed inside the container +See the `sample_configs` folder for examples of what the container configuration should look like. + +``` +cp libcontainer/sample_configs/minimal.json /busybox/container.json +cd /busybox +``` + +Now the nsinit is ready to work. +To execute `/bin/bash` in the current directory as a container just run the following **as root**: +```bash +nsinit exec --tty /bin/bash +``` + +If you wish to spawn another process inside the container while your +current bash session is running, run the same command again to +get another bash shell (or change the command). If the original +process (PID 1) dies, all other processes spawned inside the container +will be killed and the namespace will be removed. + +You can identify if a process is running in a container by +looking to see if `state.json` is in the root of the directory. + +You may also specify an alternate root place where +the `container.json` file is read and where the `state.json` file will be saved. diff --git a/vendor/src/github.com/docker/libcontainer/process.go b/vendor/src/github.com/docker/libcontainer/process.go index 12f90daf79..82fcff8c4c 100644 --- a/vendor/src/github.com/docker/libcontainer/process.go +++ b/vendor/src/github.com/docker/libcontainer/process.go @@ -41,6 +41,10 @@ type Process struct { // consolePath is the path to the console allocated to the container. consolePath string + // Capabilities specify the capabilities to keep when executing the process inside the container + // All capbilities not specified will be dropped from the processes capability mask + Capabilities []string + ops processOperations } diff --git a/vendor/src/github.com/docker/libcontainer/process_linux.go b/vendor/src/github.com/docker/libcontainer/process_linux.go index 5aab5a7f55..1c74b65490 100644 --- a/vendor/src/github.com/docker/libcontainer/process_linux.go +++ b/vendor/src/github.com/docker/libcontainer/process_linux.go @@ -4,6 +4,7 @@ package libcontainer import ( "encoding/json" + "errors" "io" "os" "os/exec" @@ -44,8 +45,12 @@ func (p *setnsProcess) startTime() (string, error) { return system.GetProcessStartTime(p.pid()) } -func (p *setnsProcess) signal(s os.Signal) error { - return p.cmd.Process.Signal(s) +func (p *setnsProcess) signal(sig os.Signal) error { + s, ok := sig.(syscall.Signal) + if !ok { + return errors.New("os: unsupported signal type") + } + return syscall.Kill(p.cmd.Process.Pid, s) } func (p *setnsProcess) start() (err error) { @@ -235,6 +240,10 @@ func (p *initProcess) createNetworkInterfaces() error { return nil } -func (p *initProcess) signal(s os.Signal) error { - return p.cmd.Process.Signal(s) +func (p *initProcess) signal(sig os.Signal) error { + s, ok := sig.(syscall.Signal) + if !ok { + return errors.New("os: unsupported signal type") + } + return syscall.Kill(p.cmd.Process.Pid, s) }