exec_linux_test.go 18 KB

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