diff options
author | Fabricio Voznika <fvoznika@google.com> | 2019-06-12 09:40:50 -0700 |
---|---|---|
committer | Shentubot <shentubot@google.com> | 2019-06-12 09:41:50 -0700 |
commit | 356d1be140bb51f2a50d2c7fe24242cbfeedc9d6 (patch) | |
tree | 3685e89ffdf701c2e9aebb19023cf0606ca8593b /runsc/specutils | |
parent | df110ad4fe571721a7eb4a5a1f9ce92584ef7809 (diff) |
Allow 'runsc do' to run without root
'--rootless' flag lets a non-root user execute 'runsc do'.
The drawback is that the sandbox and gofer processes will
run as root inside a user namespace that is mapped to the
caller's user, intead of nobody. And network is defaulted
to '--network=host' inside the root network namespace. On
the bright side, it's very convenient for testing:
runsc --rootless do ls
runsc --rootless do curl www.google.com
PiperOrigin-RevId: 252840970
Diffstat (limited to 'runsc/specutils')
-rw-r--r-- | runsc/specutils/BUILD | 5 | ||||
-rw-r--r-- | runsc/specutils/namespace.go | 52 |
2 files changed, 53 insertions, 4 deletions
diff --git a/runsc/specutils/BUILD b/runsc/specutils/BUILD index 15476de6f..0456e4c4f 100644 --- a/runsc/specutils/BUILD +++ b/runsc/specutils/BUILD @@ -10,10 +10,7 @@ go_library( "specutils.go", ], importpath = "gvisor.googlesource.com/gvisor/runsc/specutils", - visibility = [ - "//runsc:__subpackages__", - "//test:__subpackages__", - ], + visibility = ["//:sandbox"], deps = [ "//pkg/abi/linux", "//pkg/log", diff --git a/runsc/specutils/namespace.go b/runsc/specutils/namespace.go index 7d194335c..06c13d1ab 100644 --- a/runsc/specutils/namespace.go +++ b/runsc/specutils/namespace.go @@ -220,3 +220,55 @@ func HasCapabilities(cs ...capability.Cap) bool { } return true } + +// MaybeRunAsRoot ensures the process runs with capabilities needed to create a +// sandbox, e.g. CAP_SYS_ADMIN, CAP_SYS_CHROOT, etc. If capabilities are needed, +// it will create a new user namespace and re-execute the process as root +// inside the namespace with the same arguments and environment. +// +// This function returns immediately when no new capability is needed. If +// another process is executed, it returns straight from here with the same exit +// code as the child. +func MaybeRunAsRoot() error { + if HasCapabilities(capability.CAP_SYS_ADMIN, capability.CAP_SYS_CHROOT, capability.CAP_SETUID, capability.CAP_SETGID) { + return nil + } + + // Current process doesn't have required capabilities, create user namespace + // and run as root inside the namespace to acquire capabilities. + log.Infof("*** Re-running as root in new user namespace ***") + + cmd := exec.Command("/proc/self/exe", os.Args[1:]...) + + cmd.SysProcAttr = &syscall.SysProcAttr{ + Cloneflags: syscall.CLONE_NEWUSER | syscall.CLONE_NEWNS, + // Set current user/group as root inside the namespace. Since we may not + // have CAP_SETUID/CAP_SETGID, just map root to the current user/group. + UidMappings: []syscall.SysProcIDMap{ + {ContainerID: 0, HostID: os.Getuid(), Size: 1}, + }, + GidMappings: []syscall.SysProcIDMap{ + {ContainerID: 0, HostID: os.Getgid(), Size: 1}, + }, + Credential: &syscall.Credential{Uid: 0, Gid: 0}, + GidMappingsEnableSetgroups: false, + } + + 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()) + } + log.Warningf("No wait status provided, exiting with -1: %v", err) + os.Exit(-1) + } + return fmt.Errorf("re-executing self: %v", err) + } + // Child completed with success. + os.Exit(0) + panic("unreachable") +} |