diff options
Diffstat (limited to 'runsc/sandbox')
-rw-r--r-- | runsc/sandbox/BUILD | 1 | ||||
-rw-r--r-- | runsc/sandbox/chroot.go | 97 | ||||
-rw-r--r-- | runsc/sandbox/sandbox.go | 86 |
3 files changed, 45 insertions, 139 deletions
diff --git a/runsc/sandbox/BUILD b/runsc/sandbox/BUILD index d6043bcf7..899fd99de 100644 --- a/runsc/sandbox/BUILD +++ b/runsc/sandbox/BUILD @@ -5,7 +5,6 @@ package(licenses = ["notice"]) # Apache 2.0 go_library( name = "sandbox", srcs = [ - "chroot.go", "network.go", "sandbox.go", ], diff --git a/runsc/sandbox/chroot.go b/runsc/sandbox/chroot.go deleted file mode 100644 index 354049871..000000000 --- a/runsc/sandbox/chroot.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2018 Google LLC -// -// 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 sandbox - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "syscall" - - "gvisor.googlesource.com/gvisor/pkg/log" - "gvisor.googlesource.com/gvisor/runsc/specutils" -) - -// chrootBinPath is the location inside the chroot where the runsc binary will -// be mounted. -const chrootBinPath = "/runsc" - -// mountInChroot creates the destination mount point in the given chroot and -// mounts the source. -func mountInChroot(chroot, src, dst, typ string, flags uint32) error { - chrootDst := filepath.Join(chroot, dst) - log.Infof("Mounting %q at %q", src, chrootDst) - - if err := specutils.Mount(src, chrootDst, typ, flags); err != nil { - return fmt.Errorf("error mounting %q at %q: %v", src, chrootDst, err) - } - return nil -} - -// setUpChroot creates an empty directory with runsc mounted at /runsc and proc -// mounted at /proc. -func setUpChroot() (string, error) { - // Create the chroot directory and make it accessible to all users. - chroot, err := ioutil.TempDir("", "runsc-sandbox-chroot-") - if err != nil { - return "", fmt.Errorf("TempDir() failed: %v", err) - } - if err := os.Chmod(chroot, 0777); err != nil { - return "", fmt.Errorf("Chmod(%q) failed: %v", chroot, err) - } - log.Infof("Setting up sandbox chroot in %q", chroot) - - // Mount /proc. - 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) - } - - // Mount runsc at /runsc in the chroot. - binPath, err := specutils.BinPath() - if err != nil { - return "", err - } - if err := mountInChroot(chroot, binPath, chrootBinPath, "bind", syscall.MS_BIND|syscall.MS_RDONLY); err != nil { - return "", fmt.Errorf("error mounting runsc in chroot: %v", err) - } - - return chroot, nil -} - -// tearDownChroot unmounts /proc and /runsc from the chroot before deleting the -// directory. -func tearDownChroot(chroot string) error { - log.Debugf("Removing chroot mounts %q", chroot) - - // Unmount /proc. - proc := filepath.Join(chroot, "proc") - if err := syscall.Unmount(proc, 0); err != nil { - return fmt.Errorf("error unmounting %q: %v", proc, err) - } - - // Unmount /runsc. - exe := filepath.Join(chroot, chrootBinPath) - if err := syscall.Unmount(exe, 0); err != nil { - return fmt.Errorf("error unmounting %q: %v", exe, err) - } - - // Remove chroot directory. - if err := os.RemoveAll(chroot); err != nil { - return fmt.Errorf("error removing %q: %v", chroot, err) - } - - return nil -} diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go index fe55ddab8..411200793 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -56,10 +56,6 @@ type Sandbox struct { // is not running. Pid int `json:"pid"` - // Chroot is the path to the chroot directory that the sandbox process - // is running in. - Chroot string `json:"chroot"` - // Cgroup has the cgroup configuration for the sandbox. Cgroup *cgroup.Cgroup `json:"cgroup"` @@ -491,6 +487,17 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund // rules. cmd.Args = append(cmd.Args, "--apply-caps=true") + // If we have CAP_SYS_ADMIN, we can create an empty chroot and + // bind-mount the executable inside it. + if conf.TestOnlyAllowRunAsCurrentUserWithoutChroot { + log.Warningf("Running sandbox in test mode without chroot. This is only safe in tests!") + + } else if specutils.HasCapabilities(capability.CAP_SYS_ADMIN) { + log.Infof("Sandbox will be started in minimal chroot") + cmd.Args = append(cmd.Args, "--setup-root") + } else { + return fmt.Errorf("can't run sandbox process in minimal chroot since we don't have CAP_SYS_ADMIN") + } } else { log.Infof("Sandbox will be started in new user namespace") nss = append(nss, specs.LinuxNamespace{Type: specs.UserNamespace}) @@ -499,50 +506,53 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund // as user nobody. if conf.TestOnlyAllowRunAsCurrentUserWithoutChroot { log.Warningf("Running sandbox in test mode as current user (uid=%d gid=%d). This is only safe in tests!", os.Getuid(), os.Getgid()) + log.Warningf("Running sandbox in test mode without chroot. This is only safe in tests!") } else if specutils.HasCapabilities(capability.CAP_SETUID, capability.CAP_SETGID) { // Map nobody in the new namespace to nobody in the parent namespace. + // + // A sandbox process will construct an empty + // root for itself, so it has to have the CAP_SYS_ADMIN + // capability. + // + // FIXME: The current implementations of + // os/exec doesn't allow to set ambient capabilities if + // a process is started in a new user namespace. As a + // workaround, we start the sandbox process with the 0 + // UID and then it constructs a chroot and sets UID to + // nobody. https://github.com/golang/go/issues/2315 const nobody = 65534 - cmd.SysProcAttr.UidMappings = []syscall.SysProcIDMap{{ - ContainerID: int(nobody), - HostID: int(nobody), - Size: int(1), - }} - cmd.SysProcAttr.GidMappings = []syscall.SysProcIDMap{{ - ContainerID: int(nobody), - HostID: int(nobody), - Size: int(1), - }} + cmd.SysProcAttr.UidMappings = []syscall.SysProcIDMap{ + { + ContainerID: int(0), + HostID: int(nobody - 1), + Size: int(1), + }, + { + ContainerID: int(nobody), + HostID: int(nobody), + Size: int(1), + }, + } + cmd.SysProcAttr.GidMappings = []syscall.SysProcIDMap{ + { + ContainerID: int(nobody), + HostID: int(nobody), + Size: int(1), + }, + } // Set credentials to run as user and group nobody. cmd.SysProcAttr.Credential = &syscall.Credential{ - Uid: nobody, + Uid: 0, Gid: nobody, } + cmd.Args = append(cmd.Args, "--setup-root") } else { return fmt.Errorf("can't run sandbox process as user nobody since we don't have CAP_SETUID or CAP_SETGID") } } - // If we have CAP_SYS_ADMIN, we can create an empty chroot and - // bind-mount the executable inside it. - if conf.TestOnlyAllowRunAsCurrentUserWithoutChroot { - log.Warningf("Running sandbox in test mode without chroot. This is only safe in tests!") - - } else if specutils.HasCapabilities(capability.CAP_SYS_ADMIN, capability.CAP_SYS_CHROOT) { - log.Infof("Sandbox will be started in minimal chroot") - chroot, err := setUpChroot() - if err != nil { - return fmt.Errorf("error setting up chroot: %v", err) - } - s.Chroot = chroot // Remember path so it can cleaned up. - cmd.SysProcAttr.Chroot = chroot - cmd.Dir = "/" - cmd.Args[0] = "/runsc" - cmd.Path = "/runsc" - - } else { - return fmt.Errorf("can't run sandbox process in minimal chroot since we don't have CAP_SYS_ADMIN and CAP_SYS_CHROOT") - } + cmd.Args[0] = "runsc-sandbox" if s.Cgroup != nil { cpuNum, err := s.Cgroup.NumCPU() @@ -676,12 +686,6 @@ func (s *Sandbox) destroy() error { } } - if s.Chroot != "" { - if err := tearDownChroot(s.Chroot); err != nil { - return err - } - } - return nil } |