diff --git a/Makefile b/Makefile index 0ec995fc..0c4dda7c 100644 --- a/Makefile +++ b/Makefile @@ -12,10 +12,10 @@ sh: GO_PACKAGES = $(shell find . -not \( -wholename ./vendor -prune -o -wholename ./.git -prune \) -name '*.go' -print0 | xargs -0n1 dirname | sort -u) direct-test: - go test -cover -v $(GO_PACKAGES) + go test $(TEST_TAGS) -cover -v $(GO_PACKAGES) direct-test-short: - go test -cover -test.short -v $(GO_PACKAGES) + go test $(TEST_TAGS) -cover -test.short -v $(GO_PACKAGES) direct-build: go build -v $(GO_PACKAGES) diff --git a/config.go b/config.go index 1fb377dc..57ea5c69 100644 --- a/config.go +++ b/config.go @@ -47,6 +47,9 @@ type Config struct { // Networks specifies the container's network setup to be created Networks []*Network `json:"networks,omitempty"` + // Ipc specifies the container's ipc setup to be created + IpcNsPath string `json:"ipc,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 96099189..261d208e 100644 --- a/integration/exec_test.go +++ b/integration/exec_test.go @@ -1,6 +1,7 @@ package integration import ( + "os" "strings" "testing" ) @@ -36,3 +37,122 @@ func TestExecPS(t *testing.T) { t.Fatalf("expected output %q but received %q", expected, actual) } } + +func TestIPCPrivate(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/ipc") + if err != nil { + t.Fatal(err) + } + + config := newTemplateConfig(rootfs) + config.Namespaces["NEWIPC"] = true + buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc") + 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("ipc link should be private to the conatiner but equals host %q %q", actual, l) + } +} + +func TestIPCHost(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/ipc") + if err != nil { + t.Fatal(err) + } + + config := newTemplateConfig(rootfs) + config.Namespaces["NEWIPC"] = false + buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc") + 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("ipc link not equal to host link %q %q", actual, l) + } +} + +func TestIPCJoinPath(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/ipc") + if err != nil { + t.Fatal(err) + } + + config := newTemplateConfig(rootfs) + config.Namespaces["NEWIPC"] = false + config.IpcNsPath = "/proc/1/ns/ipc" + + buffers, exitCode, err := runContainer(config, "", "readlink", "/proc/self/ns/ipc") + 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("ipc link not equal to host link %q %q", actual, l) + } +} + +func TestIPCBadPath(t *testing.T) { + if testing.Short() { + return + } + + rootfs, err := newRootFs() + if err != nil { + t.Fatal(err) + } + defer remove(rootfs) + + config := newTemplateConfig(rootfs) + config.Namespaces["NEWIPC"] = false + config.IpcNsPath = "/proc/1/ns/ipcc" + + _, _, err = runContainer(config, "", "true") + if err == nil { + t.Fatal("container succeded with bad ipc path") + } +} diff --git a/ipc/ipc.go b/ipc/ipc.go new file mode 100644 index 00000000..147cf557 --- /dev/null +++ b/ipc/ipc.go @@ -0,0 +1,29 @@ +package ipc + +import ( + "fmt" + "os" + "syscall" + + "github.com/docker/libcontainer/system" +) + +// Join the IPC Namespace of specified ipc 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 IPC namespace fd: %v", err) + } + + err = system.Setns(f.Fd(), syscall.CLONE_NEWIPC) + f.Close() + + if err != nil { + return fmt.Errorf("failed to setns current IPC namespace: %v", err) + } + return nil +} diff --git a/namespaces/init.go b/namespaces/init.go index 4c2b3327..879ac21e 100644 --- a/namespaces/init.go +++ b/namespaces/init.go @@ -11,6 +11,7 @@ import ( "github.com/docker/libcontainer" "github.com/docker/libcontainer/apparmor" "github.com/docker/libcontainer/console" + "github.com/docker/libcontainer/ipc" "github.com/docker/libcontainer/label" "github.com/docker/libcontainer/mount" "github.com/docker/libcontainer/netlink" @@ -66,6 +67,9 @@ func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, syn return fmt.Errorf("setctty %s", err) } } + if err := ipc.Initialize(container.IpcNsPath); err != nil { + return fmt.Errorf("setup IPC %s", err) + } if err := setupNetwork(container, networkState); err != nil { return fmt.Errorf("setup networking %s", err) }