From db81c0b02f2f947ae837a3e16471a148a66436eb Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Mon, 27 Aug 2018 11:09:06 -0700 Subject: Put fsgofer inside chroot Now each container gets its own dedicated gofer that is chroot'd to the rootfs path. This is done to add an extra layer of security in case the gofer gets compromised. PiperOrigin-RevId: 210396476 Change-Id: Iba21360a59dfe90875d61000db103f8609157ca0 --- runsc/test/testutil/BUILD | 1 + runsc/test/testutil/docker.go | 6 ++--- runsc/test/testutil/testutil.go | 57 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 3 deletions(-) (limited to 'runsc/test') diff --git a/runsc/test/testutil/BUILD b/runsc/test/testutil/BUILD index 03ab3c4ac..ca91e07ff 100644 --- a/runsc/test/testutil/BUILD +++ b/runsc/test/testutil/BUILD @@ -18,5 +18,6 @@ go_library( "//runsc/specutils", "@com_github_cenkalti_backoff//:go_default_library", "@com_github_opencontainers_runtime-spec//specs-go:go_default_library", + "@com_github_syndtr_gocapability//capability:go_default_library", ], ) diff --git a/runsc/test/testutil/docker.go b/runsc/test/testutil/docker.go index b7d60e712..fc67c174a 100644 --- a/runsc/test/testutil/docker.go +++ b/runsc/test/testutil/docker.go @@ -32,7 +32,7 @@ func init() { rand.Seed(time.Now().UnixNano()) } -func runtime() string { +func getRuntime() string { r := os.Getenv("RUNSC_RUNTIME") if r == "" { return "runsc-test" @@ -43,7 +43,7 @@ func runtime() string { // IsPauseResumeSupported returns true if Pause/Resume is supported by runtime. func IsPauseResumeSupported() bool { // Native host network stack can't be saved. - return !strings.Contains(runtime(), "hostnet") + return !strings.Contains(getRuntime(), "hostnet") } // EnsureSupportedDockerVersion checks if correct docker is installed. @@ -128,7 +128,7 @@ type Docker struct { // Names of containers will be unique. func MakeDocker(namePrefix string) Docker { suffix := fmt.Sprintf("-%06d", rand.Int())[:7] - return Docker{Name: namePrefix + suffix, Runtime: runtime()} + return Docker{Name: namePrefix + suffix, Runtime: getRuntime()} } // Create calls 'docker create' with the arguments provided. diff --git a/runsc/test/testutil/testutil.go b/runsc/test/testutil/testutil.go index fc3d61e52..e90ab5ad5 100644 --- a/runsc/test/testutil/testutil.go +++ b/runsc/test/testutil/testutil.go @@ -23,11 +23,16 @@ import ( "io/ioutil" "net/http" "os" + "os/exec" "path/filepath" + "runtime" + "syscall" + "testing" "time" "github.com/cenkalti/backoff" specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/syndtr/gocapability/capability" "gvisor.googlesource.com/gvisor/runsc/boot" "gvisor.googlesource.com/gvisor/runsc/specutils" ) @@ -227,3 +232,55 @@ func WaitForHTTP(port int, timeout time.Duration) error { } return Poll(cb, timeout) } + +// RunAsRoot ensures the test runs with CAP_SYS_ADMIN. If need it will create +// a new user namespace and reexecute the test as root inside of the namespace. +func RunAsRoot(m *testing.M) { + caps, err := capability.NewPid2(os.Getpid()) + if err != nil { + panic(err.Error()) + } + if err := caps.Load(); err != nil { + panic(err.Error()) + } + if caps.Get(capability.EFFECTIVE, capability.CAP_SYS_ADMIN) { + // Capability: check! Good to run. + os.Exit(m.Run()) + } + + // Current process doesn't have CAP_SYS_ADMIN, create user namespace and run + // as root inside that namespace to get it. + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + cmd := exec.Command("/proc/self/exe", os.Args...) + cmd.SysProcAttr = &syscall.SysProcAttr{ + Cloneflags: syscall.CLONE_NEWUSER | syscall.CLONE_NEWNS, + // Set current user/group as root inside the namespace. + UidMappings: []syscall.SysProcIDMap{ + {ContainerID: 0, HostID: os.Getuid(), Size: 1}, + }, + GidMappings: []syscall.SysProcIDMap{ + {ContainerID: 0, HostID: os.Getgid(), Size: 1}, + }, + GidMappingsEnableSetgroups: false, + Credential: &syscall.Credential{ + Uid: 0, + Gid: 0, + }, + } + cmd.Env = os.Environ() + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + if exit, ok := err.(*exec.ExitError); ok { + if ws, ok := exit.Sys().(syscall.WaitStatus); ok { + os.Exit(ws.ExitStatus()) + } + os.Exit(-1) + } + panic(fmt.Sprint("error running child process:", err.Error())) + } + os.Exit(0) +} -- cgit v1.2.3