From 115d8bac6017cc5703fe34e679f1a30a4759a9ff Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 12 Jan 2015 10:38:15 -0800 Subject: [PATCH] Update libcontainer to 6460fd79667466d2d9ec03f77f3 Signed-off-by: Michael Crosby --- project/vendor.sh | 2 +- .../libcontainer/cgroups/fs/apply_raw.go | 2 +- .../docker/libcontainer/cgroups/utils.go | 4 +- .../github.com/docker/libcontainer/config.go | 4 + .../libcontainer/integration/exec_test.go | 35 ++- .../docker/libcontainer/namespaces/exec.go | 35 +++ .../docker/libcontainer/namespaces/init.go | 10 +- .../libcontainer/namespaces/nsenter/nsenter.c | 73 ++++--- .../namespaces/nsenter/nsenter_test.go | 70 ++++++ .../libcontainer/sample_configs/host-pid.json | 200 ++++++++++++++++++ 10 files changed, 395 insertions(+), 40 deletions(-) create mode 100644 vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter_test.go create mode 100644 vendor/src/github.com/docker/libcontainer/sample_configs/host-pid.json diff --git a/project/vendor.sh b/project/vendor.sh index 94ad5e3371..e6a43e0f27 100755 --- a/project/vendor.sh +++ b/project/vendor.sh @@ -68,7 +68,7 @@ if [ "$1" = '--go' ]; then mv tmp-tar src/code.google.com/p/go/src/pkg/archive/tar fi -clone git github.com/docker/libcontainer be02944484da197166020d6b3f08a19d7d7d244c +clone git github.com/docker/libcontainer 6460fd79667466d2d9ec03f77f319a241c58d40b # 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')" 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 6f85793dd2..f05377f25b 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 @@ -105,7 +105,7 @@ func GetStats(systemPaths map[string]string) (*cgroups.Stats, error) { stats := cgroups.NewStats() for name, path := range systemPaths { sys, ok := subsystems[name] - if !ok { + if !ok || !cgroups.PathExists(path) { continue } if err := sys.GetStats(path, stats); err != nil { diff --git a/vendor/src/github.com/docker/libcontainer/cgroups/utils.go b/vendor/src/github.com/docker/libcontainer/cgroups/utils.go index 5753ca453a..a360904cce 100644 --- a/vendor/src/github.com/docker/libcontainer/cgroups/utils.go +++ b/vendor/src/github.com/docker/libcontainer/cgroups/utils.go @@ -174,7 +174,7 @@ func ParseCgroupFile(subsystem string, r io.Reader) (string, error) { return "", NewNotFoundError(subsystem) } -func pathExists(path string) bool { +func PathExists(path string) bool { if _, err := os.Stat(path); err != nil { return false } @@ -183,7 +183,7 @@ func pathExists(path string) bool { func EnterPid(cgroupPaths map[string]string, pid int) error { for _, path := range cgroupPaths { - if pathExists(path) { + if PathExists(path) { if err := ioutil.WriteFile(filepath.Join(path, "cgroup.procs"), []byte(strconv.Itoa(pid)), 0700); err != nil { return err diff --git a/vendor/src/github.com/docker/libcontainer/config.go b/vendor/src/github.com/docker/libcontainer/config.go index 7ab9a9a76a..643601adac 100644 --- a/vendor/src/github.com/docker/libcontainer/config.go +++ b/vendor/src/github.com/docker/libcontainer/config.go @@ -120,6 +120,10 @@ type Config struct { // Rlimits specifies the resource limits, such as max open files, to set in the container // If Rlimits are not set, the container will inherit rlimits from the parent process Rlimits []Rlimit `json:"rlimits,omitempty"` + + // AdditionalGroups specifies the gids that should be added to supplementary groups + // in addition to those that the user belongs to. + AdditionalGroups []int `json:"additional_groups,omitempty"` } // Routes can be specified to create entries in the route table as the container is started 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 f0728c5817..fb1d1d7a10 100644 --- a/vendor/src/github.com/docker/libcontainer/integration/exec_test.go +++ b/vendor/src/github.com/docker/libcontainer/integration/exec_test.go @@ -67,7 +67,7 @@ func TestIPCPrivate(t *testing.T) { } if actual := strings.Trim(buffers.Stdout.String(), "\n"); actual == l { - t.Fatalf("ipc link should be private to the conatiner but equals host %q %q", actual, l) + t.Fatalf("ipc link should be private to the container but equals host %q %q", actual, l) } } @@ -152,7 +152,7 @@ func TestIPCBadPath(t *testing.T) { _, _, err = runContainer(config, "", "true") if err == nil { - t.Fatal("container succeded with bad ipc path") + t.Fatal("container succeeded with bad ipc path") } } @@ -176,3 +176,34 @@ func TestRlimit(t *testing.T) { t.Fatalf("expected rlimit to be 1024, got %s", limit) } } + +func TestPIDNSPrivate(t *testing.T) { + if testing.Short() { + return + } + + rootfs, err := newRootFs() + if err != nil { + t.Fatal(err) + } + defer remove(rootfs) + + l, err := os.Readlink("/proc/1/ns/pid") + if err != nil { + t.Fatal(err) + } + + config := newTemplateConfig(rootfs) + buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/pid") + if err != nil { + t.Fatal(err) + } + + if exitCode != 0 { + t.Fatalf("exit code not 0. code %d stderr %q", exitCode, buffers.Stderr) + } + + if actual := strings.Trim(buffers.Stdout.String(), "\n"); actual == l { + t.Fatalf("pid link should be private to the container but equals host %q %q", actual, l) + } +} diff --git a/vendor/src/github.com/docker/libcontainer/namespaces/exec.go b/vendor/src/github.com/docker/libcontainer/namespaces/exec.go index b7873edd0e..abe67ae257 100644 --- a/vendor/src/github.com/docker/libcontainer/namespaces/exec.go +++ b/vendor/src/github.com/docker/libcontainer/namespaces/exec.go @@ -110,9 +110,44 @@ func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Wri return -1, err } } + if !container.Namespaces.Contains(libcontainer.NEWPID) { + killAllPids(container) + } return command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil } +// killAllPids itterates over all of the container's processes +// sending a SIGKILL to each process. +func killAllPids(container *libcontainer.Config) error { + var ( + procs []*os.Process + freeze = fs.Freeze + getPids = fs.GetPids + ) + if systemd.UseSystemd() { + freeze = systemd.Freeze + getPids = systemd.GetPids + } + freeze(container.Cgroups, cgroups.Frozen) + pids, err := getPids(container.Cgroups) + if err != nil { + return err + } + for _, pid := range pids { + // TODO: log err without aborting if we are unable to find + // a single PID + if p, err := os.FindProcess(pid); err == nil { + procs = append(procs, p) + p.Kill() + } + } + freeze(container.Cgroups, cgroups.Thawed) + for _, p := range procs { + p.Wait() + } + return err +} + // DefaultCreateCommand will return an exec.Cmd with the Cloneflags set to the proper namespaces // defined on the container's configuration and use the current binary as the init with the // args provided diff --git a/vendor/src/github.com/docker/libcontainer/namespaces/init.go b/vendor/src/github.com/docker/libcontainer/namespaces/init.go index a4400bddbc..0a4ff19620 100644 --- a/vendor/src/github.com/docker/libcontainer/namespaces/init.go +++ b/vendor/src/github.com/docker/libcontainer/namespaces/init.go @@ -170,7 +170,7 @@ func RestoreParentDeathSignal(old int) error { } // SetupUser changes the groups, gid, and uid for the user inside the container -func SetupUser(u string) error { +func SetupUser(container *libcontainer.Config) error { // Set up defaults. defaultExecUser := user.ExecUser{ Uid: syscall.Getuid(), @@ -188,12 +188,14 @@ func SetupUser(u string) error { return err } - execUser, err := user.GetExecUserPath(u, &defaultExecUser, passwdPath, groupPath) + execUser, err := user.GetExecUserPath(container.User, &defaultExecUser, passwdPath, groupPath) if err != nil { return fmt.Errorf("get supplementary groups %s", err) } - if err := syscall.Setgroups(execUser.Sgids); err != nil { + suppGroups := append(execUser.Sgids, container.AdditionalGroups...) + + if err := syscall.Setgroups(suppGroups); err != nil { return fmt.Errorf("setgroups %s", err) } @@ -273,7 +275,7 @@ func FinalizeNamespace(container *libcontainer.Config) error { return fmt.Errorf("set keep caps %s", err) } - if err := SetupUser(container.User); err != nil { + if err := SetupUser(container); err != nil { return fmt.Errorf("setup user %s", err) } diff --git a/vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter.c b/vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter.c index b735b1fa20..9782702dcd 100644 --- a/vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter.c +++ b/vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter.c @@ -15,6 +15,8 @@ #include #include +#define pr_perror(fmt, ...) fprintf(stderr, "nsenter: " fmt ": %m\n", ##__VA_ARGS__) + static const kBufSize = 256; static const char *kNsEnter = "nsenter"; @@ -22,6 +24,10 @@ void get_args(int *argc, char ***argv) { // Read argv int fd = open("/proc/self/cmdline", O_RDONLY); + if (fd < 0) { + pr_perror("Unable to open /proc/self/cmdline"); + exit(1); + } // Read the whole commandline. ssize_t contents_size = 0; @@ -34,6 +40,10 @@ void get_args(int *argc, char ***argv) bytes_read = read(fd, contents + contents_offset, contents_size - contents_offset); + if (bytes_read < 0) { + pr_perror("Unable to read from /proc/self/cmdline"); + exit(1); + } contents_offset += bytes_read; } while (bytes_read > 0); @@ -91,8 +101,7 @@ void nsenter() #ifdef PR_SET_CHILD_SUBREAPER if (prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0) == -1) { - fprintf(stderr, "nsenter: failed to set child subreaper: %s", - strerror(errno)); + pr_perror("Failed to set child subreaper"); exit(1); } #endif @@ -124,9 +133,8 @@ void nsenter() init_pid = strtol(init_pid_str, NULL, 10); if ((init_pid == 0 && errno == EINVAL) || errno == ERANGE) { - fprintf(stderr, - "nsenter: Failed to parse PID from \"%s\" with output \"%d\" and error: \"%s\"\n", - init_pid_str, init_pid, strerror(errno)); + pr_perror("Failed to parse PID from \"%s\" with output \"%d\"", + init_pid_str, init_pid); print_usage(); exit(1); } @@ -135,7 +143,7 @@ void nsenter() argv += 3; if (setsid() == -1) { - fprintf(stderr, "setsid failed. Error: %s\n", strerror(errno)); + pr_perror("setsid failed"); exit(1); } // before we setns we need to dup the console @@ -143,9 +151,7 @@ void nsenter() if (console != NULL) { consolefd = open(console, O_RDWR); if (consolefd < 0) { - fprintf(stderr, - "nsenter: failed to open console %s %s\n", - console, strerror(errno)); + pr_perror("Failed to open console %s", console); exit(1); } } @@ -154,51 +160,60 @@ void nsenter() memset(ns_dir, 0, PATH_MAX); snprintf(ns_dir, PATH_MAX - 1, "/proc/%d/ns/", init_pid); + int ns_dir_fd; + ns_dir_fd = open(ns_dir, O_RDONLY | O_DIRECTORY); + if (ns_dir_fd < 0) { + pr_perror("Unable to open %s", ns_dir); + exit(1); + } + char *namespaces[] = { "ipc", "uts", "net", "pid", "mnt" }; const int num = sizeof(namespaces) / sizeof(char *); int i; for (i = 0; i < num; i++) { - char buf[PATH_MAX]; - memset(buf, 0, PATH_MAX); - snprintf(buf, PATH_MAX - 1, "%s%s", ns_dir, namespaces[i]); - int fd = open(buf, O_RDONLY); - if (fd == -1) { - // Ignore nonexistent namespaces. + // A zombie process has links on namespaces, but they can't be opened + struct stat st; + if (fstatat(ns_dir_fd, namespaces[i], &st, AT_SYMLINK_NOFOLLOW) == -1) { if (errno == ENOENT) continue; + pr_perror("Failed to stat ns file %s for ns %s", + ns_dir, namespaces[i]); + exit(1); + } - fprintf(stderr, - "nsenter: Failed to open ns file \"%s\" for ns \"%s\" with error: \"%s\"\n", - buf, namespaces[i], strerror(errno)); + int fd = openat(ns_dir_fd, namespaces[i], O_RDONLY); + if (fd == -1) { + pr_perror("Failed to open ns file %s for ns %s", + ns_dir, namespaces[i]); exit(1); } // Set the namespace. if (setns(fd, 0) == -1) { - fprintf(stderr, - "nsenter: Failed to setns for \"%s\" with error: \"%s\"\n", - namespaces[i], strerror(errno)); + pr_perror("Failed to setns for %s", namespaces[i]); exit(1); } close(fd); } + close(ns_dir_fd); // We must fork to actually enter the PID namespace. int child = fork(); + if (child == -1) { + pr_perror("Unable to fork a process"); + exit(1); + } if (child == 0) { if (consolefd != -1) { if (dup2(consolefd, STDIN_FILENO) != 0) { - fprintf(stderr, "nsenter: failed to dup 0 %s\n", - strerror(errno)); + pr_perror("Failed to dup 0"); exit(1); } if (dup2(consolefd, STDOUT_FILENO) != STDOUT_FILENO) { - fprintf(stderr, "nsenter: failed to dup 1 %s\n", - strerror(errno)); + pr_perror("Failed to dup 1"); exit(1); } if (dup2(consolefd, STDERR_FILENO) != STDERR_FILENO) { - fprintf(stderr, "nsenter: failed to dup 2 %s\n", - strerror(errno)); + pr_perror("Failed to dup 2\n"); exit(1); } } @@ -208,9 +223,7 @@ void nsenter() // Parent, wait for the child. int status = 0; if (waitpid(child, &status, 0) == -1) { - fprintf(stderr, - "nsenter: Failed to waitpid with error: \"%s\"\n", - strerror(errno)); + pr_perror("nsenter: Failed to waitpid with error"); exit(1); } // Forward the child's exit code or re-send its death signal. diff --git a/vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter_test.go b/vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter_test.go new file mode 100644 index 0000000000..85ee5d6725 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/namespaces/nsenter/nsenter_test.go @@ -0,0 +1,70 @@ +package nsenter + +import ( + "fmt" + "os" + "os/exec" + "os/signal" + "strings" + "syscall" + "testing" +) + +func TestNsenterAlivePid(t *testing.T) { + args := []string{"nsenter-exec", "--nspid", fmt.Sprintf("%d", os.Getpid())} + + cmd := &exec.Cmd{ + Path: os.Args[0], + Args: args, + } + + err := cmd.Run() + if err != nil { + t.Fatal("nsenter exits with a non-zero exit status") + } +} + +func TestNsenterInvalidPid(t *testing.T) { + args := []string{"nsenter-exec", "--nspid", "-1"} + + cmd := &exec.Cmd{ + Path: os.Args[0], + Args: args, + } + + err := cmd.Run() + if err == nil { + t.Fatal("nsenter exits with a zero exit status") + } +} + +func TestNsenterDeadPid(t *testing.T) { + + c := make(chan os.Signal) + signal.Notify(c, syscall.SIGCHLD) + dead_cmd := exec.Command("true") + if err := dead_cmd.Start(); err != nil { + t.Fatal(err) + } + defer dead_cmd.Wait() + <-c // dead_cmd is zombie + + args := []string{"nsenter-exec", "--nspid", fmt.Sprintf("%d", dead_cmd.Process.Pid)} + + cmd := &exec.Cmd{ + Path: os.Args[0], + Args: args, + } + + err := cmd.Run() + if err == nil { + t.Fatal("nsenter exits with a zero exit status") + } +} + +func init() { + if strings.HasPrefix(os.Args[0], "nsenter-") { + os.Exit(0) + } + return +} diff --git a/vendor/src/github.com/docker/libcontainer/sample_configs/host-pid.json b/vendor/src/github.com/docker/libcontainer/sample_configs/host-pid.json new file mode 100644 index 0000000000..f47af930e6 --- /dev/null +++ b/vendor/src/github.com/docker/libcontainer/sample_configs/host-pid.json @@ -0,0 +1,200 @@ +{ + "capabilities": [ + "CHOWN", + "DAC_OVERRIDE", + "FOWNER", + "MKNOD", + "NET_RAW", + "SETGID", + "SETUID", + "SETFCAP", + "SETPCAP", + "NET_BIND_SERVICE", + "SYS_CHROOT", + "KILL" + ], + "cgroups": { + "allowed_devices": [ + { + "cgroup_permissions": "m", + "major_number": -1, + "minor_number": -1, + "type": 99 + }, + { + "cgroup_permissions": "m", + "major_number": -1, + "minor_number": -1, + "type": 98 + }, + { + "cgroup_permissions": "rwm", + "major_number": 5, + "minor_number": 1, + "path": "/dev/console", + "type": 99 + }, + { + "cgroup_permissions": "rwm", + "major_number": 4, + "path": "/dev/tty0", + "type": 99 + }, + { + "cgroup_permissions": "rwm", + "major_number": 4, + "minor_number": 1, + "path": "/dev/tty1", + "type": 99 + }, + { + "cgroup_permissions": "rwm", + "major_number": 136, + "minor_number": -1, + "type": 99 + }, + { + "cgroup_permissions": "rwm", + "major_number": 5, + "minor_number": 2, + "type": 99 + }, + { + "cgroup_permissions": "rwm", + "major_number": 10, + "minor_number": 200, + "type": 99 + }, + { + "cgroup_permissions": "rwm", + "file_mode": 438, + "major_number": 1, + "minor_number": 3, + "path": "/dev/null", + "type": 99 + }, + { + "cgroup_permissions": "rwm", + "file_mode": 438, + "major_number": 1, + "minor_number": 5, + "path": "/dev/zero", + "type": 99 + }, + { + "cgroup_permissions": "rwm", + "file_mode": 438, + "major_number": 1, + "minor_number": 7, + "path": "/dev/full", + "type": 99 + }, + { + "cgroup_permissions": "rwm", + "file_mode": 438, + "major_number": 5, + "path": "/dev/tty", + "type": 99 + }, + { + "cgroup_permissions": "rwm", + "file_mode": 438, + "major_number": 1, + "minor_number": 9, + "path": "/dev/urandom", + "type": 99 + }, + { + "cgroup_permissions": "rwm", + "file_mode": 438, + "major_number": 1, + "minor_number": 8, + "path": "/dev/random", + "type": 99 + } + ], + "name": "docker-koye", + "parent": "docker" + }, + "restrict_sys": true, + "mount_config": { + "device_nodes": [ + { + "cgroup_permissions": "rwm", + "file_mode": 438, + "major_number": 1, + "minor_number": 3, + "path": "/dev/null", + "type": 99 + }, + { + "cgroup_permissions": "rwm", + "file_mode": 438, + "major_number": 1, + "minor_number": 5, + "path": "/dev/zero", + "type": 99 + }, + { + "cgroup_permissions": "rwm", + "file_mode": 438, + "major_number": 1, + "minor_number": 7, + "path": "/dev/full", + "type": 99 + }, + { + "cgroup_permissions": "rwm", + "file_mode": 438, + "major_number": 5, + "path": "/dev/tty", + "type": 99 + }, + { + "cgroup_permissions": "rwm", + "file_mode": 438, + "major_number": 1, + "minor_number": 9, + "path": "/dev/urandom", + "type": 99 + }, + { + "cgroup_permissions": "rwm", + "file_mode": 438, + "major_number": 1, + "minor_number": 8, + "path": "/dev/random", + "type": 99 + } + ], + "mounts": [ + { + "type": "tmpfs", + "destination": "/tmp" + } + ] + }, + "environment": [ + "HOME=/", + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "HOSTNAME=koye", + "TERM=xterm" + ], + "hostname": "koye", + "namespaces": [ + {"type": "NEWIPC"}, + {"type": "NEWNET"}, + {"type": "NEWNS"}, + {"type": "NEWUTS"} + ], + "networks": [ + { + "address": "127.0.0.1/0", + "gateway": "localhost", + "mtu": 1500, + "type": "loopback" + } + ], + "tty": true, + "user": "daemon" +}