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  }