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.
27 func isDocker() bool {
28 _
, err
:= os
.Stat("/.dockerenv")
33 return os
.Getenv("container") == "lxc"
36 func skipInContainer(t
*testing
.T
) {
37 // TODO: the callers of this func are using this func to skip
38 // tests when running as some sort of "fake root" that's uid 0
39 // but lacks certain Linux capabilities. Most of the Go builds
40 // run in privileged containers, though, where root is much
41 // closer (if not identical) to the real root. We should test
42 // for what we need exactly (which capabilities are active?),
43 // instead of just assuming "docker == bad". Then we'd get more test
44 // coverage on a bunch of builders too.
46 t
.Skip("skip this test in Docker container")
49 t
.Skip("skip this test in LXC container")
53 func skipNoUserNamespaces(t
*testing
.T
) {
54 if _
, err
:= os
.Stat("/proc/self/ns/user"); err
!= nil {
55 if os
.IsNotExist(err
) {
56 t
.Skip("kernel doesn't support user namespaces")
58 if os
.IsPermission(err
) {
59 t
.Skip("unable to test user namespaces due to permissions")
61 t
.Fatalf("Failed to stat /proc/self/ns/user: %v", err
)
65 func skipUnprivilegedUserClone(t
*testing
.T
) {
66 // Skip the test if the sysctl that prevents unprivileged user
67 // from creating user namespaces is enabled.
68 data
, errRead
:= ioutil
.ReadFile("/proc/sys/kernel/unprivileged_userns_clone")
69 if errRead
!= nil ||
len(data
) < 1 || data
[0] == '0' {
70 t
.Skip("kernel prohibits user namespace in unprivileged process")
74 // Check if we are in a chroot by checking if the inode of / is
75 // different from 2 (there is no better test available to non-root on
77 func isChrooted(t
*testing
.T
) bool {
78 root
, err
:= os
.Stat("/")
80 t
.Fatalf("cannot stat /: %v", err
)
82 return root
.Sys().(*syscall
.Stat_t
).Ino
!= 2
85 func checkUserNS(t
*testing
.T
) {
87 skipNoUserNamespaces(t
)
89 // create_user_ns in the kernel (see
90 // https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/kernel/user_namespace.c)
91 // forbids the creation of user namespaces when chrooted.
92 t
.Skip("cannot create user namespaces when chrooted")
94 // On some systems, there is a sysctl setting.
96 skipUnprivilegedUserClone(t
)
98 // On Centos 7 make sure they set the kernel parameter user_namespace=1
99 // See issue 16283 and 20796.
100 if _
, err
:= os
.Stat("/sys/module/user_namespace/parameters/enable"); err
== nil {
101 buf
, _
:= ioutil
.ReadFile("/sys/module/user_namespace/parameters/enabled")
102 if !strings
.HasPrefix(string(buf
), "Y") {
103 t
.Skip("kernel doesn't support user namespaces")
107 // On Centos 7.5+, user namespaces are disabled if user.max_user_namespaces = 0
108 if _
, err
:= os
.Stat("/proc/sys/user/max_user_namespaces"); err
== nil {
109 buf
, errRead
:= ioutil
.ReadFile("/proc/sys/user/max_user_namespaces")
110 if errRead
== nil && buf
[0] == '0' {
111 t
.Skip("kernel doesn't support user namespaces")
115 // When running under the Go continuous build, skip tests for
116 // now when under Kubernetes. (where things are root but not quite)
117 // Both of these are our own environment variables.
119 if os
.Getenv("GO_BUILDER_NAME") != "" && os
.Getenv("IN_KUBERNETES") == "1" {
120 t
.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
124 func whoamiCmd(t
*testing
.T
, uid
, gid
int, setgroups
bool) *exec
.Cmd
{
126 cmd
:= exec
.Command("whoami")
127 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{
128 Cloneflags
: syscall
.CLONE_NEWUSER
,
129 UidMappings
: []syscall
.SysProcIDMap
{
130 {ContainerID
: 0, HostID
: uid
, Size
: 1},
132 GidMappings
: []syscall
.SysProcIDMap
{
133 {ContainerID
: 0, HostID
: gid
, Size
: 1},
135 GidMappingsEnableSetgroups
: setgroups
,
140 func testNEWUSERRemap(t
*testing
.T
, uid
, gid
int, setgroups
bool) {
141 cmd
:= whoamiCmd(t
, uid
, gid
, setgroups
)
142 out
, err
:= cmd
.CombinedOutput()
144 t
.Fatalf("Cmd failed with err %v, output: %s", err
, out
)
146 sout
:= strings
.TrimSpace(string(out
))
149 t
.Fatalf("whoami = %q; want %q", out
, want
)
153 func TestCloneNEWUSERAndRemapRootDisableSetgroups(t
*testing
.T
) {
154 if os
.Getuid() != 0 {
155 t
.Skip("skipping root only test")
157 testNEWUSERRemap(t
, 0, 0, false)
160 func TestCloneNEWUSERAndRemapRootEnableSetgroups(t
*testing
.T
) {
161 if os
.Getuid() != 0 {
162 t
.Skip("skipping root only test")
164 testNEWUSERRemap(t
, 0, 0, true)
167 func TestCloneNEWUSERAndRemapNoRootDisableSetgroups(t
*testing
.T
) {
168 if os
.Getuid() == 0 {
169 t
.Skip("skipping unprivileged user only test")
171 testNEWUSERRemap(t
, os
.Getuid(), os
.Getgid(), false)
174 func TestCloneNEWUSERAndRemapNoRootSetgroupsEnableSetgroups(t
*testing
.T
) {
175 if os
.Getuid() == 0 {
176 t
.Skip("skipping unprivileged user only test")
178 cmd
:= whoamiCmd(t
, os
.Getuid(), os
.Getgid(), true)
181 t
.Skip("probably old kernel without security fix")
183 if !os
.IsPermission(err
) {
184 t
.Fatalf("Unprivileged gid_map rewriting with GidMappingsEnableSetgroups must fail with permission error; got %v", err
)
188 func TestEmptyCredGroupsDisableSetgroups(t
*testing
.T
) {
189 cmd
:= whoamiCmd(t
, os
.Getuid(), os
.Getgid(), false)
190 cmd
.SysProcAttr
.Credential
= &syscall
.Credential
{}
191 if err
:= cmd
.Run(); err
!= nil {
196 func TestUnshare(t
*testing
.T
) {
198 // Make sure we are running as root so we have permissions to use unshare
199 // and create a network namespace.
200 if os
.Getuid() != 0 {
201 t
.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
204 // When running under the Go continuous build, skip tests for
205 // now when under Kubernetes. (where things are root but not quite)
206 // Both of these are our own environment variables.
208 if os
.Getenv("GO_BUILDER_NAME") != "" && os
.Getenv("IN_KUBERNETES") == "1" {
209 t
.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
212 path
:= "/proc/net/dev"
213 if _
, err
:= os
.Stat(path
); err
!= nil {
214 if os
.IsNotExist(err
) {
215 t
.Skip("kernel doesn't support proc filesystem")
217 if os
.IsPermission(err
) {
218 t
.Skip("unable to test proc filesystem due to permissions")
222 if _
, err
:= os
.Stat("/proc/self/ns/net"); err
!= nil {
223 if os
.IsNotExist(err
) {
224 t
.Skip("kernel doesn't support net namespace")
229 orig
, err
:= ioutil
.ReadFile(path
)
233 origLines
:= strings
.Split(strings
.TrimSpace(string(orig
)), "\n")
235 cmd
:= exec
.Command("cat", path
)
236 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{
237 Unshareflags
: syscall
.CLONE_NEWNET
,
239 out
, err
:= cmd
.CombinedOutput()
241 if strings
.Contains(err
.Error(), "operation not permitted") {
242 // Issue 17206: despite all the checks above,
243 // this still reportedly fails for some users.
244 // (older kernels?). Just skip.
245 t
.Skip("skipping due to permission error")
247 t
.Fatalf("Cmd failed with err %v, output: %s", err
, out
)
250 // Check there is only the local network interface
251 sout
:= strings
.TrimSpace(string(out
))
252 if !strings
.Contains(sout
, "lo:") {
253 t
.Fatalf("Expected lo network interface to exist, got %s", sout
)
256 lines
:= strings
.Split(sout
, "\n")
257 if len(lines
) >= len(origLines
) {
258 t
.Fatalf("Got %d lines of output, want <%d", len(lines
), len(origLines
))
262 func TestGroupCleanup(t
*testing
.T
) {
263 if os
.Getuid() != 0 {
264 t
.Skip("we need root for credential")
266 cmd
:= exec
.Command("id")
267 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{
268 Credential
: &syscall
.Credential
{
273 out
, err
:= cmd
.CombinedOutput()
275 t
.Fatalf("Cmd failed with err %v, output: %s", err
, out
)
277 strOut
:= strings
.TrimSpace(string(out
))
278 expected
:= "uid=0(root) gid=0(root)"
279 // Just check prefix because some distros reportedly output a
280 // context parameter; see https://golang.org/issue/16224.
281 // Alpine does not output groups; see https://golang.org/issue/19938.
282 if !strings
.HasPrefix(strOut
, expected
) {
283 t
.Errorf("id command output: %q, expected prefix: %q", strOut
, expected
)
287 func TestGroupCleanupUserNamespace(t
*testing
.T
) {
288 if os
.Getuid() != 0 {
289 t
.Skip("we need root for credential")
292 cmd
:= exec
.Command("id")
293 uid
, gid
:= os
.Getuid(), os
.Getgid()
294 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{
295 Cloneflags
: syscall
.CLONE_NEWUSER
,
296 Credential
: &syscall
.Credential
{
300 UidMappings
: []syscall
.SysProcIDMap
{
301 {ContainerID
: 0, HostID
: uid
, Size
: 1},
303 GidMappings
: []syscall
.SysProcIDMap
{
304 {ContainerID
: 0, HostID
: gid
, Size
: 1},
307 out
, err
:= cmd
.CombinedOutput()
309 t
.Fatalf("Cmd failed with err %v, output: %s", err
, out
)
311 strOut
:= strings
.TrimSpace(string(out
))
313 // Strings we've seen in the wild.
314 expected
:= []string{
315 "uid=0(root) gid=0(root) groups=0(root)",
316 "uid=0(root) gid=0(root) groups=0(root),65534(nobody)",
317 "uid=0(root) gid=0(root) groups=0(root),65534(nogroup)",
318 "uid=0(root) gid=0(root) groups=0(root),65534",
319 "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
320 "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
322 for _
, e
:= range expected
{
327 t
.Errorf("id command output: %q, expected one of %q", strOut
, expected
)
330 // TestUnshareHelperProcess isn't a real test. It's used as a helper process
331 // for TestUnshareMountNameSpace.
332 func TestUnshareMountNameSpaceHelper(*testing
.T
) {
333 if os
.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
337 if err
:= syscall
.Mount("none", flag
.Args()[0], "proc", 0, ""); err
!= nil {
338 fmt
.Fprintf(os
.Stderr
, "unshare: mount %v failed: %v", os
.Args
, err
)
343 // Test for Issue 38471: unshare fails because systemd has forced / to be shared
344 func TestUnshareMountNameSpace(t
*testing
.T
) {
346 // Make sure we are running as root so we have permissions to use unshare
347 // and create a network namespace.
348 if os
.Getuid() != 0 {
349 t
.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
352 d
, err
:= ioutil
.TempDir("", "unshare")
354 t
.Fatalf("tempdir: %v", err
)
357 cmd
:= exec
.Command(os
.Args
[0], "-test.run=TestUnshareMountNameSpaceHelper", d
)
358 cmd
.Env
= append(os
.Environ(), "GO_WANT_HELPER_PROCESS=1")
359 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{Unshareflags
: syscall
.CLONE_NEWNS
}
361 o
, err
:= cmd
.CombinedOutput()
363 if strings
.Contains(err
.Error(), ": permission denied") {
364 t
.Skipf("Skipping test (golang.org/issue/19698); unshare failed due to permissions: %s, %v", o
, err
)
366 t
.Fatalf("unshare failed: %s, %v", o
, err
)
369 // How do we tell if the namespace was really unshared? It turns out
370 // to be simple: just try to remove the directory. If it's still mounted
371 // on the rm will fail with EBUSY. Then we have some cleanup to do:
372 // we must unmount it, then try to remove it again.
374 if err
:= os
.Remove(d
); err
!= nil {
375 t
.Errorf("rmdir failed on %v: %v", d
, err
)
376 if err
:= syscall
.Unmount(d
, syscall
.MNT_FORCE
); err
!= nil {
377 t
.Errorf("Can't unmount %v: %v", d
, err
)
379 if err
:= os
.Remove(d
); err
!= nil {
380 t
.Errorf("rmdir after unmount failed on %v: %v", d
, err
)
385 // Test for Issue 20103: unshare fails when chroot is used
386 func TestUnshareMountNameSpaceChroot(t
*testing
.T
) {
388 // Make sure we are running as root so we have permissions to use unshare
389 // and create a network namespace.
390 if os
.Getuid() != 0 {
391 t
.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
394 d
, err
:= ioutil
.TempDir("", "unshare")
396 t
.Fatalf("tempdir: %v", err
)
399 // Since we are doing a chroot, we need the binary there,
400 // and it must be statically linked.
401 x
:= filepath
.Join(d
, "syscall.test")
402 cmd
:= exec
.Command(testenv
.GoToolPath(t
), "test", "-c", "-o", x
, "syscall")
403 cmd
.Env
= append(os
.Environ(), "CGO_ENABLED=0")
404 if o
, err
:= cmd
.CombinedOutput(); err
!= nil {
405 t
.Fatalf("Build of syscall in chroot failed, output %v, err %v", o
, err
)
408 cmd
= exec
.Command("/syscall.test", "-test.run=TestUnshareMountNameSpaceHelper", "/")
409 cmd
.Env
= append(os
.Environ(), "GO_WANT_HELPER_PROCESS=1")
410 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{Chroot
: d
, Unshareflags
: syscall
.CLONE_NEWNS
}
412 o
, err
:= cmd
.CombinedOutput()
414 if strings
.Contains(err
.Error(), ": permission denied") {
415 t
.Skipf("Skipping test (golang.org/issue/19698); unshare failed due to permissions: %s, %v", o
, err
)
417 t
.Fatalf("unshare failed: %s, %v", o
, err
)
420 // How do we tell if the namespace was really unshared? It turns out
421 // to be simple: just try to remove the executable. If it's still mounted
422 // on, the rm will fail. Then we have some cleanup to do:
423 // we must force unmount it, then try to remove it again.
425 if err
:= os
.Remove(x
); err
!= nil {
426 t
.Errorf("rm failed on %v: %v", x
, err
)
427 if err
:= syscall
.Unmount(d
, syscall
.MNT_FORCE
); err
!= nil {
428 t
.Fatalf("Can't unmount %v: %v", d
, err
)
430 if err
:= os
.Remove(x
); err
!= nil {
431 t
.Fatalf("rm failed on %v: %v", x
, err
)
435 if err
:= os
.Remove(d
); err
!= nil {
436 t
.Errorf("rmdir failed on %v: %v", d
, err
)
440 func TestUnshareUidGidMappingHelper(*testing
.T
) {
441 if os
.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
445 if err
:= syscall
.Chroot(os
.TempDir()); err
!= nil {
446 fmt
.Fprintln(os
.Stderr
, err
)
451 // Test for Issue 29789: unshare fails when uid/gid mapping is specified
452 func TestUnshareUidGidMapping(t
*testing
.T
) {
453 if os
.Getuid() == 0 {
454 t
.Skip("test exercises unprivileged user namespace, fails with privileges")
457 cmd
:= exec
.Command(os
.Args
[0], "-test.run=TestUnshareUidGidMappingHelper")
458 cmd
.Env
= append(os
.Environ(), "GO_WANT_HELPER_PROCESS=1")
459 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{
460 Unshareflags
: syscall
.CLONE_NEWNS | syscall
.CLONE_NEWUSER
,
461 GidMappingsEnableSetgroups
: false,
462 UidMappings
: []syscall
.SysProcIDMap
{
465 HostID
: syscall
.Getuid(),
469 GidMappings
: []syscall
.SysProcIDMap
{
472 HostID
: syscall
.Getgid(),
477 out
, err
:= cmd
.CombinedOutput()
479 t
.Fatalf("Cmd failed with err %v, output: %s", err
, out
)
483 type capHeader
struct {
488 type capData
struct {
494 const CAP_SYS_TIME
= 25
495 const CAP_SYSLOG
= 34
502 func getCaps() (caps
, error
) {
505 // Get capability version
506 if _
, _
, errno
:= syscall
.Syscall(syscall
.SYS_CAPGET
, uintptr(unsafe
.Pointer(&c
.hdr
)), uintptr(unsafe
.Pointer(nil)), 0); errno
!= 0 {
507 return c
, fmt
.Errorf("SYS_CAPGET: %v", errno
)
510 // Get current capabilities
511 if _
, _
, errno
:= syscall
.Syscall(syscall
.SYS_CAPGET
, uintptr(unsafe
.Pointer(&c
.hdr
)), uintptr(unsafe
.Pointer(&c
.data
[0])), 0); errno
!= 0 {
512 return c
, fmt
.Errorf("SYS_CAPGET: %v", errno
)
518 func mustSupportAmbientCaps(t
*testing
.T
) {
519 var uname syscall
.Utsname
520 if err
:= syscall
.Uname(&uname
); err
!= nil {
521 t
.Fatalf("Uname: %v", err
)
524 for i
, b
:= range uname
.Release
{
527 ver
:= string(buf
[:])
528 if i
:= strings
.Index(ver
, "\x00"); i
!= -1 {
531 if strings
.HasPrefix(ver
, "2.") ||
532 strings
.HasPrefix(ver
, "3.") ||
533 strings
.HasPrefix(ver
, "4.1.") ||
534 strings
.HasPrefix(ver
, "4.2.") {
535 t
.Skipf("kernel version %q predates required 4.3; skipping test", ver
)
539 // TestAmbientCapsHelper isn't a real test. It's used as a helper process for
541 func TestAmbientCapsHelper(*testing
.T
) {
542 if os
.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
547 caps
, err
:= getCaps()
549 fmt
.Fprintln(os
.Stderr
, err
)
552 if caps
.data
[0].effective
&(1<<uint(CAP_SYS_TIME
)) == 0 {
553 fmt
.Fprintln(os
.Stderr
, "CAP_SYS_TIME unexpectedly not in the effective capability mask")
556 if caps
.data
[1].effective
&(1<<uint(CAP_SYSLOG
&31)) == 0 {
557 fmt
.Fprintln(os
.Stderr
, "CAP_SYSLOG unexpectedly not in the effective capability mask")
562 func TestAmbientCaps(t
*testing
.T
) {
563 // Make sure we are running as root so we have permissions to use unshare
564 // and create a network namespace.
565 if os
.Getuid() != 0 {
566 t
.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
569 testAmbientCaps(t
, false)
572 func TestAmbientCapsUserns(t
*testing
.T
) {
574 testAmbientCaps(t
, true)
577 func testAmbientCaps(t
*testing
.T
, userns
bool) {
579 mustSupportAmbientCaps(t
)
581 skipUnprivilegedUserClone(t
)
583 // skip on android, due to lack of lookup support
584 if runtime
.GOOS
== "android" {
585 t
.Skip("skipping test on android; see Issue 27327")
588 u
, err
:= user
.Lookup("nobody")
592 uid
, err
:= strconv
.ParseInt(u
.Uid
, 0, 32)
596 gid
, err
:= strconv
.ParseInt(u
.Gid
, 0, 32)
601 // Copy the test binary to a temporary location which is readable by nobody.
602 f
, err
:= ioutil
.TempFile("", "gotest")
606 defer os
.Remove(f
.Name())
608 e
, err
:= os
.Open(os
.Args
[0])
613 if _
, err
:= io
.Copy(f
, e
); err
!= nil {
616 if err
:= f
.Chmod(0755); err
!= nil {
619 if err
:= f
.Close(); err
!= nil {
623 cmd
:= exec
.Command(f
.Name(), "-test.run=TestAmbientCapsHelper")
624 cmd
.Env
= append(os
.Environ(), "GO_WANT_HELPER_PROCESS=1")
625 cmd
.Stdout
= os
.Stdout
626 cmd
.Stderr
= os
.Stderr
627 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{
628 Credential
: &syscall
.Credential
{
632 AmbientCaps
: []uintptr{CAP_SYS_TIME
, CAP_SYSLOG
},
635 cmd
.SysProcAttr
.Cloneflags
= syscall
.CLONE_NEWUSER
639 cmd
.SysProcAttr
.UidMappings
= []syscall
.SysProcIDMap
{{
640 ContainerID
: int(nobody
),
644 cmd
.SysProcAttr
.GidMappings
= []syscall
.SysProcIDMap
{{
645 ContainerID
: int(nobody
),
650 // Set credentials to run as user and group nobody.
651 cmd
.SysProcAttr
.Credential
= &syscall
.Credential
{
656 if err
:= cmd
.Run(); err
!= nil {