Source file src/syscall/exec_linux_test.go

Documentation: syscall

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build linux
     6  
     7  package syscall_test
     8  
     9  import (
    10  	"flag"
    11  	"fmt"
    12  	"internal/testenv"
    13  	"io"
    14  	"io/ioutil"
    15  	"os"
    16  	"os/exec"
    17  	"os/user"
    18  	"path/filepath"
    19  	"runtime"
    20  	"strconv"
    21  	"strings"
    22  	"syscall"
    23  	"testing"
    24  	"unsafe"
    25  )
    26  
    27  func isDocker() bool {
    28  	_, err := os.Stat("/.dockerenv")
    29  	return err == nil
    30  }
    31  
    32  func isLXC() bool {
    33  	return os.Getenv("container") == "lxc"
    34  }
    35  
    36  func skipInContainer(t *testing.T) {
    37  	if isDocker() {
    38  		t.Skip("skip this test in Docker container")
    39  	}
    40  	if isLXC() {
    41  		t.Skip("skip this test in LXC container")
    42  	}
    43  }
    44  
    45  func skipUnprivilegedUserClone(t *testing.T) {
    46  	// Skip the test if the sysctl that prevents unprivileged user
    47  	// from creating user namespaces is enabled.
    48  	data, errRead := ioutil.ReadFile("/proc/sys/kernel/unprivileged_userns_clone")
    49  	if errRead != nil || len(data) < 1 || data[0] == '0' {
    50  		t.Skip("kernel prohibits user namespace in unprivileged process")
    51  	}
    52  }
    53  
    54  // Check if we are in a chroot by checking if the inode of / is
    55  // different from 2 (there is no better test available to non-root on
    56  // linux).
    57  func isChrooted(t *testing.T) bool {
    58  	root, err := os.Stat("/")
    59  	if err != nil {
    60  		t.Fatalf("cannot stat /: %v", err)
    61  	}
    62  	return root.Sys().(*syscall.Stat_t).Ino != 2
    63  }
    64  
    65  func checkUserNS(t *testing.T) {
    66  	skipInContainer(t)
    67  	if _, err := os.Stat("/proc/self/ns/user"); err != nil {
    68  		if os.IsNotExist(err) {
    69  			t.Skip("kernel doesn't support user namespaces")
    70  		}
    71  		if os.IsPermission(err) {
    72  			t.Skip("unable to test user namespaces due to permissions")
    73  		}
    74  		t.Fatalf("Failed to stat /proc/self/ns/user: %v", err)
    75  	}
    76  	if isChrooted(t) {
    77  		// create_user_ns in the kernel (see
    78  		// https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/kernel/user_namespace.c)
    79  		// forbids the creation of user namespaces when chrooted.
    80  		t.Skip("cannot create user namespaces when chrooted")
    81  	}
    82  	// On some systems, there is a sysctl setting.
    83  	if os.Getuid() != 0 {
    84  		skipUnprivilegedUserClone(t)
    85  	}
    86  	// On Centos 7 make sure they set the kernel parameter user_namespace=1
    87  	// See issue 16283 and 20796.
    88  	if _, err := os.Stat("/sys/module/user_namespace/parameters/enable"); err == nil {
    89  		buf, _ := ioutil.ReadFile("/sys/module/user_namespace/parameters/enabled")
    90  		if !strings.HasPrefix(string(buf), "Y") {
    91  			t.Skip("kernel doesn't support user namespaces")
    92  		}
    93  	}
    94  
    95  	// On Centos 7.5+, user namespaces are disabled if user.max_user_namespaces = 0
    96  	if _, err := os.Stat("/proc/sys/user/max_user_namespaces"); err == nil {
    97  		buf, errRead := ioutil.ReadFile("/proc/sys/user/max_user_namespaces")
    98  		if errRead == nil && buf[0] == '0' {
    99  			t.Skip("kernel doesn't support user namespaces")
   100  		}
   101  	}
   102  
   103  	// When running under the Go continuous build, skip tests for
   104  	// now when under Kubernetes. (where things are root but not quite)
   105  	// Both of these are our own environment variables.
   106  	// See Issue 12815.
   107  	if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" {
   108  		t.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
   109  	}
   110  }
   111  
   112  func whoamiCmd(t *testing.T, uid, gid int, setgroups bool) *exec.Cmd {
   113  	checkUserNS(t)
   114  	cmd := exec.Command("whoami")
   115  	cmd.SysProcAttr = &syscall.SysProcAttr{
   116  		Cloneflags: syscall.CLONE_NEWUSER,
   117  		UidMappings: []syscall.SysProcIDMap{
   118  			{ContainerID: 0, HostID: uid, Size: 1},
   119  		},
   120  		GidMappings: []syscall.SysProcIDMap{
   121  			{ContainerID: 0, HostID: gid, Size: 1},
   122  		},
   123  		GidMappingsEnableSetgroups: setgroups,
   124  	}
   125  	return cmd
   126  }
   127  
   128  func testNEWUSERRemap(t *testing.T, uid, gid int, setgroups bool) {
   129  	cmd := whoamiCmd(t, uid, gid, setgroups)
   130  	out, err := cmd.CombinedOutput()
   131  	if err != nil {
   132  		t.Fatalf("Cmd failed with err %v, output: %s", err, out)
   133  	}
   134  	sout := strings.TrimSpace(string(out))
   135  	want := "root"
   136  	if sout != want {
   137  		t.Fatalf("whoami = %q; want %q", out, want)
   138  	}
   139  }
   140  
   141  func TestCloneNEWUSERAndRemapRootDisableSetgroups(t *testing.T) {
   142  	if os.Getuid() != 0 {
   143  		t.Skip("skipping root only test")
   144  	}
   145  	testNEWUSERRemap(t, 0, 0, false)
   146  }
   147  
   148  func TestCloneNEWUSERAndRemapRootEnableSetgroups(t *testing.T) {
   149  	if os.Getuid() != 0 {
   150  		t.Skip("skipping root only test")
   151  	}
   152  	testNEWUSERRemap(t, 0, 0, true)
   153  }
   154  
   155  func TestCloneNEWUSERAndRemapNoRootDisableSetgroups(t *testing.T) {
   156  	if os.Getuid() == 0 {
   157  		t.Skip("skipping unprivileged user only test")
   158  	}
   159  	testNEWUSERRemap(t, os.Getuid(), os.Getgid(), false)
   160  }
   161  
   162  func TestCloneNEWUSERAndRemapNoRootSetgroupsEnableSetgroups(t *testing.T) {
   163  	if os.Getuid() == 0 {
   164  		t.Skip("skipping unprivileged user only test")
   165  	}
   166  	cmd := whoamiCmd(t, os.Getuid(), os.Getgid(), true)
   167  	err := cmd.Run()
   168  	if err == nil {
   169  		t.Skip("probably old kernel without security fix")
   170  	}
   171  	if !os.IsPermission(err) {
   172  		t.Fatalf("Unprivileged gid_map rewriting with GidMappingsEnableSetgroups must fail")
   173  	}
   174  }
   175  
   176  func TestEmptyCredGroupsDisableSetgroups(t *testing.T) {
   177  	cmd := whoamiCmd(t, os.Getuid(), os.Getgid(), false)
   178  	cmd.SysProcAttr.Credential = &syscall.Credential{}
   179  	if err := cmd.Run(); err != nil {
   180  		t.Fatal(err)
   181  	}
   182  }
   183  
   184  func TestUnshare(t *testing.T) {
   185  	skipInContainer(t)
   186  	// Make sure we are running as root so we have permissions to use unshare
   187  	// and create a network namespace.
   188  	if os.Getuid() != 0 {
   189  		t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
   190  	}
   191  
   192  	// When running under the Go continuous build, skip tests for
   193  	// now when under Kubernetes. (where things are root but not quite)
   194  	// Both of these are our own environment variables.
   195  	// See Issue 12815.
   196  	if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" {
   197  		t.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
   198  	}
   199  
   200  	path := "/proc/net/dev"
   201  	if _, err := os.Stat(path); err != nil {
   202  		if os.IsNotExist(err) {
   203  			t.Skip("kernel doesn't support proc filesystem")
   204  		}
   205  		if os.IsPermission(err) {
   206  			t.Skip("unable to test proc filesystem due to permissions")
   207  		}
   208  		t.Fatal(err)
   209  	}
   210  	if _, err := os.Stat("/proc/self/ns/net"); err != nil {
   211  		if os.IsNotExist(err) {
   212  			t.Skip("kernel doesn't support net namespace")
   213  		}
   214  		t.Fatal(err)
   215  	}
   216  
   217  	orig, err := ioutil.ReadFile(path)
   218  	if err != nil {
   219  		t.Fatal(err)
   220  	}
   221  	origLines := strings.Split(strings.TrimSpace(string(orig)), "\n")
   222  
   223  	cmd := exec.Command("cat", path)
   224  	cmd.SysProcAttr = &syscall.SysProcAttr{
   225  		Unshareflags: syscall.CLONE_NEWNET,
   226  	}
   227  	out, err := cmd.CombinedOutput()
   228  	if err != nil {
   229  		if strings.Contains(err.Error(), "operation not permitted") {
   230  			// Issue 17206: despite all the checks above,
   231  			// this still reportedly fails for some users.
   232  			// (older kernels?). Just skip.
   233  			t.Skip("skipping due to permission error")
   234  		}
   235  		t.Fatalf("Cmd failed with err %v, output: %s", err, out)
   236  	}
   237  
   238  	// Check there is only the local network interface
   239  	sout := strings.TrimSpace(string(out))
   240  	if !strings.Contains(sout, "lo:") {
   241  		t.Fatalf("Expected lo network interface to exist, got %s", sout)
   242  	}
   243  
   244  	lines := strings.Split(sout, "\n")
   245  	if len(lines) >= len(origLines) {
   246  		t.Fatalf("Got %d lines of output, want <%d", len(lines), len(origLines))
   247  	}
   248  }
   249  
   250  func TestGroupCleanup(t *testing.T) {
   251  	if os.Getuid() != 0 {
   252  		t.Skip("we need root for credential")
   253  	}
   254  	cmd := exec.Command("id")
   255  	cmd.SysProcAttr = &syscall.SysProcAttr{
   256  		Credential: &syscall.Credential{
   257  			Uid: 0,
   258  			Gid: 0,
   259  		},
   260  	}
   261  	out, err := cmd.CombinedOutput()
   262  	if err != nil {
   263  		t.Fatalf("Cmd failed with err %v, output: %s", err, out)
   264  	}
   265  	strOut := strings.TrimSpace(string(out))
   266  	expected := "uid=0(root) gid=0(root)"
   267  	// Just check prefix because some distros reportedly output a
   268  	// context parameter; see https://golang.org/issue/16224.
   269  	// Alpine does not output groups; see https://golang.org/issue/19938.
   270  	if !strings.HasPrefix(strOut, expected) {
   271  		t.Errorf("id command output: %q, expected prefix: %q", strOut, expected)
   272  	}
   273  }
   274  
   275  func TestGroupCleanupUserNamespace(t *testing.T) {
   276  	if os.Getuid() != 0 {
   277  		t.Skip("we need root for credential")
   278  	}
   279  	checkUserNS(t)
   280  	cmd := exec.Command("id")
   281  	uid, gid := os.Getuid(), os.Getgid()
   282  	cmd.SysProcAttr = &syscall.SysProcAttr{
   283  		Cloneflags: syscall.CLONE_NEWUSER,
   284  		Credential: &syscall.Credential{
   285  			Uid: uint32(uid),
   286  			Gid: uint32(gid),
   287  		},
   288  		UidMappings: []syscall.SysProcIDMap{
   289  			{ContainerID: 0, HostID: uid, Size: 1},
   290  		},
   291  		GidMappings: []syscall.SysProcIDMap{
   292  			{ContainerID: 0, HostID: gid, Size: 1},
   293  		},
   294  	}
   295  	out, err := cmd.CombinedOutput()
   296  	if err != nil {
   297  		t.Fatalf("Cmd failed with err %v, output: %s", err, out)
   298  	}
   299  	strOut := strings.TrimSpace(string(out))
   300  
   301  	// Strings we've seen in the wild.
   302  	expected := []string{
   303  		"uid=0(root) gid=0(root) groups=0(root)",
   304  		"uid=0(root) gid=0(root) groups=0(root),65534(nobody)",
   305  		"uid=0(root) gid=0(root) groups=0(root),65534(nogroup)",
   306  		"uid=0(root) gid=0(root) groups=0(root),65534",
   307  		"uid=0(root) gid=0(root) groups=0(root),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody),65534(nobody)", // Alpine; see https://golang.org/issue/19938
   308  	}
   309  	for _, e := range expected {
   310  		if strOut == e {
   311  			return
   312  		}
   313  	}
   314  	t.Errorf("id command output: %q, expected one of %q", strOut, expected)
   315  }
   316  
   317  // TestUnshareHelperProcess isn't a real test. It's used as a helper process
   318  // for TestUnshareMountNameSpace.
   319  func TestUnshareMountNameSpaceHelper(*testing.T) {
   320  	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
   321  		return
   322  	}
   323  	defer os.Exit(0)
   324  	if err := syscall.Mount("none", flag.Args()[0], "proc", 0, ""); err != nil {
   325  		fmt.Fprintf(os.Stderr, "unshare: mount %v failed: %v", os.Args, err)
   326  		os.Exit(2)
   327  	}
   328  }
   329  
   330  // Test for Issue 38471: unshare fails because systemd has forced / to be shared
   331  func TestUnshareMountNameSpace(t *testing.T) {
   332  	skipInContainer(t)
   333  	// Make sure we are running as root so we have permissions to use unshare
   334  	// and create a network namespace.
   335  	if os.Getuid() != 0 {
   336  		t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
   337  	}
   338  
   339  	// When running under the Go continuous build, skip tests for
   340  	// now when under Kubernetes. (where things are root but not quite)
   341  	// Both of these are our own environment variables.
   342  	// See Issue 12815.
   343  	if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" {
   344  		t.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
   345  	}
   346  
   347  	d, err := ioutil.TempDir("", "unshare")
   348  	if err != nil {
   349  		t.Fatalf("tempdir: %v", err)
   350  	}
   351  
   352  	cmd := exec.Command(os.Args[0], "-test.run=TestUnshareMountNameSpaceHelper", d)
   353  	cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
   354  	cmd.SysProcAttr = &syscall.SysProcAttr{Unshareflags: syscall.CLONE_NEWNS}
   355  
   356  	o, err := cmd.CombinedOutput()
   357  	if err != nil {
   358  		if strings.Contains(err.Error(), ": permission denied") {
   359  			t.Skipf("Skipping test (golang.org/issue/19698); unshare failed due to permissions: %s, %v", o, err)
   360  		}
   361  		t.Fatalf("unshare failed: %s, %v", o, err)
   362  	}
   363  
   364  	// How do we tell if the namespace was really unshared? It turns out
   365  	// to be simple: just try to remove the directory. If it's still mounted
   366  	// on the rm will fail with EBUSY. Then we have some cleanup to do:
   367  	// we must unmount it, then try to remove it again.
   368  
   369  	if err := os.Remove(d); err != nil {
   370  		t.Errorf("rmdir failed on %v: %v", d, err)
   371  		if err := syscall.Unmount(d, syscall.MNT_FORCE); err != nil {
   372  			t.Errorf("Can't unmount %v: %v", d, err)
   373  		}
   374  		if err := os.Remove(d); err != nil {
   375  			t.Errorf("rmdir after unmount failed on %v: %v", d, err)
   376  		}
   377  	}
   378  }
   379  
   380  // Test for Issue 20103: unshare fails when chroot is used
   381  func TestUnshareMountNameSpaceChroot(t *testing.T) {
   382  	skipInContainer(t)
   383  	// Make sure we are running as root so we have permissions to use unshare
   384  	// and create a network namespace.
   385  	if os.Getuid() != 0 {
   386  		t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
   387  	}
   388  
   389  	// When running under the Go continuous build, skip tests for
   390  	// now when under Kubernetes. (where things are root but not quite)
   391  	// Both of these are our own environment variables.
   392  	// See Issue 12815.
   393  	if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" {
   394  		t.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
   395  	}
   396  
   397  	d, err := ioutil.TempDir("", "unshare")
   398  	if err != nil {
   399  		t.Fatalf("tempdir: %v", err)
   400  	}
   401  
   402  	// Since we are doing a chroot, we need the binary there,
   403  	// and it must be statically linked.
   404  	x := filepath.Join(d, "syscall.test")
   405  	cmd := exec.Command(testenv.GoToolPath(t), "test", "-c", "-o", x, "syscall")
   406  	cmd.Env = append(os.Environ(), "CGO_ENABLED=0")
   407  	if o, err := cmd.CombinedOutput(); err != nil {
   408  		t.Fatalf("Build of syscall in chroot failed, output %v, err %v", o, err)
   409  	}
   410  
   411  	cmd = exec.Command("/syscall.test", "-test.run=TestUnshareMountNameSpaceHelper", "/")
   412  	cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
   413  	cmd.SysProcAttr = &syscall.SysProcAttr{Chroot: d, Unshareflags: syscall.CLONE_NEWNS}
   414  
   415  	o, err := cmd.CombinedOutput()
   416  	if err != nil {
   417  		if strings.Contains(err.Error(), ": permission denied") {
   418  			t.Skipf("Skipping test (golang.org/issue/19698); unshare failed due to permissions: %s, %v", o, err)
   419  		}
   420  		t.Fatalf("unshare failed: %s, %v", o, err)
   421  	}
   422  
   423  	// How do we tell if the namespace was really unshared? It turns out
   424  	// to be simple: just try to remove the executable. If it's still mounted
   425  	// on, the rm will fail. Then we have some cleanup to do:
   426  	// we must force unmount it, then try to remove it again.
   427  
   428  	if err := os.Remove(x); err != nil {
   429  		t.Errorf("rm failed on %v: %v", x, err)
   430  		if err := syscall.Unmount(d, syscall.MNT_FORCE); err != nil {
   431  			t.Fatalf("Can't unmount %v: %v", d, err)
   432  		}
   433  		if err := os.Remove(x); err != nil {
   434  			t.Fatalf("rm failed on %v: %v", x, err)
   435  		}
   436  	}
   437  
   438  	if err := os.Remove(d); err != nil {
   439  		t.Errorf("rmdir failed on %v: %v", d, err)
   440  	}
   441  }
   442  
   443  func TestUnshareUidGidMappingHelper(*testing.T) {
   444  	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
   445  		return
   446  	}
   447  	defer os.Exit(0)
   448  	if err := syscall.Chroot(os.TempDir()); err != nil {
   449  		fmt.Fprintln(os.Stderr, err)
   450  		os.Exit(2)
   451  	}
   452  }
   453  
   454  // Test for Issue 29789: unshare fails when uid/gid mapping is specified
   455  func TestUnshareUidGidMapping(t *testing.T) {
   456  	if os.Getuid() == 0 {
   457  		t.Skip("test exercises unprivileged user namespace, fails with privileges")
   458  	}
   459  	checkUserNS(t)
   460  	cmd := exec.Command(os.Args[0], "-test.run=TestUnshareUidGidMappingHelper")
   461  	cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
   462  	cmd.SysProcAttr = &syscall.SysProcAttr{
   463  		Unshareflags:               syscall.CLONE_NEWNS | syscall.CLONE_NEWUSER,
   464  		GidMappingsEnableSetgroups: false,
   465  		UidMappings: []syscall.SysProcIDMap{
   466  			{
   467  				ContainerID: 0,
   468  				HostID:      syscall.Getuid(),
   469  				Size:        1,
   470  			},
   471  		},
   472  		GidMappings: []syscall.SysProcIDMap{
   473  			{
   474  				ContainerID: 0,
   475  				HostID:      syscall.Getgid(),
   476  				Size:        1,
   477  			},
   478  		},
   479  	}
   480  	out, err := cmd.CombinedOutput()
   481  	if err != nil {
   482  		t.Fatalf("Cmd failed with err %v, output: %s", err, out)
   483  	}
   484  }
   485  
   486  type capHeader struct {
   487  	version uint32
   488  	pid     int32
   489  }
   490  
   491  type capData struct {
   492  	effective   uint32
   493  	permitted   uint32
   494  	inheritable uint32
   495  }
   496  
   497  const CAP_SYS_TIME = 25
   498  const CAP_SYSLOG = 34
   499  
   500  type caps struct {
   501  	hdr  capHeader
   502  	data [2]capData
   503  }
   504  
   505  func getCaps() (caps, error) {
   506  	var c caps
   507  
   508  	// Get capability version
   509  	if _, _, errno := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(&c.hdr)), uintptr(unsafe.Pointer(nil)), 0); errno != 0 {
   510  		return c, fmt.Errorf("SYS_CAPGET: %v", errno)
   511  	}
   512  
   513  	// Get current capabilities
   514  	if _, _, errno := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(&c.hdr)), uintptr(unsafe.Pointer(&c.data[0])), 0); errno != 0 {
   515  		return c, fmt.Errorf("SYS_CAPGET: %v", errno)
   516  	}
   517  
   518  	return c, nil
   519  }
   520  
   521  func mustSupportAmbientCaps(t *testing.T) {
   522  	var uname syscall.Utsname
   523  	if err := syscall.Uname(&uname); err != nil {
   524  		t.Fatalf("Uname: %v", err)
   525  	}
   526  	var buf [65]byte
   527  	for i, b := range uname.Release {
   528  		buf[i] = byte(b)
   529  	}
   530  	ver := string(buf[:])
   531  	if i := strings.Index(ver, "\x00"); i != -1 {
   532  		ver = ver[:i]
   533  	}
   534  	if strings.HasPrefix(ver, "2.") ||
   535  		strings.HasPrefix(ver, "3.") ||
   536  		strings.HasPrefix(ver, "4.1.") ||
   537  		strings.HasPrefix(ver, "4.2.") {
   538  		t.Skipf("kernel version %q predates required 4.3; skipping test", ver)
   539  	}
   540  }
   541  
   542  // TestAmbientCapsHelper isn't a real test. It's used as a helper process for
   543  // TestAmbientCaps.
   544  func TestAmbientCapsHelper(*testing.T) {
   545  	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
   546  		return
   547  	}
   548  	defer os.Exit(0)
   549  
   550  	caps, err := getCaps()
   551  	if err != nil {
   552  		fmt.Fprintln(os.Stderr, err)
   553  		os.Exit(2)
   554  	}
   555  	if caps.data[0].effective&(1<<uint(CAP_SYS_TIME)) == 0 {
   556  		fmt.Fprintln(os.Stderr, "CAP_SYS_TIME unexpectedly not in the effective capability mask")
   557  		os.Exit(2)
   558  	}
   559  	if caps.data[1].effective&(1<<uint(CAP_SYSLOG&31)) == 0 {
   560  		fmt.Fprintln(os.Stderr, "CAP_SYSLOG unexpectedly not in the effective capability mask")
   561  		os.Exit(2)
   562  	}
   563  }
   564  
   565  func TestAmbientCaps(t *testing.T) {
   566  	// Make sure we are running as root so we have permissions to use unshare
   567  	// and create a network namespace.
   568  	if os.Getuid() != 0 {
   569  		t.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
   570  	}
   571  
   572  	testAmbientCaps(t, false)
   573  }
   574  
   575  func TestAmbientCapsUserns(t *testing.T) {
   576  	testAmbientCaps(t, true)
   577  }
   578  
   579  func testAmbientCaps(t *testing.T, userns bool) {
   580  	skipInContainer(t)
   581  	mustSupportAmbientCaps(t)
   582  
   583  	// When running under the Go continuous build, skip tests for
   584  	// now when under Kubernetes. (where things are root but not quite)
   585  	// Both of these are our own environment variables.
   586  	// See Issue 12815.
   587  	if os.Getenv("GO_BUILDER_NAME") != "" && os.Getenv("IN_KUBERNETES") == "1" {
   588  		t.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
   589  	}
   590  
   591  	skipUnprivilegedUserClone(t)
   592  
   593  	// skip on android, due to lack of lookup support
   594  	if runtime.GOOS == "android" {
   595  		t.Skip("skipping test on android; see Issue 27327")
   596  	}
   597  
   598  	u, err := user.Lookup("nobody")
   599  	if err != nil {
   600  		t.Fatal(err)
   601  	}
   602  	uid, err := strconv.ParseInt(u.Uid, 0, 32)
   603  	if err != nil {
   604  		t.Fatal(err)
   605  	}
   606  	gid, err := strconv.ParseInt(u.Gid, 0, 32)
   607  	if err != nil {
   608  		t.Fatal(err)
   609  	}
   610  
   611  	// Copy the test binary to a temporary location which is readable by nobody.
   612  	f, err := ioutil.TempFile("", "gotest")
   613  	if err != nil {
   614  		t.Fatal(err)
   615  	}
   616  	defer os.Remove(f.Name())
   617  	defer f.Close()
   618  	e, err := os.Open(os.Args[0])
   619  	if err != nil {
   620  		t.Fatal(err)
   621  	}
   622  	defer e.Close()
   623  	if _, err := io.Copy(f, e); err != nil {
   624  		t.Fatal(err)
   625  	}
   626  	if err := f.Chmod(0755); err != nil {
   627  		t.Fatal(err)
   628  	}
   629  	if err := f.Close(); err != nil {
   630  		t.Fatal(err)
   631  	}
   632  
   633  	cmd := exec.Command(f.Name(), "-test.run=TestAmbientCapsHelper")
   634  	cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
   635  	cmd.Stdout = os.Stdout
   636  	cmd.Stderr = os.Stderr
   637  	cmd.SysProcAttr = &syscall.SysProcAttr{
   638  		Credential: &syscall.Credential{
   639  			Uid: uint32(uid),
   640  			Gid: uint32(gid),
   641  		},
   642  		AmbientCaps: []uintptr{CAP_SYS_TIME, CAP_SYSLOG},
   643  	}
   644  	if userns {
   645  		cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWUSER
   646  		const nobody = 65534
   647  		uid := os.Getuid()
   648  		gid := os.Getgid()
   649  		cmd.SysProcAttr.UidMappings = []syscall.SysProcIDMap{{
   650  			ContainerID: int(nobody),
   651  			HostID:      int(uid),
   652  			Size:        int(1),
   653  		}}
   654  		cmd.SysProcAttr.GidMappings = []syscall.SysProcIDMap{{
   655  			ContainerID: int(nobody),
   656  			HostID:      int(gid),
   657  			Size:        int(1),
   658  		}}
   659  
   660  		// Set credentials to run as user and group nobody.
   661  		cmd.SysProcAttr.Credential = &syscall.Credential{
   662  			Uid: nobody,
   663  			Gid: nobody,
   664  		}
   665  	}
   666  	if err := cmd.Run(); err != nil {
   667  		t.Fatal(err.Error())
   668  	}
   669  }
   670  

View as plain text