github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/root/chroot_test.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package root is used for tests that requires sysadmin privileges run. 16 package root 17 18 import ( 19 "context" 20 "fmt" 21 "io/ioutil" 22 "os/exec" 23 "path/filepath" 24 "strconv" 25 "strings" 26 "testing" 27 28 "github.com/SagerNet/gvisor/pkg/test/dockerutil" 29 ) 30 31 // TestChroot verifies that the sandbox is chroot'd and that mounts are cleaned 32 // up after the sandbox is destroyed. 33 func TestChroot(t *testing.T) { 34 ctx := context.Background() 35 d := dockerutil.MakeContainer(ctx, t) 36 defer d.CleanUp(ctx) 37 38 if err := d.Spawn(ctx, dockerutil.RunOpts{ 39 Image: "basic/alpine", 40 }, "sleep", "10000"); err != nil { 41 t.Fatalf("docker run failed: %v", err) 42 } 43 44 pid, err := d.SandboxPid(ctx) 45 if err != nil { 46 t.Fatalf("Docker.SandboxPid(): %v", err) 47 } 48 49 // Check that sandbox is chroot'ed. 50 procRoot := filepath.Join("/proc", strconv.Itoa(pid), "root") 51 chroot, err := filepath.EvalSymlinks(procRoot) 52 if err != nil { 53 t.Fatalf("error resolving /proc/<pid>/root symlink: %v", err) 54 } 55 if chroot != "/" { 56 t.Errorf("sandbox is not chroot'd, it should be inside: /, got: %q", chroot) 57 } 58 59 path, err := filepath.EvalSymlinks(filepath.Join("/proc", strconv.Itoa(pid), "cwd")) 60 if err != nil { 61 t.Fatalf("error resolving /proc/<pid>/cwd symlink: %v", err) 62 } 63 if chroot != path { 64 t.Errorf("sandbox current dir is wrong, want: %q, got: %q", chroot, path) 65 } 66 67 fi, err := ioutil.ReadDir(procRoot) 68 if err != nil { 69 t.Fatalf("error listing %q: %v", chroot, err) 70 } 71 if want, got := 1, len(fi); want != got { 72 t.Fatalf("chroot dir got %d entries, want %d", got, want) 73 } 74 75 // chroot dir is prepared by runsc and should contains only /proc. 76 if fi[0].Name() != "proc" { 77 t.Errorf("chroot got children %v, want %v", fi[0].Name(), "proc") 78 } 79 80 d.CleanUp(ctx) 81 } 82 83 func TestChrootGofer(t *testing.T) { 84 ctx := context.Background() 85 d := dockerutil.MakeContainer(ctx, t) 86 defer d.CleanUp(ctx) 87 88 if err := d.Spawn(ctx, dockerutil.RunOpts{ 89 Image: "basic/alpine", 90 }, "sleep", "10000"); err != nil { 91 t.Fatalf("docker run failed: %v", err) 92 } 93 94 // It's tricky to find gofers. Get sandbox PID first, then find parent. From 95 // parent get all immediate children, remove the sandbox, and everything else 96 // are gofers. 97 sandPID, err := d.SandboxPid(ctx) 98 if err != nil { 99 t.Fatalf("Docker.SandboxPid(): %v", err) 100 } 101 102 // Find sandbox's parent PID. 103 cmd := fmt.Sprintf("grep PPid /proc/%d/status | awk '{print $2}'", sandPID) 104 parent, err := exec.Command("sh", "-c", cmd).CombinedOutput() 105 if err != nil { 106 t.Fatalf("failed to fetch runsc (%d) parent PID: %v, out:\n%s", sandPID, err, string(parent)) 107 } 108 parentPID, err := strconv.Atoi(strings.TrimSpace(string(parent))) 109 if err != nil { 110 t.Fatalf("failed to parse PPID %q: %v", string(parent), err) 111 } 112 113 // Get all children from parent. 114 childrenOut, err := exec.Command("/usr/bin/pgrep", "-P", strconv.Itoa(parentPID)).CombinedOutput() 115 if err != nil { 116 t.Fatalf("failed to fetch containerd-shim children: %v", err) 117 } 118 children := strings.Split(strings.TrimSpace(string(childrenOut)), "\n") 119 120 // This where the root directory is mapped on the host and that's where the 121 // gofer must have chroot'd to. 122 root := "/root" 123 124 for _, child := range children { 125 childPID, err := strconv.Atoi(child) 126 if err != nil { 127 t.Fatalf("failed to parse child PID %q: %v", child, err) 128 } 129 if childPID == sandPID { 130 // Skip the sandbox, all other immediate children are gofers. 131 continue 132 } 133 134 // Check that gofer is chroot'ed. 135 chroot, err := filepath.EvalSymlinks(filepath.Join("/proc", child, "root")) 136 if err != nil { 137 t.Fatalf("error resolving /proc/<pid>/root symlink: %v", err) 138 } 139 if root != chroot { 140 t.Errorf("gofer chroot is wrong, want: %q, got: %q", root, chroot) 141 } 142 143 path, err := filepath.EvalSymlinks(filepath.Join("/proc", child, "cwd")) 144 if err != nil { 145 t.Fatalf("error resolving /proc/<pid>/cwd symlink: %v", err) 146 } 147 if root != path { 148 t.Errorf("gofer current dir is wrong, want: %q, got: %q", root, path) 149 } 150 } 151 }