annotate libgo/go/syscall/exec_linux_test.go @ 138:fc828634a951

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