From 248ed23ac5686d698dd3e129bac8de7cb9ab0076 Mon Sep 17 00:00:00 2001 From: Dan Walsh Date: Tue, 2 Dec 2014 14:17:58 -0500 Subject: [PATCH] Allow users to share pid ns with host or other containers This patch allows us to turn off the PID namespace and to use another containers pid namespace Docker-DCO-1.1-Signed-off-by: Dan Walsh (github: rhatdan) --- config.go | 3 + integration/exec_test.go | 119 +++++++++++++++++++++++++++++++++++++++ namespaces/init.go | 4 ++ pidns/pidns.go | 29 ++++++++++ 4 files changed, 155 insertions(+) create mode 100644 pidns/pidns.go diff --git a/config.go b/config.go index 915e00660..0b6f7d0d2 100644 --- a/config.go +++ b/config.go @@ -50,6 +50,9 @@ type Config struct { // Ipc specifies the container's ipc setup to be created IpcNsPath string `json:"ipc,omitempty"` + // PidNS specifies the container's pid setup to be created + PidNsPath string `json:"pidns,omitempty"` + // Routes can be specified to create entries in the route table as the container is started Routes []*Route `json:"routes,omitempty"` diff --git a/integration/exec_test.go b/integration/exec_test.go index 8f4dae0f9..c800dfaf4 100644 --- a/integration/exec_test.go +++ b/integration/exec_test.go @@ -177,3 +177,122 @@ 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) + config.Namespaces["NEWPIDNS"] = true + 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 conatiner but equals host %q %q", actual, l) + } +} + +func TestPIDNSHost(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) + config.Namespaces["NEWPIDNS"] = false + 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 not equal to host link %q %q", actual, l) + } +} + +func TestPIDNSJoinPath(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) + config.Namespaces["NEWPIDNS"] = false + config.PidNsPath = "/proc/1/ns/pid" + + 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 not equal to host link %q %q", actual, l) + } +} + +func TestPIDNSBadPath(t *testing.T) { + if testing.Short() { + return + } + + rootfs, err := newRootFs() + if err != nil { + t.Fatal(err) + } + defer remove(rootfs) + + config := newTemplateConfig(rootfs) + config.Namespaces["NEWPIDNS"] = false + config.PidNsPath = "/proc/1/ns/pidc" + + _, _, err = runContainer(config, "", "true") + if err == nil { + t.Fatal("container succeded with bad pid path") + } +} diff --git a/namespaces/init.go b/namespaces/init.go index 7c83b1376..bd346fd1a 100644 --- a/namespaces/init.go +++ b/namespaces/init.go @@ -18,6 +18,7 @@ import ( "github.com/docker/libcontainer/mount" "github.com/docker/libcontainer/netlink" "github.com/docker/libcontainer/network" + "github.com/docker/libcontainer/pidns" "github.com/docker/libcontainer/security/capabilities" "github.com/docker/libcontainer/security/restrict" "github.com/docker/libcontainer/system" @@ -82,6 +83,9 @@ func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, pip if err := ipc.Initialize(container.IpcNsPath); err != nil { return fmt.Errorf("setup IPC %s", err) } + if err := pidns.Initialize(container.PidNsPath); err != nil { + return fmt.Errorf("setup PIDNS %s", err) + } if err := setupNetwork(container, networkState); err != nil { return fmt.Errorf("setup networking %s", err) } diff --git a/pidns/pidns.go b/pidns/pidns.go new file mode 100644 index 000000000..ce79a494b --- /dev/null +++ b/pidns/pidns.go @@ -0,0 +1,29 @@ +package pidns + +import ( + "fmt" + "os" + "syscall" + + "github.com/docker/libcontainer/system" +) + +// Join the PID Namespace of specified pid path if it exists. +// If the path does not exist then you are not joining a container. +func Initialize(nsPath string) error { + if nsPath == "" { + return nil + } + f, err := os.OpenFile(nsPath, os.O_RDONLY, 0) + if err != nil { + return fmt.Errorf("failed get PID namespace fd: %v", err) + } + + err = system.Setns(f.Fd(), syscall.CLONE_NEWPID) + f.Close() + + if err != nil { + return fmt.Errorf("failed to setns current PID namespace: %v", err) + } + return nil +}