diff options
Diffstat (limited to 'libgo/go/syscall/exec_linux_test.go')
-rw-r--r-- | libgo/go/syscall/exec_linux_test.go | 133 |
1 files changed, 132 insertions, 1 deletions
diff --git a/libgo/go/syscall/exec_linux_test.go b/libgo/go/syscall/exec_linux_test.go index 6d31941184..7a4b571760 100644 --- a/libgo/go/syscall/exec_linux_test.go +++ b/libgo/go/syscall/exec_linux_test.go @@ -26,11 +26,14 @@ func isChrooted(t *testing.T) bool { return root.Sys().(*syscall.Stat_t).Ino != 2 } -func whoamiCmd(t *testing.T, uid, gid int, setgroups bool) *exec.Cmd { +func checkUserNS(t *testing.T) { if _, err := os.Stat("/proc/self/ns/user"); err != nil { if os.IsNotExist(err) { t.Skip("kernel doesn't support user namespaces") } + if os.IsPermission(err) { + t.Skip("unable to test user namespaces due to permissions") + } t.Fatalf("Failed to stat /proc/self/ns/user: %v", err) } if isChrooted(t) { @@ -53,6 +56,10 @@ func whoamiCmd(t *testing.T, uid, gid int, setgroups bool) *exec.Cmd { if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" { t.Skip("skipping test on Kubernetes-based builders; see Issue 12815") } +} + +func whoamiCmd(t *testing.T, uid, gid int, setgroups bool) *exec.Cmd { + checkUserNS(t) cmd := exec.Command("whoami") cmd.SysProcAttr = &syscall.SysProcAttr{ Cloneflags: syscall.CLONE_NEWUSER, @@ -122,3 +129,127 @@ func TestEmptyCredGroupsDisableSetgroups(t *testing.T) { t.Fatal(err) } } + +func TestUnshare(t *testing.T) { + // Make sure we are running as root so we have permissions to use unshare + // and create a network namespace. + if os.Getuid() != 0 { + t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace") + } + + // When running under the Go continuous build, skip tests for + // now when under Kubernetes. (where things are root but not quite) + // Both of these are our own environment variables. + // See Issue 12815. + if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" { + t.Skip("skipping test on Kubernetes-based builders; see Issue 12815") + } + + path := "/proc/net/dev" + if _, err := os.Stat(path); err != nil { + if os.IsNotExist(err) { + t.Skip("kernel doesn't support proc filesystem") + } + if os.IsPermission(err) { + t.Skip("unable to test proc filesystem due to permissions") + } + t.Fatal(err) + } + if _, err := os.Stat("/proc/self/ns/net"); err != nil { + if os.IsNotExist(err) { + t.Skip("kernel doesn't support net namespace") + } + t.Fatal(err) + } + + orig, err := ioutil.ReadFile(path) + if err != nil { + t.Fatal(err) + } + origLines := strings.Split(strings.TrimSpace(string(orig)), "\n") + + cmd := exec.Command("cat", path) + cmd.SysProcAttr = &syscall.SysProcAttr{ + Unshareflags: syscall.CLONE_NEWNET, + } + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("Cmd failed with err %v, output: %s", err, out) + } + + // Check there is only the local network interface + sout := strings.TrimSpace(string(out)) + if !strings.Contains(sout, "lo:") { + t.Fatalf("Expected lo network interface to exist, got %s", sout) + } + + lines := strings.Split(sout, "\n") + if len(lines) >= len(origLines) { + t.Fatalf("Got %d lines of output, want <%d", len(lines), len(origLines)) + } +} + +func TestGroupCleanup(t *testing.T) { + if os.Getuid() != 0 { + t.Skip("we need root for credential") + } + cmd := exec.Command("id") + cmd.SysProcAttr = &syscall.SysProcAttr{ + Credential: &syscall.Credential{ + Uid: 0, + Gid: 0, + }, + } + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("Cmd failed with err %v, output: %s", err, out) + } + strOut := strings.TrimSpace(string(out)) + expected := "uid=0(root) gid=0(root) groups=0(root)" + // Just check prefix because some distros reportedly output a + // context parameter; see https://golang.org/issue/16224. + if !strings.HasPrefix(strOut, expected) { + t.Errorf("id command output: %q, expected prefix: %q", strOut, expected) + } +} + +func TestGroupCleanupUserNamespace(t *testing.T) { + if os.Getuid() != 0 { + t.Skip("we need root for credential") + } + checkUserNS(t) + cmd := exec.Command("id") + uid, gid := os.Getuid(), os.Getgid() + cmd.SysProcAttr = &syscall.SysProcAttr{ + Cloneflags: syscall.CLONE_NEWUSER, + Credential: &syscall.Credential{ + Uid: uint32(uid), + Gid: uint32(gid), + }, + UidMappings: []syscall.SysProcIDMap{ + {ContainerID: 0, HostID: uid, Size: 1}, + }, + GidMappings: []syscall.SysProcIDMap{ + {ContainerID: 0, HostID: gid, Size: 1}, + }, + } + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("Cmd failed with err %v, output: %s", err, out) + } + strOut := strings.TrimSpace(string(out)) + + // Strings we've seen in the wild. + expected := []string{ + "uid=0(root) gid=0(root) groups=0(root)", + "uid=0(root) gid=0(root) groups=0(root),65534(nobody)", + "uid=0(root) gid=0(root) groups=0(root),65534(nogroup)", + "uid=0(root) gid=0(root) groups=0(root),65534", + } + for _, e := range expected { + if strOut == e { + return + } + } + t.Errorf("id command output: %q, expected one of %q", strOut, expected) +} |