Source file src/syscall/exec_linux_test.go

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

View as plain text