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.
18 // Check if we are in a chroot by checking if the inode of / is
19 // different from 2 (there is no better test available to non-root on
21 func isChrooted(t
*testing
.T
) bool {
22 root
, err
:= os
.Stat("/")
24 t
.Fatalf("cannot stat /: %v", err
)
26 return root
.Sys().(*syscall
.Stat_t
).Ino
!= 2
29 func checkUserNS(t
*testing
.T
) {
30 if _
, err
:= os
.Stat("/proc/self/ns/user"); err
!= nil {
31 if os
.IsNotExist(err
) {
32 t
.Skip("kernel doesn't support user namespaces")
34 if os
.IsPermission(err
) {
35 t
.Skip("unable to test user namespaces due to permissions")
37 t
.Fatalf("Failed to stat /proc/self/ns/user: %v", err
)
40 // create_user_ns in the kernel (see
41 // https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/kernel/user_namespace.c)
42 // forbids the creation of user namespaces when chrooted.
43 t
.Skip("cannot create user namespaces when chrooted")
45 // On some systems, there is a sysctl setting.
47 data
, errRead
:= ioutil
.ReadFile("/proc/sys/kernel/unprivileged_userns_clone")
48 if errRead
== nil && data
[0] == '0' {
49 t
.Skip("kernel prohibits user namespace in unprivileged process")
52 // When running under the Go continuous build, skip tests for
53 // now when under Kubernetes. (where things are root but not quite)
54 // Both of these are our own environment variables.
56 if os
.Getenv("GO_BUILDER_NAME") != "" && os
.Getenv("IN_KUBERNETES") == "1" {
57 t
.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
61 func whoamiCmd(t
*testing
.T
, uid
, gid
int, setgroups
bool) *exec
.Cmd
{
63 cmd
:= exec
.Command("whoami")
64 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{
65 Cloneflags
: syscall
.CLONE_NEWUSER
,
66 UidMappings
: []syscall
.SysProcIDMap
{
67 {ContainerID
: 0, HostID
: uid
, Size
: 1},
69 GidMappings
: []syscall
.SysProcIDMap
{
70 {ContainerID
: 0, HostID
: gid
, Size
: 1},
72 GidMappingsEnableSetgroups
: setgroups
,
77 func testNEWUSERRemap(t
*testing
.T
, uid
, gid
int, setgroups
bool) {
78 cmd
:= whoamiCmd(t
, uid
, gid
, setgroups
)
79 out
, err
:= cmd
.CombinedOutput()
81 t
.Fatalf("Cmd failed with err %v, output: %s", err
, out
)
83 sout
:= strings
.TrimSpace(string(out
))
86 t
.Fatalf("whoami = %q; want %q", out
, want
)
90 func TestCloneNEWUSERAndRemapRootDisableSetgroups(t
*testing
.T
) {
92 t
.Skip("skipping root only test")
94 testNEWUSERRemap(t
, 0, 0, false)
97 func TestCloneNEWUSERAndRemapRootEnableSetgroups(t
*testing
.T
) {
99 t
.Skip("skipping root only test")
101 testNEWUSERRemap(t
, 0, 0, false)
104 func TestCloneNEWUSERAndRemapNoRootDisableSetgroups(t
*testing
.T
) {
105 if os
.Getuid() == 0 {
106 t
.Skip("skipping unprivileged user only test")
108 testNEWUSERRemap(t
, os
.Getuid(), os
.Getgid(), false)
111 func TestCloneNEWUSERAndRemapNoRootSetgroupsEnableSetgroups(t
*testing
.T
) {
112 if os
.Getuid() == 0 {
113 t
.Skip("skipping unprivileged user only test")
115 cmd
:= whoamiCmd(t
, os
.Getuid(), os
.Getgid(), true)
118 t
.Skip("probably old kernel without security fix")
120 if !os
.IsPermission(err
) {
121 t
.Fatalf("Unprivileged gid_map rewriting with GidMappingsEnableSetgroups must fail")
125 func TestEmptyCredGroupsDisableSetgroups(t
*testing
.T
) {
126 cmd
:= whoamiCmd(t
, os
.Getuid(), os
.Getgid(), false)
127 cmd
.SysProcAttr
.Credential
= &syscall
.Credential
{}
128 if err
:= cmd
.Run(); err
!= nil {
133 func TestUnshare(t
*testing
.T
) {
134 // Make sure we are running as root so we have permissions to use unshare
135 // and create a network namespace.
136 if os
.Getuid() != 0 {
137 t
.Skip("kernel prohibits unshare in unprivileged process, unless using user namespace")
140 // When running under the Go continuous build, skip tests for
141 // now when under Kubernetes. (where things are root but not quite)
142 // Both of these are our own environment variables.
144 if os
.Getenv("GO_BUILDER_NAME") != "" && os
.Getenv("IN_KUBERNETES") == "1" {
145 t
.Skip("skipping test on Kubernetes-based builders; see Issue 12815")
148 path
:= "/proc/net/dev"
149 if _
, err
:= os
.Stat(path
); err
!= nil {
150 if os
.IsNotExist(err
) {
151 t
.Skip("kernel doesn't support proc filesystem")
153 if os
.IsPermission(err
) {
154 t
.Skip("unable to test proc filesystem due to permissions")
158 if _
, err
:= os
.Stat("/proc/self/ns/net"); err
!= nil {
159 if os
.IsNotExist(err
) {
160 t
.Skip("kernel doesn't support net namespace")
165 orig
, err
:= ioutil
.ReadFile(path
)
169 origLines
:= strings
.Split(strings
.TrimSpace(string(orig
)), "\n")
171 cmd
:= exec
.Command("cat", path
)
172 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{
173 Unshareflags
: syscall
.CLONE_NEWNET
,
175 out
, err
:= cmd
.CombinedOutput()
177 t
.Fatalf("Cmd failed with err %v, output: %s", err
, out
)
180 // Check there is only the local network interface
181 sout
:= strings
.TrimSpace(string(out
))
182 if !strings
.Contains(sout
, "lo:") {
183 t
.Fatalf("Expected lo network interface to exist, got %s", sout
)
186 lines
:= strings
.Split(sout
, "\n")
187 if len(lines
) >= len(origLines
) {
188 t
.Fatalf("Got %d lines of output, want <%d", len(lines
), len(origLines
))
192 func TestGroupCleanup(t
*testing
.T
) {
193 if os
.Getuid() != 0 {
194 t
.Skip("we need root for credential")
196 cmd
:= exec
.Command("id")
197 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{
198 Credential
: &syscall
.Credential
{
203 out
, err
:= cmd
.CombinedOutput()
205 t
.Fatalf("Cmd failed with err %v, output: %s", err
, out
)
207 strOut
:= strings
.TrimSpace(string(out
))
208 expected
:= "uid=0(root) gid=0(root) groups=0(root)"
209 // Just check prefix because some distros reportedly output a
210 // context parameter; see https://golang.org/issue/16224.
211 if !strings
.HasPrefix(strOut
, expected
) {
212 t
.Errorf("id command output: %q, expected prefix: %q", strOut
, expected
)
216 func TestGroupCleanupUserNamespace(t
*testing
.T
) {
217 if os
.Getuid() != 0 {
218 t
.Skip("we need root for credential")
221 cmd
:= exec
.Command("id")
222 uid
, gid
:= os
.Getuid(), os
.Getgid()
223 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{
224 Cloneflags
: syscall
.CLONE_NEWUSER
,
225 Credential
: &syscall
.Credential
{
229 UidMappings
: []syscall
.SysProcIDMap
{
230 {ContainerID
: 0, HostID
: uid
, Size
: 1},
232 GidMappings
: []syscall
.SysProcIDMap
{
233 {ContainerID
: 0, HostID
: gid
, Size
: 1},
236 out
, err
:= cmd
.CombinedOutput()
238 t
.Fatalf("Cmd failed with err %v, output: %s", err
, out
)
240 strOut
:= strings
.TrimSpace(string(out
))
242 // Strings we've seen in the wild.
243 expected
:= []string{
244 "uid=0(root) gid=0(root) groups=0(root)",
245 "uid=0(root) gid=0(root) groups=0(root),65534(nobody)",
246 "uid=0(root) gid=0(root) groups=0(root),65534(nogroup)",
247 "uid=0(root) gid=0(root) groups=0(root),65534",
249 for _
, e
:= range expected
{
254 t
.Errorf("id command output: %q, expected one of %q", strOut
, expected
)