github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/e2e/exec_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 integration provides end-to-end integration tests for runsc. These 16 // tests require docker and runsc to be installed on the machine. 17 // 18 // Each test calls docker commands to start up a container, and tests that it 19 // is behaving properly, with various runsc commands. The container is killed 20 // and deleted at the end. 21 22 package integration 23 24 import ( 25 "context" 26 "fmt" 27 "strconv" 28 "strings" 29 "testing" 30 "time" 31 32 "github.com/SagerNet/gvisor/pkg/abi/linux" 33 "github.com/SagerNet/gvisor/pkg/bits" 34 "github.com/SagerNet/gvisor/pkg/test/dockerutil" 35 "github.com/SagerNet/gvisor/runsc/specutils" 36 ) 37 38 // Test that exec uses the exact same capability set as the container. 39 func TestExecCapabilities(t *testing.T) { 40 ctx := context.Background() 41 d := dockerutil.MakeContainer(ctx, t) 42 defer d.CleanUp(ctx) 43 44 // Start the container. 45 if err := d.Spawn(ctx, dockerutil.RunOpts{ 46 Image: "basic/alpine", 47 }, "sh", "-c", "cat /proc/self/status; sleep 100"); err != nil { 48 t.Fatalf("docker run failed: %v", err) 49 } 50 51 // Check that capability. 52 matches, err := d.WaitForOutputSubmatch(ctx, "CapEff:\t([0-9a-f]+)\n", 5*time.Second) 53 if err != nil { 54 t.Fatalf("WaitForOutputSubmatch() timeout: %v", err) 55 } 56 if len(matches) != 2 { 57 t.Fatalf("There should be a match for the whole line and the capability bitmask") 58 } 59 want := fmt.Sprintf("CapEff:\t%s\n", matches[1]) 60 t.Log("Root capabilities:", want) 61 62 // Now check that exec'd process capabilities match the root. 63 got, err := d.Exec(ctx, dockerutil.ExecOpts{}, "grep", "CapEff:", "/proc/self/status") 64 if err != nil { 65 t.Fatalf("docker exec failed: %v", err) 66 } 67 t.Logf("CapEff: %v", got) 68 if got != want { 69 t.Errorf("wrong capabilities, got: %q, want: %q", got, want) 70 } 71 } 72 73 // Test that 'exec --privileged' adds all capabilities, except for CAP_NET_RAW 74 // which is removed from the container when --net-raw=false. 75 func TestExecPrivileged(t *testing.T) { 76 ctx := context.Background() 77 d := dockerutil.MakeContainer(ctx, t) 78 defer d.CleanUp(ctx) 79 80 // Start the container with all capabilities dropped. 81 if err := d.Spawn(ctx, dockerutil.RunOpts{ 82 Image: "basic/alpine", 83 CapDrop: []string{"all"}, 84 }, "sh", "-c", "cat /proc/self/status; sleep 100"); err != nil { 85 t.Fatalf("docker run failed: %v", err) 86 } 87 88 // Check that all capabilities where dropped from container. 89 matches, err := d.WaitForOutputSubmatch(ctx, "CapEff:\t([0-9a-f]+)\n", 5*time.Second) 90 if err != nil { 91 t.Fatalf("WaitForOutputSubmatch() timeout: %v", err) 92 } 93 if len(matches) != 2 { 94 t.Fatalf("There should be a match for the whole line and the capability bitmask") 95 } 96 containerCaps, err := strconv.ParseUint(matches[1], 16, 64) 97 if err != nil { 98 t.Fatalf("failed to convert capabilities %q: %v", matches[1], err) 99 } 100 t.Logf("Container capabilities: %#x", containerCaps) 101 if containerCaps != 0 { 102 t.Fatalf("Container should have no capabilities: %x", containerCaps) 103 } 104 105 // Check that 'exec --privileged' adds all capabilities, except for 106 // CAP_NET_RAW. 107 got, err := d.Exec(ctx, dockerutil.ExecOpts{ 108 Privileged: true, 109 }, "grep", "CapEff:", "/proc/self/status") 110 if err != nil { 111 t.Fatalf("docker exec failed: %v", err) 112 } 113 t.Logf("Exec CapEff: %v", got) 114 want := fmt.Sprintf("CapEff:\t%016x\n", specutils.AllCapabilitiesUint64()&^bits.MaskOf64(int(linux.CAP_NET_RAW))) 115 if got != want { 116 t.Errorf("Wrong capabilities, got: %q, want: %q. Make sure runsc is not using '--net-raw'", got, want) 117 } 118 } 119 120 func TestExecJobControl(t *testing.T) { 121 ctx := context.Background() 122 d := dockerutil.MakeContainer(ctx, t) 123 defer d.CleanUp(ctx) 124 125 // Start the container. 126 if err := d.Spawn(ctx, dockerutil.RunOpts{ 127 Image: "basic/alpine", 128 }, "sleep", "1000"); err != nil { 129 t.Fatalf("docker run failed: %v", err) 130 } 131 132 p, err := d.ExecProcess(ctx, dockerutil.ExecOpts{UseTTY: true}, "/bin/sh") 133 if err != nil { 134 t.Fatalf("docker exec failed: %v", err) 135 } 136 137 if _, err = p.Write(time.Second, []byte("sleep 100 | cat\n")); err != nil { 138 t.Fatalf("error exit: %v", err) 139 } 140 time.Sleep(time.Second) 141 142 if _, err = p.Write(time.Second, []byte{0x03}); err != nil { 143 t.Fatalf("error exit: %v", err) 144 } 145 146 if _, err = p.Write(time.Second, []byte("exit $(expr $? + 10)\n")); err != nil { 147 t.Fatalf("error exit: %v", err) 148 } 149 150 want := 140 151 got, err := p.WaitExitStatus(ctx) 152 if err != nil { 153 t.Fatalf("wait for exit failed with: %v", err) 154 } else if got != want { 155 t.Fatalf("wait for exit returned: %d want: %d", got, want) 156 } 157 } 158 159 // Test that failure to exec returns proper error message. 160 func TestExecError(t *testing.T) { 161 ctx := context.Background() 162 d := dockerutil.MakeContainer(ctx, t) 163 defer d.CleanUp(ctx) 164 165 // Start the container. 166 if err := d.Spawn(ctx, dockerutil.RunOpts{ 167 Image: "basic/alpine", 168 }, "sleep", "1000"); err != nil { 169 t.Fatalf("docker run failed: %v", err) 170 } 171 172 // Attempt to exec a binary that doesn't exist. 173 out, err := d.Exec(ctx, dockerutil.ExecOpts{}, "no_can_find") 174 if err == nil { 175 t.Fatalf("docker exec didn't fail") 176 } 177 if want := `error finding executable "no_can_find" in PATH`; !strings.Contains(out, want) { 178 t.Fatalf("docker exec wrong error, got: %s, want: .*%s.*", out, want) 179 } 180 } 181 182 // Test that exec inherits environment from run. 183 func TestExecEnv(t *testing.T) { 184 ctx := context.Background() 185 d := dockerutil.MakeContainer(ctx, t) 186 defer d.CleanUp(ctx) 187 188 // Start the container with env FOO=BAR. 189 if err := d.Spawn(ctx, dockerutil.RunOpts{ 190 Image: "basic/alpine", 191 Env: []string{"FOO=BAR"}, 192 }, "sleep", "1000"); err != nil { 193 t.Fatalf("docker run failed: %v", err) 194 } 195 196 // Exec "echo $FOO". 197 got, err := d.Exec(ctx, dockerutil.ExecOpts{}, "/bin/sh", "-c", "echo $FOO") 198 if err != nil { 199 t.Fatalf("docker exec failed: %v", err) 200 } 201 if got, want := strings.TrimSpace(got), "BAR"; got != want { 202 t.Errorf("bad output from 'docker exec'. Got %q; Want %q.", got, want) 203 } 204 } 205 206 // TestRunEnvHasHome tests that run always has HOME environment set. 207 func TestRunEnvHasHome(t *testing.T) { 208 // Base alpine image does not have any environment variables set. 209 ctx := context.Background() 210 d := dockerutil.MakeContainer(ctx, t) 211 defer d.CleanUp(ctx) 212 213 // Exec "echo $HOME". The 'bin' user's home dir is '/bin'. 214 got, err := d.Run(ctx, dockerutil.RunOpts{ 215 Image: "basic/alpine", 216 User: "bin", 217 }, "/bin/sh", "-c", "echo $HOME") 218 if err != nil { 219 t.Fatalf("docker run failed: %v", err) 220 } 221 222 // Check that the directory matches. 223 if got, want := strings.TrimSpace(got), "/bin"; got != want { 224 t.Errorf("bad output from 'docker run'. Got %q; Want %q.", got, want) 225 } 226 } 227 228 // Test that exec always has HOME environment set, even when not set in run. 229 func TestExecEnvHasHome(t *testing.T) { 230 // Base alpine image does not have any environment variables set. 231 ctx := context.Background() 232 d := dockerutil.MakeContainer(ctx, t) 233 defer d.CleanUp(ctx) 234 235 if err := d.Spawn(ctx, dockerutil.RunOpts{ 236 Image: "basic/alpine", 237 }, "sleep", "1000"); err != nil { 238 t.Fatalf("docker run failed: %v", err) 239 } 240 241 // Exec "echo $HOME", and expect to see "/root". 242 got, err := d.Exec(ctx, dockerutil.ExecOpts{}, "/bin/sh", "-c", "echo $HOME") 243 if err != nil { 244 t.Fatalf("docker exec failed: %v", err) 245 } 246 if want := "/root"; !strings.Contains(got, want) { 247 t.Errorf("wanted exec output to contain %q, got %q", want, got) 248 } 249 250 // Create a new user with a home directory. 251 newUID := 1234 252 newHome := "/foo/bar" 253 cmd := fmt.Sprintf("mkdir -p -m 777 %q && adduser foo -D -u %d -h %q", newHome, newUID, newHome) 254 if _, err := d.Exec(ctx, dockerutil.ExecOpts{}, "/bin/sh", "-c", cmd); err != nil { 255 t.Fatalf("docker exec failed: %v", err) 256 } 257 258 // Execute the same as the new user and expect newHome. 259 got, err = d.Exec(ctx, dockerutil.ExecOpts{ 260 User: strconv.Itoa(newUID), 261 }, "/bin/sh", "-c", "echo $HOME") 262 if err != nil { 263 t.Fatalf("docker exec failed: %v", err) 264 } 265 if want := newHome; !strings.Contains(got, want) { 266 t.Errorf("wanted exec output to contain %q, got %q", want, got) 267 } 268 }