mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Fix sandbox chroot
Sandbox was setting chroot, but was not chaging the working dir. Added test to ensure this doesn't happen in the future. PiperOrigin-RevId: 215676270 Change-Id: I14352d3de64a4dcb90e50948119dc8328c9c15e1
This commit is contained in:
committed by
Shentubot
parent
beac59b37a
commit
3f46f2e501
+21
-2
@@ -35,6 +35,11 @@ bazel build //...
|
||||
runtime=runsc_test_$((RANDOM))
|
||||
sudo -n ./runsc/test/install.sh --runtime ${runtime}
|
||||
|
||||
# Best effort to uninstall the runtime
|
||||
uninstallRuntime() {
|
||||
sudo -n ./runsc/test/install.sh -u --runtime ${runtime}
|
||||
}
|
||||
|
||||
# Run the tests and upload results.
|
||||
#
|
||||
# We turn off "-e" flag because we must move the log files even if the test
|
||||
@@ -43,6 +48,7 @@ set +e
|
||||
bazel test --test_output=errors //...
|
||||
exit_code=${?}
|
||||
|
||||
# Execute local tests that require docker.
|
||||
if [[ ${exit_code} -eq 0 ]]; then
|
||||
# These names are used to exclude tests not supported in certain
|
||||
# configuration, e.g. save/restore not supported with hostnet.
|
||||
@@ -59,8 +65,21 @@ if [[ ${exit_code} -eq 0 ]]; then
|
||||
done
|
||||
fi
|
||||
|
||||
# Best effort to uninstall
|
||||
sudo -n ./runsc/test/install.sh -u --runtime ${runtime}
|
||||
# Execute local tests that require superuser.
|
||||
if [[ ${exit_code} -eq 0 ]]; then
|
||||
bazel build //runsc/test/root:root_test
|
||||
root_test=$(find -L ./bazel-bin/ -executable -type f -name root_test | grep __main__)
|
||||
if [[ ! -f "${root_test}" ]]; then
|
||||
uninstallRuntime
|
||||
echo "root_test executable not found"
|
||||
exit 1
|
||||
fi
|
||||
sudo -n -E RUNSC_RUNTIME=${runtime} ${root_test}
|
||||
exit_code=${?}
|
||||
fi
|
||||
|
||||
uninstallRuntime
|
||||
|
||||
set -e
|
||||
|
||||
# Find and rename all test xml and log files so that Sponge can pick them up.
|
||||
|
||||
@@ -55,7 +55,7 @@ func setUpChroot() (string, error) {
|
||||
log.Infof("Setting up sandbox chroot in %q", chroot)
|
||||
|
||||
// Mount /proc.
|
||||
if err := mountInChroot(chroot, "proc", "/proc", "proc", 0); err != nil {
|
||||
if err := mountInChroot(chroot, "proc", "/proc", "proc", syscall.MS_NOSUID|syscall.MS_NODEV|syscall.MS_NOEXEC); err != nil {
|
||||
return "", fmt.Errorf("error mounting proc in chroot: %v", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -475,6 +475,7 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund
|
||||
}
|
||||
s.Chroot = chroot // Remember path so it can cleaned up.
|
||||
cmd.SysProcAttr.Chroot = chroot
|
||||
cmd.Dir = "/"
|
||||
cmd.Args[0] = "/runsc"
|
||||
cmd.Path = "/runsc"
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# Tests
|
||||
|
||||
The tests defined under this path are verifying functionality beyond what unit
|
||||
tests can cover, e.g. integration and end to end tests. Due to their nature,
|
||||
they may need extra setup in the test machine and extra configuration to run.
|
||||
|
||||
- **integration:** defines integration tests that uses `docker run` to test
|
||||
functionality.
|
||||
- **image:** basic end to end test for popular images.
|
||||
- **root:** tests that require to be run as root.
|
||||
- **testutil:** utilities library to support the tests.
|
||||
|
||||
The following setup steps are required in order to run these tests:
|
||||
|
||||
|
||||
`./runsc/test/install.sh [--runtime <name>]`
|
||||
|
||||
The tests expect the runtime name to be provided in the `RUNSC_RUNTIME`
|
||||
environment variable (default: `runsc-test`). To run the tests execute:
|
||||
|
||||
|
||||
```
|
||||
bazel test --test_env=RUNSC_RUNTIME=runsc-test \
|
||||
//runsc/test/image:image_test \
|
||||
//runsc/test/integration:integration_test
|
||||
```
|
||||
@@ -12,17 +12,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package image provides end-to-end image tests for runsc. These tests require
|
||||
// docker and runsc to be installed on the machine. To set it up, run:
|
||||
//
|
||||
// ./runsc/test/install.sh [--runtime <name>]
|
||||
//
|
||||
// The tests expect the runtime name to be provided in the RUNSC_RUNTIME
|
||||
// environment variable (default: runsc-test).
|
||||
//
|
||||
// Package image provides end-to-end image tests for runsc.
|
||||
|
||||
// Each test calls docker commands to start up a container, and tests that it is
|
||||
// behaving properly, like connecting to a port or looking at the output. The
|
||||
// container is killed and deleted at the end.
|
||||
//
|
||||
// Setup instruction in runsc/test/README.md.
|
||||
package image
|
||||
|
||||
import (
|
||||
@@ -307,7 +303,7 @@ func TestRuby(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func MainTest(m *testing.M) {
|
||||
func TestMain(m *testing.M) {
|
||||
testutil.EnsureSupportedDockerVersion()
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
@@ -12,18 +12,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package image provides end-to-end integration tests for runsc. These tests require
|
||||
// docker and runsc to be installed on the machine. To set it up, run:
|
||||
//
|
||||
// ./runsc/test/install.sh [--runtime <name>]
|
||||
//
|
||||
// The tests expect the runtime name to be provided in the RUNSC_RUNTIME
|
||||
// environment variable (default: runsc-test).
|
||||
// Package integration provides end-to-end integration tests for runsc.
|
||||
//
|
||||
// Each test calls docker commands to start up a container, and tests that it is
|
||||
// behaving properly, with various runsc commands. The container is killed and deleted
|
||||
// at the end.
|
||||
|
||||
// behaving properly, with various runsc commands. The container is killed and
|
||||
// deleted at the end.
|
||||
//
|
||||
// Setup instruction in runsc/test/README.md.
|
||||
package integration
|
||||
|
||||
import (
|
||||
@@ -184,7 +179,7 @@ func TestConnectToSelf(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func MainTest(m *testing.M) {
|
||||
func TestMain(m *testing.M) {
|
||||
testutil.EnsureSupportedDockerVersion()
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package(licenses = ["notice"]) # Apache 2.0
|
||||
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "root",
|
||||
srcs = ["root.go"],
|
||||
importpath = "gvisor.googlesource.com/gvisor/runsc/test/root",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "root_test",
|
||||
size = "small",
|
||||
srcs = ["chroot_test.go"],
|
||||
embed = [":root"],
|
||||
tags = [
|
||||
# Requires docker and runsc to be configured before the test runs.
|
||||
# Also test only runs as root.
|
||||
"manual",
|
||||
"local",
|
||||
],
|
||||
deps = [
|
||||
"//runsc/specutils",
|
||||
"//runsc/test/testutil",
|
||||
"@com_github_syndtr_gocapability//capability:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright 2018 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package root is used for tests that requires sysadmin privileges run. First,
|
||||
// follow the setup instruction in runsc/test/README.md. To run these test:
|
||||
//
|
||||
// bazel build //runsc/test/root:root_test
|
||||
// root_test=$(find -L ./bazel-bin/ -executable -type f -name root_test | grep __main__)
|
||||
// sudo RUNSC_RUNTIME=runsc-test ${root_test}
|
||||
package root
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/syndtr/gocapability/capability"
|
||||
"gvisor.googlesource.com/gvisor/runsc/specutils"
|
||||
"gvisor.googlesource.com/gvisor/runsc/test/testutil"
|
||||
)
|
||||
|
||||
// TestChroot verifies that the sandbox is chroot'd and that mounts are cleaned
|
||||
// up after the sandbox is destroyed.
|
||||
func TestChroot(t *testing.T) {
|
||||
d := testutil.MakeDocker("chroot-test")
|
||||
if err := d.Run("alpine", "sleep", "10000"); err != nil {
|
||||
t.Fatalf("docker run failed: %v", err)
|
||||
}
|
||||
defer d.CleanUp()
|
||||
|
||||
pid, err := d.SandboxPid()
|
||||
if err != nil {
|
||||
t.Fatalf("Docker.SandboxPid(): %v", err)
|
||||
}
|
||||
|
||||
// Check that sandbox is chroot'ed.
|
||||
chroot, err := filepath.EvalSymlinks(filepath.Join("/proc", strconv.Itoa(pid), "root"))
|
||||
if err != nil {
|
||||
t.Fatalf("error resolving /proc/<pid>/root symlink: %v", err)
|
||||
}
|
||||
if want := "/tmp/runsc-sandbox-chroot-"; !strings.HasPrefix(chroot, want) {
|
||||
t.Errorf("sandbox is not chroot'd, it should be inside: %q, got: %q", want, chroot)
|
||||
}
|
||||
|
||||
path, err := filepath.EvalSymlinks(filepath.Join("/proc", strconv.Itoa(pid), "cwd"))
|
||||
if err != nil {
|
||||
t.Fatalf("error resolving /proc/<pid>/cwd symlink: %v", err)
|
||||
}
|
||||
if chroot != path {
|
||||
t.Errorf("sandbox current dir is wrong, want: %q, got: %q", chroot, path)
|
||||
}
|
||||
|
||||
fi, err := ioutil.ReadDir(chroot)
|
||||
if err != nil {
|
||||
t.Fatalf("error listing %q: %v", chroot, err)
|
||||
}
|
||||
if want, got := 2, len(fi); want != got {
|
||||
t.Fatalf("chroot dir got %d entries, want %d", want, got)
|
||||
}
|
||||
|
||||
// chroot dir is prepared by runsc and should contains only the executable
|
||||
// and /proc.
|
||||
files := []string{fi[0].Name(), fi[1].Name()}
|
||||
sort.Strings(files)
|
||||
if want := []string{"proc", "runsc"}; !reflect.DeepEqual(files, want) {
|
||||
t.Errorf("chroot got children %v, want %v", files, want)
|
||||
}
|
||||
|
||||
d.CleanUp()
|
||||
|
||||
// Check that chroot directory was cleaned up.
|
||||
if _, err := os.Stat(chroot); err == nil || !os.IsNotExist(err) {
|
||||
t.Errorf("chroot directory %q was not deleted: %v", chroot, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
testutil.EnsureSupportedDockerVersion()
|
||||
|
||||
if !specutils.HasCapabilities(capability.CAP_SYS_ADMIN, capability.CAP_DAC_OVERRIDE) {
|
||||
fmt.Println("Test requires sysadmin privileges to run. Try again with sudo.")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2018 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package root is empty. See chroot_test.go for description.
|
||||
package root
|
||||
@@ -267,6 +267,19 @@ func (d *Docker) FindPort(sandboxPort int) (int, error) {
|
||||
return port, nil
|
||||
}
|
||||
|
||||
// SandboxPid returns the PID to the sandbox process.
|
||||
func (d *Docker) SandboxPid() (int, error) {
|
||||
out, err := do("inspect", "-f={{.State.Pid}}", d.Name)
|
||||
if err != nil {
|
||||
return -1, fmt.Errorf("error retrieving pid: %v", err)
|
||||
}
|
||||
pid, err := strconv.Atoi(strings.TrimSuffix(string(out), "\n"))
|
||||
if err != nil {
|
||||
return -1, fmt.Errorf("error parsing pid %q: %v", out, err)
|
||||
}
|
||||
return pid, nil
|
||||
}
|
||||
|
||||
// WaitForOutput calls 'docker logs' to retrieve containers output and searches
|
||||
// for the given pattern.
|
||||
func (d *Docker) WaitForOutput(pattern string, timeout time.Duration) (string, error) {
|
||||
|
||||
@@ -238,7 +238,7 @@ func WaitForHTTP(port int, timeout time.Duration) error {
|
||||
}
|
||||
|
||||
// RunAsRoot ensures the test runs with CAP_SYS_ADMIN and CAP_SYS_CHROOT. If
|
||||
// need it will create a new user namespace and reexecute the test as root
|
||||
// needed it will create a new user namespace and re-execute the test as root
|
||||
// inside of the namespace. This functionr returns when it's running as root. If
|
||||
// it needs to create another process, it will exit from there and not return.
|
||||
func RunAsRoot() {
|
||||
@@ -246,6 +246,8 @@ func RunAsRoot() {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("*** Re-running test as root in new user namespace ***")
|
||||
|
||||
// Current process doesn't have CAP_SYS_ADMIN, create user namespace and run
|
||||
// as root inside that namespace to get it.
|
||||
runtime.LockOSThread()
|
||||
|
||||
Reference in New Issue
Block a user