diff options
Diffstat (limited to 'runsc/sandbox')
-rw-r--r-- | runsc/sandbox/BUILD | 1 | ||||
-rw-r--r-- | runsc/sandbox/chroot.go | 120 | ||||
-rw-r--r-- | runsc/sandbox/sandbox.go | 33 |
3 files changed, 150 insertions, 4 deletions
diff --git a/runsc/sandbox/BUILD b/runsc/sandbox/BUILD index 9317b1c14..8ebd14c4e 100644 --- a/runsc/sandbox/BUILD +++ b/runsc/sandbox/BUILD @@ -5,6 +5,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "sandbox", srcs = [ + "chroot.go", "network.go", "sandbox.go", ], diff --git a/runsc/sandbox/chroot.go b/runsc/sandbox/chroot.go new file mode 100644 index 000000000..a77a186c2 --- /dev/null +++ b/runsc/sandbox/chroot.go @@ -0,0 +1,120 @@ +// 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 sandbox + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "syscall" + + "gvisor.googlesource.com/gvisor/pkg/log" + "gvisor.googlesource.com/gvisor/runsc/boot" + "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) + + return specutils.Mount(src, chrootDst, typ, flags) +} + +// setUpChroot creates an empty directory with runsc mounted at /runsc, proc +// mounted at /proc, and any dev files needed for the platform. +func setUpChroot(platform boot.PlatformType) (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", 0); 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) + } + + // Mount dev files needed for platform. + var devMount string + switch platform { + case boot.PlatformKVM: + devMount = "/dev/kvm" + } + if devMount != "" { + if err := mountInChroot(chroot, devMount, devMount, "bind", syscall.MS_BIND); err != nil { + return "", fmt.Errorf("error mounting platform device in chroot: %v", err) + } + } + + return chroot, nil +} + +// tearDownChroot unmounts /proc and /runsc from the chroot before deleting the +// directory. +func tearDownChroot(chroot string) error { + // 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) + } + + // Unmount platform dev files. + devFiles := []string{"dev/kvm"} + for _, f := range devFiles { + devPath := filepath.Join(chroot, f) + if _, err := os.Stat(devPath); err != nil { + if os.IsNotExist(err) { + continue + } + return fmt.Errorf("Stat(%q) failed: %v", devPath, err) + } + if err := syscall.Unmount(devPath, 0); err != nil { + return fmt.Errorf("error unmounting %q: %v", devPath, 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 dd5a0aa56..f6264d5b2 100644 --- a/runsc/sandbox/sandbox.go +++ b/runsc/sandbox/sandbox.go @@ -51,6 +51,10 @@ type Sandbox struct { // Pid is the pid of the running sandbox (immutable). May be 0 is the sandbox // 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"` } // Create creates the sandbox process. @@ -392,12 +396,11 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund log.Infof("Sandbox will be started in new user namespace") nss = append(nss, specs.LinuxNamespace{Type: specs.UserNamespace}) - if conf.TestOnlyAllowRunAsCurrentUser { + // If we have CAP_SETUID and CAP_SETGID, then we can also run + // 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()) } else if specutils.CanSetUIDGID() { - // If we have CAP_SETUID and CAP_SETGID, then we can also run - // as user nobody. - // Map nobody in the new namespace to nobody in the parent namespace. const nobody = 65534 cmd.SysProcAttr.UidMappings = []syscall.SysProcIDMap{{ @@ -419,6 +422,23 @@ func (s *Sandbox) createSandboxProcess(spec *specs.Spec, conf *boot.Config, bund } 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.HasCapSysAdmin() { + log.Infof("Sandbox will be started in minimal chroot") + chroot, err := setUpChroot(conf.Platform) + if err != nil { + return fmt.Errorf("error setting up chroot: %v", err) + } + cmd.SysProcAttr.Chroot = chroot + 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") + } } // Log the fds we are donating to the sandbox process. @@ -525,6 +545,11 @@ func (s *Sandbox) Destroy() error { log.Debugf("Killing sandbox %q", s.ID) signalProcess(s.Pid, unix.SIGKILL) } + + if s.Chroot != "" { + return tearDownChroot(s.Chroot) + } + return nil } |