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.
26 func isDocker() bool {
27 _
, err
:= os
.Stat("/.dockerenv")
32 return os
.Getenv("container") == "lxc"
35 func skipInContainer(t
*testing
.T
) {
37 t
.Skip("skip this test in Docker container")
40 t
.Skip("skip this test in LXC container")
44 // Check if we are in a chroot by checking if the inode of / is
45 // different from 2 (there is no better test available to non-root on
47 func isChrooted(t
*testing
.T
) bool {
48 root
, err
:= os
.Stat("/")
50 t
.Fatalf("cannot stat /: %v", err
)
52 return root
.Sys().(*syscall
.Stat_t
).Ino
!= 2
55 func checkUserNS(t
*testing
.T
) {
57 if _
, err
:= os
.Stat("/proc/self/ns/user"); err
!= nil {
58 if os
.IsNotExist(err
) {
59 t
.Skip("kernel doesn't support user namespaces")
61 if os
.IsPermission(err
) {
62 t
.Skip("unable to test user namespaces due to permissions")
64 t
.Fatalf("Failed to stat /proc/self/ns/user: %v", err
)
67 // create_user_ns in the kernel (see
68 // https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/kernel/user_namespace.c)
69 // forbids the creation of user namespaces when chrooted.
70 t
.Skip("cannot create user namespaces when chrooted")
72 // On some systems, there is a sysctl setting.
74 data
, errRead
:= ioutil
.ReadFile("/proc/sys/kernel/unprivileged_userns_clone")
75 if errRead
== nil && data
[0] == '0' {
76 t
.Skip("kernel prohibits user namespace in unprivileged process")
79 // On Centos 7 make sure they set the kernel parameter user_namespace=1
80 // See issue 16283 and 20796.
81 if _
, err
:= os
.Stat("/sys/module/user_namespace/parameters/enable"); err
== nil {
82 buf
, _
:= ioutil
.ReadFile("/sys/module/user_namespace/parameters/enabled")
83 if !strings
.HasPrefix(string(buf
), "Y") {
84 t
.Skip("kernel doesn't support user namespaces")
88 // On Centos 7.5+, user namespaces are disabled if user.max_user_namespaces = 0
89 if _
, err
:= os
.Stat("/proc/sys/user/max_user_namespaces"); err
== nil {
90 buf
, errRead
:= ioutil
.ReadFile("/proc/sys/user/max_user_namespaces")
91 if errRead
== nil && buf
[0] == '0' {
92 t
.Skip("kernel doesn't support user namespaces")
96 // When running under the Go continuous build, skip tests for
97 // now when under Kubernetes. (where things are root but not quite)
98 // Both of these are our own environment variables.
100 if os
.Getenv("GO_BUILDER_NAME") != "" && os
.Getenv("IN_KUBERNETES") == "1" {
101 t
.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
105 func whoamiCmd(t
*testing
.T
, uid
, gid
int, setgroups
bool) *exec
.Cmd
{
107 cmd
:= exec
.Command("whoami")
108 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{
109 Cloneflags
: syscall
.CLONE_NEWUSER
,
110 UidMappings
: []syscall
.SysProcIDMap
{
111 {ContainerID
: 0, HostID
: uid
, Size
: 1},
113 GidMappings
: []syscall
.SysProcIDMap
{
114 {ContainerID
: 0, HostID
: gid
, Size
: 1},
116 GidMappingsEnableSetgroups
: setgroups
,
121 func testNEWUSERRemap(t
*testing
.T
, uid
, gid
int, setgroups
bool) {
122 cmd
:= whoamiCmd(t
, uid
, gid
, setgroups
)
123 out
, err
:= cmd
.CombinedOutput()
125 t
.Fatalf("Cmd failed with err %v, output: %s", err
, out
)
127 sout
:= strings
.TrimSpace(string(out
))
130 t
.Fatalf("whoami = %q; want %q", out
, want
)
134 func TestCloneNEWUSERAndRemapRootDisableSetgroups(t
*testing
.T
) {
135 if os
.Getuid() != 0 {
136 t
.Skip("skipping root only test")
138 testNEWUSERRemap(t
, 0, 0, false)
141 func TestCloneNEWUSERAndRemapRootEnableSetgroups(t
*testing
.T
) {
142 if os
.Getuid() != 0 {
143 t
.Skip("skipping root only test")
145 testNEWUSERRemap(t
, 0, 0, true)
148 func TestCloneNEWUSERAndRemapNoRootDisableSetgroups(t
*testing
.T
) {
149 if os
.Getuid() == 0 {
150 t
.Skip("skipping unprivileged user only test")
152 testNEWUSERRemap(t
, os
.Getuid(), os
.Getgid(), false)
155 func TestCloneNEWUSERAndRemapNoRootSetgroupsEnableSetgroups(t
*testing
.T
) {
156 if os
.Getuid() == 0 {
157 t
.Skip("skipping unprivileged user only test")
159 cmd
:= whoamiCmd(t
, os
.Getuid(), os
.Getgid(), true)
162 t
.Skip("probably old kernel without security fix")
164 if !os
.IsPermission(err
) {
165 t
.Fatalf("Unprivileged gid_map rewriting with GidMappingsEnableSetgroups must fail")
169 func TestEmptyCredGroupsDisableSetgroups(t
*testing
.T
) {
170 cmd
:= whoamiCmd(t
, os
.Getuid(), os
.Getgid(), false)
171 cmd
.SysProcAttr
.Credential
= &syscall
.Credential
{}
172 if err
:= cmd
.Run(); err
!= nil {
177 func TestUnshare(t
*testing
.T
) {
179 // Make sure we are running as root so we have permissions to use unshare
180 // and create a network namespace.
181 if os
.Getuid() != 0 {
182 t
.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
185 // When running under the Go continuous build, skip tests for
186 // now when under Kubernetes. (where things are root but not quite)
187 // Both of these are our own environment variables.
189 if os
.Getenv("GO_BUILDER_NAME") != "" && os
.Getenv("IN_KUBERNETES") == "1" {
190 t
.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
193 path
:= "/proc/net/dev"
194 if _
, err
:= os
.Stat(path
); err
!= nil {
195 if os
.IsNotExist(err
) {
196 t
.Skip("kernel doesn't support proc filesystem")
198 if os
.IsPermission(err
) {
199 t
.Skip("unable to test proc filesystem due to permissions")
203 if _
, err
:= os
.Stat("/proc/self/ns/net"); err
!= nil {
204 if os
.IsNotExist(err
) {
205 t
.Skip("kernel doesn't support net namespace")
210 orig
, err
:= ioutil
.ReadFile(path
)
214 origLines
:= strings
.Split(strings
.TrimSpace(string(orig
)), "\n")
216 cmd
:= exec
.Command("cat", path
)
217 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{
218 Unshareflags
: syscall
.CLONE_NEWNET
,
220 out
, err
:= cmd
.CombinedOutput()
222 if strings
.Contains(err
.Error(), "operation not permitted") {
223 // Issue 17206: despite all the checks above,
224 // this still reportedly fails for some users.
225 // (older kernels?). Just skip.
226 t
.Skip("skipping due to permission error")
228 t
.Fatalf("Cmd failed with err %v, output: %s", err
, out
)
231 // Check there is only the local network interface
232 sout
:= strings
.TrimSpace(string(out
))
233 if !strings
.Contains(sout
, "lo:") {
234 t
.Fatalf("Expected lo network interface to exist, got %s", sout
)
237 lines
:= strings
.Split(sout
, "\n")
238 if len(lines
) >= len(origLines
) {
239 t
.Fatalf("Got %d lines of output, want <%d", len(lines
), len(origLines
))
243 func TestGroupCleanup(t
*testing
.T
) {
244 if os
.Getuid() != 0 {
245 t
.Skip("we need root for credential")
247 cmd
:= exec
.Command("id")
248 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{
249 Credential
: &syscall
.Credential
{
254 out
, err
:= cmd
.CombinedOutput()
256 t
.Fatalf("Cmd failed with err %v, output: %s", err
, out
)
258 strOut
:= strings
.TrimSpace(string(out
))
259 expected
:= "uid=0(root) gid=0(root)"
260 // Just check prefix because some distros reportedly output a
261 // context parameter; see https://golang.org/issue/16224.
262 // Alpine does not output groups; see https://golang.org/issue/19938.
263 if !strings
.HasPrefix(strOut
, expected
) {
264 t
.Errorf("id command output: %q, expected prefix: %q", strOut
, expected
)
268 func TestGroupCleanupUserNamespace(t
*testing
.T
) {
269 if os
.Getuid() != 0 {
270 t
.Skip("we need root for credential")
273 cmd
:= exec
.Command("id")
274 uid
, gid
:= os
.Getuid(), os
.Getgid()
275 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{
276 Cloneflags
: syscall
.CLONE_NEWUSER
,
277 Credential
: &syscall
.Credential
{
281 UidMappings
: []syscall
.SysProcIDMap
{
282 {ContainerID
: 0, HostID
: uid
, Size
: 1},
284 GidMappings
: []syscall
.SysProcIDMap
{
285 {ContainerID
: 0, HostID
: gid
, Size
: 1},
288 out
, err
:= cmd
.CombinedOutput()
290 t
.Fatalf("Cmd failed with err %v, output: %s", err
, out
)
292 strOut
:= strings
.TrimSpace(string(out
))
294 // Strings we've seen in the wild.
295 expected
:= []string{
296 "uid=0(root) gid=0(root) groups=0(root)",
297 "uid=0(root) gid=0(root) groups=0(root),65534(nobody)",
298 "uid=0(root) gid=0(root) groups=0(root),65534(nogroup)",
299 "uid=0(root) gid=0(root) groups=0(root),65534",
300 "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
302 for _
, e
:= range expected
{
307 t
.Errorf("id command output: %q, expected one of %q", strOut
, expected
)
310 // TestUnshareHelperProcess isn't a real test. It's used as a helper process
311 // for TestUnshareMountNameSpace.
312 func TestUnshareMountNameSpaceHelper(*testing
.T
) {
313 if os
.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
317 if err
:= syscall
.Mount("none", flag
.Args()[0], "proc", 0, ""); err
!= nil {
318 fmt
.Fprintf(os
.Stderr
, "unshare: mount %v failed: %v", os
.Args
, err
)
323 // Test for Issue 38471: unshare fails because systemd has forced / to be shared
324 func TestUnshareMountNameSpace(t
*testing
.T
) {
326 // Make sure we are running as root so we have permissions to use unshare
327 // and create a network namespace.
328 if os
.Getuid() != 0 {
329 t
.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
332 // When running under the Go continuous build, skip tests for
333 // now when under Kubernetes. (where things are root but not quite)
334 // Both of these are our own environment variables.
336 if os
.Getenv("GO_BUILDER_NAME") != "" && os
.Getenv("IN_KUBERNETES") == "1" {
337 t
.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
340 d
, err
:= ioutil
.TempDir("", "unshare")
342 t
.Fatalf("tempdir: %v", err
)
345 cmd
:= exec
.Command(os
.Args
[0], "-test.run=TestUnshareMountNameSpaceHelper", d
)
346 cmd
.Env
= []string{"GO_WANT_HELPER_PROCESS=1"}
347 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{Unshareflags
: syscall
.CLONE_NEWNS
}
349 o
, err
:= cmd
.CombinedOutput()
351 if strings
.Contains(err
.Error(), ": permission denied") {
352 t
.Skipf("Skipping test (golang.org/issue/19698); unshare failed due to permissions: %s, %v", o
, err
)
354 t
.Fatalf("unshare failed: %s, %v", o
, err
)
357 // How do we tell if the namespace was really unshared? It turns out
358 // to be simple: just try to remove the directory. If it's still mounted
359 // on the rm will fail with EBUSY. Then we have some cleanup to do:
360 // we must unmount it, then try to remove it again.
362 if err
:= os
.Remove(d
); err
!= nil {
363 t
.Errorf("rmdir failed on %v: %v", d
, err
)
364 if err
:= syscall
.Unmount(d
, syscall
.MNT_FORCE
); err
!= nil {
365 t
.Errorf("Can't unmount %v: %v", d
, err
)
367 if err
:= os
.Remove(d
); err
!= nil {
368 t
.Errorf("rmdir after unmount failed on %v: %v", d
, err
)
373 // Test for Issue 20103: unshare fails when chroot is used
374 func TestUnshareMountNameSpaceChroot(t
*testing
.T
) {
376 // Make sure we are running as root so we have permissions to use unshare
377 // and create a network namespace.
378 if os
.Getuid() != 0 {
379 t
.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
382 // When running under the Go continuous build, skip tests for
383 // now when under Kubernetes. (where things are root but not quite)
384 // Both of these are our own environment variables.
386 if os
.Getenv("GO_BUILDER_NAME") != "" && os
.Getenv("IN_KUBERNETES") == "1" {
387 t
.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
390 d
, err
:= ioutil
.TempDir("", "unshare")
392 t
.Fatalf("tempdir: %v", err
)
395 // Since we are doing a chroot, we need the binary there,
396 // and it must be statically linked.
397 x
:= filepath
.Join(d
, "syscall.test")
398 cmd
:= exec
.Command(testenv
.GoToolPath(t
), "test", "-c", "-o", x
, "syscall")
399 cmd
.Env
= append(os
.Environ(), "CGO_ENABLED=0")
400 if o
, err
:= cmd
.CombinedOutput(); err
!= nil {
401 t
.Fatalf("Build of syscall in chroot failed, output %v, err %v", o
, err
)
404 cmd
= exec
.Command("/syscall.test", "-test.run=TestUnshareMountNameSpaceHelper", "/")
405 cmd
.Env
= []string{"GO_WANT_HELPER_PROCESS=1"}
406 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{Chroot
: d
, Unshareflags
: syscall
.CLONE_NEWNS
}
408 o
, err
:= cmd
.CombinedOutput()
410 if strings
.Contains(err
.Error(), ": permission denied") {
411 t
.Skipf("Skipping test (golang.org/issue/19698); unshare failed due to permissions: %s, %v", o
, err
)
413 t
.Fatalf("unshare failed: %s, %v", o
, err
)
416 // How do we tell if the namespace was really unshared? It turns out
417 // to be simple: just try to remove the executable. If it's still mounted
418 // on, the rm will fail. Then we have some cleanup to do:
419 // we must force unmount it, then try to remove it again.
421 if err
:= os
.Remove(x
); err
!= nil {
422 t
.Errorf("rm failed on %v: %v", x
, err
)
423 if err
:= syscall
.Unmount(d
, syscall
.MNT_FORCE
); err
!= nil {
424 t
.Fatalf("Can't unmount %v: %v", d
, err
)
426 if err
:= os
.Remove(x
); err
!= nil {
427 t
.Fatalf("rm failed on %v: %v", x
, err
)
431 if err
:= os
.Remove(d
); err
!= nil {
432 t
.Errorf("rmdir failed on %v: %v", d
, err
)
436 type capHeader
struct {
441 type capData
struct {
447 const CAP_SYS_TIME
= 25
454 func getCaps() (caps
, error
) {
457 // Get capability version
458 if _
, _
, errno
:= syscall
.Syscall(syscall
.SYS_CAPGET
, uintptr(unsafe
.Pointer(&c
.hdr
)), uintptr(unsafe
.Pointer(nil)), 0); errno
!= 0 {
459 return c
, fmt
.Errorf("SYS_CAPGET: %v", errno
)
462 // Get current capabilities
463 if _
, _
, errno
:= syscall
.Syscall(syscall
.SYS_CAPGET
, uintptr(unsafe
.Pointer(&c
.hdr
)), uintptr(unsafe
.Pointer(&c
.data
[0])), 0); errno
!= 0 {
464 return c
, fmt
.Errorf("SYS_CAPGET: %v", errno
)
470 func mustSupportAmbientCaps(t
*testing
.T
) {
471 var uname syscall
.Utsname
472 if err
:= syscall
.Uname(&uname
); err
!= nil {
473 t
.Fatalf("Uname: %v", err
)
476 for i
, b
:= range uname
.Release
{
479 ver
:= string(buf
[:])
480 if i
:= strings
.Index(ver
, "\x00"); i
!= -1 {
483 if strings
.HasPrefix(ver
, "2.") ||
484 strings
.HasPrefix(ver
, "3.") ||
485 strings
.HasPrefix(ver
, "4.1.") ||
486 strings
.HasPrefix(ver
, "4.2.") {
487 t
.Skipf("kernel version %q predates required 4.3; skipping test", ver
)
491 // TestAmbientCapsHelper isn't a real test. It's used as a helper process for
493 func TestAmbientCapsHelper(*testing
.T
) {
494 if os
.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
499 caps
, err
:= getCaps()
501 fmt
.Fprintln(os
.Stderr
, err
)
504 if caps
.data
[0].effective
&(1<<uint(CAP_SYS_TIME
)) == 0 {
505 fmt
.Fprintln(os
.Stderr
, "CAP_SYS_TIME unexpectedly not in the effective capability mask")
510 func TestAmbientCaps(t
*testing
.T
) {
512 // Make sure we are running as root so we have permissions to use unshare
513 // and create a network namespace.
514 if os
.Getuid() != 0 {
515 t
.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
517 mustSupportAmbientCaps(t
)
519 // When running under the Go continuous build, skip tests for
520 // now when under Kubernetes. (where things are root but not quite)
521 // Both of these are our own environment variables.
523 if os
.Getenv("GO_BUILDER_NAME") != "" && os
.Getenv("IN_KUBERNETES") == "1" {
524 t
.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
527 caps
, err
:= getCaps()
532 // Add CAP_SYS_TIME to the permitted and inheritable capability mask,
533 // otherwise we will not be able to add it to the ambient capability mask.
534 caps
.data
[0].permitted |
= 1 << uint(CAP_SYS_TIME
)
535 caps
.data
[0].inheritable |
= 1 << uint(CAP_SYS_TIME
)
537 if _
, _
, errno
:= syscall
.Syscall(syscall
.SYS_CAPSET
, uintptr(unsafe
.Pointer(&caps
.hdr
)), uintptr(unsafe
.Pointer(&caps
.data
[0])), 0); errno
!= 0 {
538 t
.Fatalf("SYS_CAPSET: %v", errno
)
541 u
, err
:= user
.Lookup("nobody")
545 uid
, err
:= strconv
.ParseInt(u
.Uid
, 0, 32)
549 gid
, err
:= strconv
.ParseInt(u
.Gid
, 0, 32)
554 // Copy the test binary to a temporary location which is readable by nobody.
555 f
, err
:= ioutil
.TempFile("", "gotest")
559 defer os
.Remove(f
.Name())
561 e
, err
:= os
.Open(os
.Args
[0])
566 if _
, err
:= io
.Copy(f
, e
); err
!= nil {
569 if err
:= f
.Chmod(0755); err
!= nil {
572 if err
:= f
.Close(); err
!= nil {
576 cmd
:= exec
.Command(f
.Name(), "-test.run=TestAmbientCapsHelper")
577 cmd
.Env
= []string{"GO_WANT_HELPER_PROCESS=1"}
578 cmd
.Stdout
= os
.Stdout
579 cmd
.Stderr
= os
.Stderr
580 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{
581 Credential
: &syscall
.Credential
{
585 AmbientCaps
: []uintptr{CAP_SYS_TIME
},
587 if err
:= cmd
.Run(); err
!= nil {