summaryrefslogtreecommitdiff
path: root/vendor/github.com/projectatomic/buildah/chroot
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/projectatomic/buildah/chroot')
-rw-r--r--vendor/github.com/projectatomic/buildah/chroot/run.go1308
-rw-r--r--vendor/github.com/projectatomic/buildah/chroot/seccomp.go142
-rw-r--r--vendor/github.com/projectatomic/buildah/chroot/seccomp_unsupported.go15
-rw-r--r--vendor/github.com/projectatomic/buildah/chroot/selinux.go22
-rw-r--r--vendor/github.com/projectatomic/buildah/chroot/selinux_unsupported.go18
-rw-r--r--vendor/github.com/projectatomic/buildah/chroot/unsupported.go15
-rw-r--r--vendor/github.com/projectatomic/buildah/chroot/util.go15
7 files changed, 0 insertions, 1535 deletions
diff --git a/vendor/github.com/projectatomic/buildah/chroot/run.go b/vendor/github.com/projectatomic/buildah/chroot/run.go
deleted file mode 100644
index c8aec181a..000000000
--- a/vendor/github.com/projectatomic/buildah/chroot/run.go
+++ /dev/null
@@ -1,1308 +0,0 @@
-// +build linux
-
-package chroot
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "io"
- "os"
- "os/exec"
- "path/filepath"
- "runtime"
- "strconv"
- "strings"
- "sync"
- "syscall"
- "unsafe"
-
- "github.com/containers/storage/pkg/ioutils"
- "github.com/containers/storage/pkg/mount"
- "github.com/containers/storage/pkg/reexec"
- "github.com/opencontainers/runc/libcontainer/apparmor"
- "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/pkg/errors"
- "github.com/projectatomic/buildah/bind"
- "github.com/projectatomic/buildah/unshare"
- "github.com/projectatomic/buildah/util"
- "github.com/sirupsen/logrus"
- "github.com/syndtr/gocapability/capability"
- "golang.org/x/crypto/ssh/terminal"
- "golang.org/x/sys/unix"
-)
-
-const (
- // runUsingChrootCommand is a command we use as a key for reexec
- runUsingChrootCommand = "buildah-chroot-runtime"
- // runUsingChrootExec is a command we use as a key for reexec
- runUsingChrootExecCommand = "buildah-chroot-exec"
-)
-
-var (
- rlimitsMap = map[string]int{
- "RLIMIT_AS": unix.RLIMIT_AS,
- "RLIMIT_CORE": unix.RLIMIT_CORE,
- "RLIMIT_CPU": unix.RLIMIT_CPU,
- "RLIMIT_DATA": unix.RLIMIT_DATA,
- "RLIMIT_FSIZE": unix.RLIMIT_FSIZE,
- "RLIMIT_LOCKS": unix.RLIMIT_LOCKS,
- "RLIMIT_MEMLOCK": unix.RLIMIT_MEMLOCK,
- "RLIMIT_MSGQUEUE": unix.RLIMIT_MSGQUEUE,
- "RLIMIT_NICE": unix.RLIMIT_NICE,
- "RLIMIT_NOFILE": unix.RLIMIT_NOFILE,
- "RLIMIT_NPROC": unix.RLIMIT_NPROC,
- "RLIMIT_RSS": unix.RLIMIT_RSS,
- "RLIMIT_RTPRIO": unix.RLIMIT_RTPRIO,
- "RLIMIT_RTTIME": unix.RLIMIT_RTTIME,
- "RLIMIT_SIGPENDING": unix.RLIMIT_SIGPENDING,
- "RLIMIT_STACK": unix.RLIMIT_STACK,
- }
- rlimitsReverseMap = map[int]string{}
-)
-
-func init() {
- reexec.Register(runUsingChrootCommand, runUsingChrootMain)
- reexec.Register(runUsingChrootExecCommand, runUsingChrootExecMain)
- for limitName, limitNumber := range rlimitsMap {
- rlimitsReverseMap[limitNumber] = limitName
- }
-}
-
-type runUsingChrootSubprocOptions struct {
- Spec *specs.Spec
- BundlePath string
- UIDMappings []syscall.SysProcIDMap
- GIDMappings []syscall.SysProcIDMap
-}
-
-type runUsingChrootExecSubprocOptions struct {
- Spec *specs.Spec
- BundlePath string
-}
-
-// RunUsingChroot runs a chrooted process, using some of the settings from the
-// passed-in spec, and using the specified bundlePath to hold temporary files,
-// directories, and mountpoints.
-func RunUsingChroot(spec *specs.Spec, bundlePath string, stdin io.Reader, stdout, stderr io.Writer) (err error) {
- var confwg sync.WaitGroup
-
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
-
- // Write the runtime configuration, mainly for debugging.
- specbytes, err := json.Marshal(spec)
- if err != nil {
- return err
- }
- if err = ioutils.AtomicWriteFile(filepath.Join(bundlePath, "config.json"), specbytes, 0600); err != nil {
- return errors.Wrapf(err, "error storing runtime configuration")
- }
- logrus.Debugf("config = %v", string(specbytes))
-
- // Run the grandparent subprocess in a user namespace that reuses the mappings that we have.
- uidmap, gidmap, err := util.GetHostIDMappings("")
- if err != nil {
- return err
- }
- for i := range uidmap {
- uidmap[i].HostID = uidmap[i].ContainerID
- }
- for i := range gidmap {
- gidmap[i].HostID = gidmap[i].ContainerID
- }
-
- // Default to using stdin/stdout/stderr if we weren't passed objects to use.
- if stdin == nil {
- stdin = os.Stdin
- }
- if stdout == nil {
- stdout = os.Stdout
- }
- if stderr == nil {
- stderr = os.Stderr
- }
-
- // Create a pipe for passing configuration down to the next process.
- preader, pwriter, err := os.Pipe()
- if err != nil {
- return errors.Wrapf(err, "error creating configuration pipe")
- }
- config, conferr := json.Marshal(runUsingChrootSubprocOptions{
- Spec: spec,
- BundlePath: bundlePath,
- })
- if conferr != nil {
- return errors.Wrapf(conferr, "error encoding configuration for %q", runUsingChrootCommand)
- }
-
- // Set our terminal's mode to raw, to pass handling of special
- // terminal input to the terminal in the container.
- if spec.Process.Terminal && terminal.IsTerminal(unix.Stdin) {
- state, err := terminal.MakeRaw(unix.Stdin)
- if err != nil {
- logrus.Warnf("error setting terminal state: %v", err)
- } else {
- defer func() {
- if err = terminal.Restore(unix.Stdin, state); err != nil {
- logrus.Errorf("unable to restore terminal state: %v", err)
- }
- }()
- }
- }
-
- // Raise any resource limits that are higher than they are now, before
- // we drop any more privileges.
- if err = setRlimits(spec, false, true); err != nil {
- return err
- }
-
- // Start the grandparent subprocess.
- cmd := unshare.Command(runUsingChrootCommand)
- cmd.Stdin, cmd.Stdout, cmd.Stderr = stdin, stdout, stderr
- cmd.Dir = "/"
- cmd.Env = append([]string{fmt.Sprintf("LOGLEVEL=%d", logrus.GetLevel())}, os.Environ()...)
- cmd.UnshareFlags = syscall.CLONE_NEWUSER
- cmd.UidMappings = uidmap
- cmd.GidMappings = gidmap
- cmd.GidMappingsEnableSetgroups = true
-
- logrus.Debugf("Running %#v in %#v", cmd.Cmd, cmd)
- confwg.Add(1)
- go func() {
- _, conferr = io.Copy(pwriter, bytes.NewReader(config))
- pwriter.Close()
- confwg.Done()
- }()
- cmd.ExtraFiles = append([]*os.File{preader}, cmd.ExtraFiles...)
- err = cmd.Run()
- confwg.Wait()
- if err == nil {
- return conferr
- }
- return err
-}
-
-// main() for grandparent subprocess. Its main job is to shuttle stdio back
-// and forth, managing a pseudo-terminal if we want one, for our child, the
-// parent subprocess.
-func runUsingChrootMain() {
- var options runUsingChrootSubprocOptions
-
- runtime.LockOSThread()
-
- // Set logging.
- if level := os.Getenv("LOGLEVEL"); level != "" {
- if ll, err := strconv.Atoi(level); err == nil {
- logrus.SetLevel(logrus.Level(ll))
- }
- os.Unsetenv("LOGLEVEL")
- }
-
- // Unpack our configuration.
- confPipe := os.NewFile(3, "confpipe")
- if confPipe == nil {
- fmt.Fprintf(os.Stderr, "error reading options pipe\n")
- os.Exit(1)
- }
- defer confPipe.Close()
- if err := json.NewDecoder(confPipe).Decode(&options); err != nil {
- fmt.Fprintf(os.Stderr, "error decoding options: %v\n", err)
- os.Exit(1)
- }
-
- // Prepare to shuttle stdio back and forth.
- rootUid32, rootGid32, err := util.GetHostRootIDs(options.Spec)
- if err != nil {
- logrus.Errorf("error determining ownership for container stdio")
- os.Exit(1)
- }
- rootUid := int(rootUid32)
- rootGid := int(rootGid32)
- relays := make(map[int]int)
- closeOnceRunning := []*os.File{}
- var ctty *os.File
- var stdin io.Reader
- var stdinCopy io.WriteCloser
- var stdout io.Writer
- var stderr io.Writer
- fdDesc := make(map[int]string)
- deferred := func() {}
- if options.Spec.Process.Terminal {
- // Create a pseudo-terminal -- open a copy of the master side.
- ptyMasterFd, err := unix.Open("/dev/ptmx", os.O_RDWR, 0600)
- if err != nil {
- logrus.Errorf("error opening PTY master using /dev/ptmx: %v", err)
- os.Exit(1)
- }
- // Set the kernel's lock to "unlocked".
- locked := 0
- if result, _, err := unix.Syscall(unix.SYS_IOCTL, uintptr(ptyMasterFd), unix.TIOCSPTLCK, uintptr(unsafe.Pointer(&locked))); int(result) == -1 {
- logrus.Errorf("error locking PTY descriptor: %v", err)
- os.Exit(1)
- }
- // Get a handle for the other end.
- ptyFd, _, err := unix.Syscall(unix.SYS_IOCTL, uintptr(ptyMasterFd), unix.TIOCGPTPEER, unix.O_RDWR|unix.O_NOCTTY)
- if int(ptyFd) == -1 {
- if errno, isErrno := err.(syscall.Errno); !isErrno || (errno != syscall.EINVAL && errno != syscall.ENOTTY) {
- logrus.Errorf("error getting PTY descriptor: %v", err)
- os.Exit(1)
- }
- // EINVAL means the kernel's too old to understand TIOCGPTPEER. Try TIOCGPTN.
- ptyN, err := unix.IoctlGetInt(ptyMasterFd, unix.TIOCGPTN)
- if err != nil {
- logrus.Errorf("error getting PTY number: %v", err)
- os.Exit(1)
- }
- ptyName := fmt.Sprintf("/dev/pts/%d", ptyN)
- fd, err := unix.Open(ptyName, unix.O_RDWR|unix.O_NOCTTY, 0620)
- if err != nil {
- logrus.Errorf("error opening PTY %q: %v", ptyName, err)
- os.Exit(1)
- }
- ptyFd = uintptr(fd)
- }
- // Make notes about what's going where.
- relays[ptyMasterFd] = unix.Stdout
- relays[unix.Stdin] = ptyMasterFd
- fdDesc[ptyMasterFd] = "container terminal"
- fdDesc[unix.Stdin] = "stdin"
- fdDesc[unix.Stdout] = "stdout"
- winsize := &unix.Winsize{}
- // Set the pseudoterminal's size to the configured size, or our own.
- if options.Spec.Process.ConsoleSize != nil {
- // Use configured sizes.
- winsize.Row = uint16(options.Spec.Process.ConsoleSize.Height)
- winsize.Col = uint16(options.Spec.Process.ConsoleSize.Width)
- } else {
- if terminal.IsTerminal(unix.Stdin) {
- // Use the size of our terminal.
- winsize, err = unix.IoctlGetWinsize(unix.Stdin, unix.TIOCGWINSZ)
- if err != nil {
- logrus.Debugf("error reading current terminal's size")
- winsize.Row = 0
- winsize.Col = 0
- }
- }
- }
- if winsize.Row != 0 && winsize.Col != 0 {
- if err = unix.IoctlSetWinsize(int(ptyFd), unix.TIOCSWINSZ, winsize); err != nil {
- logrus.Warnf("error setting terminal size for pty")
- }
- // FIXME - if we're connected to a terminal, we should
- // be passing the updated terminal size down when we
- // receive a SIGWINCH.
- }
- // Open an *os.File object that we can pass to our child.
- ctty = os.NewFile(ptyFd, "/dev/tty")
- // Set ownership for the PTY.
- if err = ctty.Chown(rootUid, rootGid); err != nil {
- var cttyInfo unix.Stat_t
- err2 := unix.Fstat(int(ptyFd), &cttyInfo)
- from := ""
- op := "setting"
- if err2 == nil {
- op = "changing"
- from = fmt.Sprintf("from %d/%d ", cttyInfo.Uid, cttyInfo.Gid)
- }
- logrus.Warnf("error %s ownership of container PTY %sto %d/%d: %v", op, from, rootUid, rootGid, err)
- }
- // Set permissions on the PTY.
- if err = ctty.Chmod(0620); err != nil {
- logrus.Errorf("error setting permissions of container PTY: %v", err)
- os.Exit(1)
- }
- // Make a note that our child (the parent subprocess) should
- // have the PTY connected to its stdio, and that we should
- // close it once it's running.
- stdin = ctty
- stdout = ctty
- stderr = ctty
- closeOnceRunning = append(closeOnceRunning, ctty)
- } else {
- // Create pipes for stdio.
- stdinRead, stdinWrite, err := os.Pipe()
- if err != nil {
- logrus.Errorf("error opening pipe for stdin: %v", err)
- }
- stdoutRead, stdoutWrite, err := os.Pipe()
- if err != nil {
- logrus.Errorf("error opening pipe for stdout: %v", err)
- }
- stderrRead, stderrWrite, err := os.Pipe()
- if err != nil {
- logrus.Errorf("error opening pipe for stderr: %v", err)
- }
- // Make notes about what's going where.
- relays[unix.Stdin] = int(stdinWrite.Fd())
- relays[int(stdoutRead.Fd())] = unix.Stdout
- relays[int(stderrRead.Fd())] = unix.Stderr
- fdDesc[int(stdinWrite.Fd())] = "container stdin pipe"
- fdDesc[int(stdoutRead.Fd())] = "container stdout pipe"
- fdDesc[int(stderrRead.Fd())] = "container stderr pipe"
- fdDesc[unix.Stdin] = "stdin"
- fdDesc[unix.Stdout] = "stdout"
- fdDesc[unix.Stderr] = "stderr"
- // Set ownership for the pipes.
- if err = stdinRead.Chown(rootUid, rootGid); err != nil {
- logrus.Errorf("error setting ownership of container stdin pipe: %v", err)
- os.Exit(1)
- }
- if err = stdoutWrite.Chown(rootUid, rootGid); err != nil {
- logrus.Errorf("error setting ownership of container stdout pipe: %v", err)
- os.Exit(1)
- }
- if err = stderrWrite.Chown(rootUid, rootGid); err != nil {
- logrus.Errorf("error setting ownership of container stderr pipe: %v", err)
- os.Exit(1)
- }
- // Make a note that our child (the parent subprocess) should
- // have the pipes connected to its stdio, and that we should
- // close its ends of them once it's running.
- stdin = stdinRead
- stdout = stdoutWrite
- stderr = stderrWrite
- closeOnceRunning = append(closeOnceRunning, stdinRead, stdoutWrite, stderrWrite)
- stdinCopy = stdinWrite
- defer stdoutRead.Close()
- defer stderrRead.Close()
- }
- // A helper that returns false if err is an error that would cause us
- // to give up.
- logIfNotRetryable := func(err error, what string) (retry bool) {
- if err == nil {
- return true
- }
- if errno, isErrno := err.(syscall.Errno); isErrno {
- switch errno {
- case syscall.EINTR, syscall.EAGAIN:
- return true
- }
- }
- logrus.Error(what)
- return false
- }
- for readFd, writeFd := range relays {
- if err := unix.SetNonblock(readFd, true); err != nil {
- logrus.Errorf("error setting descriptor %d (%s) non-blocking: %v", readFd, fdDesc[readFd], err)
- return
- }
- if err := unix.SetNonblock(writeFd, false); err != nil {
- logrus.Errorf("error setting descriptor %d (%s) blocking: %v", relays[writeFd], fdDesc[writeFd], err)
- return
- }
- }
- go func() {
- buffers := make(map[int]*bytes.Buffer)
- for _, writeFd := range relays {
- buffers[writeFd] = new(bytes.Buffer)
- }
- pollTimeout := -1
- for len(relays) > 0 {
- fds := make([]unix.PollFd, 0, len(relays))
- for fd := range relays {
- fds = append(fds, unix.PollFd{Fd: int32(fd), Events: unix.POLLIN | unix.POLLHUP})
- }
- _, err := unix.Poll(fds, pollTimeout)
- if !logIfNotRetryable(err, fmt.Sprintf("poll: %v", err)) {
- return
- }
- removeFds := make(map[int]struct{})
- for _, rfd := range fds {
- if rfd.Revents&unix.POLLHUP == unix.POLLHUP {
- removeFds[int(rfd.Fd)] = struct{}{}
- }
- if rfd.Revents&unix.POLLNVAL == unix.POLLNVAL {
- logrus.Debugf("error polling descriptor %s: closed?", fdDesc[int(rfd.Fd)])
- removeFds[int(rfd.Fd)] = struct{}{}
- }
- if rfd.Revents&unix.POLLIN == 0 {
- continue
- }
- b := make([]byte, 8192)
- nread, err := unix.Read(int(rfd.Fd), b)
- logIfNotRetryable(err, fmt.Sprintf("read %s: %v", fdDesc[int(rfd.Fd)], err))
- if nread > 0 {
- if wfd, ok := relays[int(rfd.Fd)]; ok {
- nwritten, err := buffers[wfd].Write(b[:nread])
- if err != nil {
- logrus.Debugf("buffer: %v", err)
- continue
- }
- if nwritten != nread {
- logrus.Debugf("buffer: expected to buffer %d bytes, wrote %d", nread, nwritten)
- continue
- }
- }
- // If this is the last of the data we'll be able to read
- // from this descriptor, read as much as there is to read.
- for rfd.Revents&unix.POLLHUP == unix.POLLHUP {
- nr, err := unix.Read(int(rfd.Fd), b)
- logIfNotRetryable(err, fmt.Sprintf("read %s: %v", fdDesc[int(rfd.Fd)], err))
- if nr <= 0 {
- break
- }
- if wfd, ok := relays[int(rfd.Fd)]; ok {
- nwritten, err := buffers[wfd].Write(b[:nr])
- if err != nil {
- logrus.Debugf("buffer: %v", err)
- break
- }
- if nwritten != nr {
- logrus.Debugf("buffer: expected to buffer %d bytes, wrote %d", nr, nwritten)
- break
- }
- }
- }
- }
- if nread == 0 {
- removeFds[int(rfd.Fd)] = struct{}{}
- }
- }
- pollTimeout = -1
- for wfd, buffer := range buffers {
- if buffer.Len() > 0 {
- nwritten, err := unix.Write(wfd, buffer.Bytes())
- logIfNotRetryable(err, fmt.Sprintf("write %s: %v", fdDesc[wfd], err))
- if nwritten >= 0 {
- _ = buffer.Next(nwritten)
- }
- }
- if buffer.Len() > 0 {
- pollTimeout = 100
- }
- }
- for rfd := range removeFds {
- if !options.Spec.Process.Terminal && rfd == unix.Stdin {
- stdinCopy.Close()
- }
- delete(relays, rfd)
- }
- }
- }()
-
- // Set up mounts and namespaces, and run the parent subprocess.
- status, err := runUsingChroot(options.Spec, options.BundlePath, ctty, stdin, stdout, stderr, closeOnceRunning)
- deferred()
- if err != nil {
- fmt.Fprintf(os.Stderr, "error running subprocess: %v\n", err)
- os.Exit(1)
- }
-
- // Pass the process's exit status back to the caller by exiting with the same status.
- if status.Exited() {
- if status.ExitStatus() != 0 {
- fmt.Fprintf(os.Stderr, "subprocess exited with status %d\n", status.ExitStatus())
- }
- os.Exit(status.ExitStatus())
- } else if status.Signaled() {
- fmt.Fprintf(os.Stderr, "subprocess exited on %s\n", status.Signal())
- os.Exit(1)
- }
-}
-
-// runUsingChroot, still in the grandparent process, sets up various bind
-// mounts and then runs the parent process in its own user namespace with the
-// necessary ID mappings.
-func runUsingChroot(spec *specs.Spec, bundlePath string, ctty *os.File, stdin io.Reader, stdout, stderr io.Writer, closeOnceRunning []*os.File) (wstatus unix.WaitStatus, err error) {
- var confwg sync.WaitGroup
-
- // Create a new mount namespace for ourselves and bind mount everything to a new location.
- undoIntermediates, err := bind.SetupIntermediateMountNamespace(spec, bundlePath)
- if err != nil {
- return 1, err
- }
- defer func() {
- undoIntermediates()
- }()
-
- // Bind mount in our filesystems.
- undoChroots, err := setupChrootBindMounts(spec, bundlePath)
- if err != nil {
- return 1, err
- }
- defer func() {
- undoChroots()
- }()
-
- // Create a pipe for passing configuration down to the next process.
- preader, pwriter, err := os.Pipe()
- if err != nil {
- return 1, errors.Wrapf(err, "error creating configuration pipe")
- }
- config, conferr := json.Marshal(runUsingChrootExecSubprocOptions{
- Spec: spec,
- BundlePath: bundlePath,
- })
- if conferr != nil {
- fmt.Fprintf(os.Stderr, "error re-encoding configuration for %q", runUsingChrootExecCommand)
- os.Exit(1)
- }
-
- // Apologize for the namespace configuration that we're about to ignore.
- logNamespaceDiagnostics(spec)
-
- // If we have configured ID mappings, set them here so that they can apply to the child.
- hostUidmap, hostGidmap, err := util.GetHostIDMappings("")
- if err != nil {
- return 1, err
- }
- uidmap, gidmap := spec.Linux.UIDMappings, spec.Linux.GIDMappings
- if len(uidmap) == 0 {
- // No UID mappings are configured for the container. Borrow our parent's mappings.
- uidmap = append([]specs.LinuxIDMapping{}, hostUidmap...)
- for i := range uidmap {
- uidmap[i].HostID = uidmap[i].ContainerID
- }
- }
- if len(gidmap) == 0 {
- // No GID mappings are configured for the container. Borrow our parent's mappings.
- gidmap = append([]specs.LinuxIDMapping{}, hostGidmap...)
- for i := range gidmap {
- gidmap[i].HostID = gidmap[i].ContainerID
- }
- }
-
- // Start the parent subprocess.
- cmd := unshare.Command(append([]string{runUsingChrootExecCommand}, spec.Process.Args...)...)
- cmd.Stdin, cmd.Stdout, cmd.Stderr = stdin, stdout, stderr
- cmd.Dir = "/"
- cmd.Env = append([]string{fmt.Sprintf("LOGLEVEL=%d", logrus.GetLevel())}, os.Environ()...)
- cmd.UnshareFlags = syscall.CLONE_NEWUSER | syscall.CLONE_NEWUTS | syscall.CLONE_NEWNS
- cmd.UidMappings = uidmap
- cmd.GidMappings = gidmap
- cmd.GidMappingsEnableSetgroups = true
- if ctty != nil {
- cmd.Setsid = true
- cmd.Ctty = ctty
- }
- cmd.OOMScoreAdj = spec.Process.OOMScoreAdj
- cmd.ExtraFiles = append([]*os.File{preader}, cmd.ExtraFiles...)
- cmd.Hook = func(int) error {
- for _, f := range closeOnceRunning {
- f.Close()
- }
- return nil
- }
-
- logrus.Debugf("Running %#v in %#v", cmd.Cmd, cmd)
- confwg.Add(1)
- go func() {
- _, conferr = io.Copy(pwriter, bytes.NewReader(config))
- pwriter.Close()
- confwg.Done()
- }()
- err = cmd.Run()
- confwg.Wait()
- if err != nil {
- if exitError, ok := err.(*exec.ExitError); ok {
- if waitStatus, ok := exitError.ProcessState.Sys().(syscall.WaitStatus); ok {
- if waitStatus.Exited() {
- if waitStatus.ExitStatus() != 0 {
- fmt.Fprintf(os.Stderr, "subprocess exited with status %d\n", waitStatus.ExitStatus())
- }
- os.Exit(waitStatus.ExitStatus())
- } else if waitStatus.Signaled() {
- fmt.Fprintf(os.Stderr, "subprocess exited on %s\n", waitStatus.Signal())
- os.Exit(1)
- }
- }
- }
- fmt.Fprintf(os.Stderr, "process exited with error: %v", err)
- os.Exit(1)
- }
-
- return 0, nil
-}
-
-// main() for parent subprocess. Its main job is to try to make our
-// environment look like the one described by the runtime configuration blob,
-// and then launch the intended command as a child.
-func runUsingChrootExecMain() {
- args := os.Args[1:]
- var options runUsingChrootExecSubprocOptions
- var err error
-
- runtime.LockOSThread()
-
- // Set logging.
- if level := os.Getenv("LOGLEVEL"); level != "" {
- if ll, err := strconv.Atoi(level); err == nil {
- logrus.SetLevel(logrus.Level(ll))
- }
- os.Unsetenv("LOGLEVEL")
- }
-
- // Unpack our configuration.
- confPipe := os.NewFile(3, "confpipe")
- if confPipe == nil {
- fmt.Fprintf(os.Stderr, "error reading options pipe\n")
- os.Exit(1)
- }
- defer confPipe.Close()
- if err := json.NewDecoder(confPipe).Decode(&options); err != nil {
- fmt.Fprintf(os.Stderr, "error decoding options: %v\n", err)
- os.Exit(1)
- }
-
- // Set the hostname. We're already in a distinct UTS namespace and are admins in the user
- // namespace which created it, so we shouldn't get a permissions error, but seccomp policy
- // might deny our attempt to call sethostname() anyway, so log a debug message for that.
- if options.Spec.Hostname != "" {
- if err := unix.Sethostname([]byte(options.Spec.Hostname)); err != nil {
- logrus.Debugf("failed to set hostname %q for process: %v", options.Spec.Hostname, err)
- }
- }
-
- // Try to chroot into the root. Do this before we potentially block the syscall via the
- // seccomp profile.
- var oldst, newst unix.Stat_t
- if err := unix.Stat(options.Spec.Root.Path, &oldst); err != nil {
- fmt.Fprintf(os.Stderr, "error stat()ing intended root directory %q: %v\n", options.Spec.Root.Path, err)
- os.Exit(1)
- }
- if err := unix.Chdir(options.Spec.Root.Path); err != nil {
- fmt.Fprintf(os.Stderr, "error chdir()ing to intended root directory %q: %v\n", options.Spec.Root.Path, err)
- os.Exit(1)
- }
- if err := unix.Chroot(options.Spec.Root.Path); err != nil {
- fmt.Fprintf(os.Stderr, "error chroot()ing into directory %q: %v\n", options.Spec.Root.Path, err)
- os.Exit(1)
- }
- if err := unix.Stat("/", &newst); err != nil {
- fmt.Fprintf(os.Stderr, "error stat()ing current root directory: %v\n", err)
- os.Exit(1)
- }
- if oldst.Dev != newst.Dev || oldst.Ino != newst.Ino {
- fmt.Fprintf(os.Stderr, "unknown error chroot()ing into directory %q: %v\n", options.Spec.Root.Path, err)
- os.Exit(1)
- }
- logrus.Debugf("chrooted into %q", options.Spec.Root.Path)
-
- // not doing because it's still shared: creating devices
- // not doing because it's not applicable: setting annotations
- // not doing because it's still shared: setting sysctl settings
- // not doing because cgroupfs is read only: configuring control groups
- // -> this means we can use the freezer to make sure there aren't any lingering processes
- // -> this means we ignore cgroups-based controls
- // not doing because we don't set any in the config: running hooks
- // not doing because we don't set it in the config: setting rootfs read-only
- // not doing because we don't set it in the config: setting rootfs propagation
- logrus.Debugf("setting apparmor profile")
- if err = setApparmorProfile(options.Spec); err != nil {
- fmt.Fprintf(os.Stderr, "error setting apparmor profile for process: %v\n", err)
- os.Exit(1)
- }
- if err = setSelinuxLabel(options.Spec); err != nil {
- fmt.Fprintf(os.Stderr, "error setting SELinux label for process: %v\n", err)
- os.Exit(1)
- }
- logrus.Debugf("setting capabilities")
- if err := setCapabilities(options.Spec); err != nil {
- fmt.Fprintf(os.Stderr, "error setting capabilities for process %v\n", err)
- os.Exit(1)
- }
- if err = setSeccomp(options.Spec); err != nil {
- fmt.Fprintf(os.Stderr, "error setting seccomp filter for process: %v\n", err)
- os.Exit(1)
- }
- logrus.Debugf("setting resource limits")
- if err = setRlimits(options.Spec, false, false); err != nil {
- fmt.Fprintf(os.Stderr, "error setting process resource limits for process: %v\n", err)
- os.Exit(1)
- }
-
- // Try to change to the directory.
- cwd := options.Spec.Process.Cwd
- if !filepath.IsAbs(cwd) {
- cwd = "/" + cwd
- }
- cwd = filepath.Clean(cwd)
- if err := unix.Chdir("/"); err != nil {
- fmt.Fprintf(os.Stderr, "error chdir()ing into new root directory %q: %v\n", options.Spec.Root.Path, err)
- os.Exit(1)
- }
- if err := unix.Chdir(cwd); err != nil {
- fmt.Fprintf(os.Stderr, "error chdir()ing into directory %q under root %q: %v\n", cwd, options.Spec.Root.Path, err)
- os.Exit(1)
- }
- logrus.Debugf("changed working directory to %q", cwd)
-
- // Drop privileges.
- user := options.Spec.Process.User
- if len(user.AdditionalGids) > 0 {
- gids := make([]int, len(user.AdditionalGids))
- for i := range user.AdditionalGids {
- gids[i] = int(user.AdditionalGids[i])
- }
- logrus.Debugf("setting supplemental groups")
- if err = syscall.Setgroups(gids); err != nil {
- fmt.Fprintf(os.Stderr, "error setting supplemental groups list: %v", err)
- os.Exit(1)
- }
- } else {
- logrus.Debugf("clearing supplemental groups")
- if err = syscall.Setgroups([]int{}); err != nil {
- fmt.Fprintf(os.Stderr, "error clearing supplemental groups list: %v", err)
- os.Exit(1)
- }
- }
- logrus.Debugf("setting gid")
- if err = syscall.Setresgid(int(user.GID), int(user.GID), int(user.GID)); err != nil {
- fmt.Fprintf(os.Stderr, "error setting GID: %v", err)
- os.Exit(1)
- }
- logrus.Debugf("setting uid")
- if err = syscall.Setresuid(int(user.UID), int(user.UID), int(user.UID)); err != nil {
- fmt.Fprintf(os.Stderr, "error setting UID: %v", err)
- os.Exit(1)
- }
-
- // Actually run the specified command.
- cmd := exec.Command(args[0], args[1:]...)
- cmd.Env = options.Spec.Process.Env
- cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
- cmd.Dir = cwd
- logrus.Debugf("Running %#v (PATH = %q)", cmd, os.Getenv("PATH"))
- if err = cmd.Run(); err != nil {
- if exitError, ok := err.(*exec.ExitError); ok {
- if waitStatus, ok := exitError.ProcessState.Sys().(syscall.WaitStatus); ok {
- if waitStatus.Exited() {
- if waitStatus.ExitStatus() != 0 {
- fmt.Fprintf(os.Stderr, "subprocess exited with status %d\n", waitStatus.ExitStatus())
- }
- os.Exit(waitStatus.ExitStatus())
- } else if waitStatus.Signaled() {
- fmt.Fprintf(os.Stderr, "subprocess exited on %s\n", waitStatus.Signal())
- os.Exit(1)
- }
- }
- }
- fmt.Fprintf(os.Stderr, "process exited with error: %v", err)
- os.Exit(1)
- }
-}
-
-// logNamespaceDiagnostics knows which namespaces we want to create.
-// Output debug messages when that differs from what we're being asked to do.
-func logNamespaceDiagnostics(spec *specs.Spec) {
- sawMountNS := false
- sawUserNS := false
- sawUTSNS := false
- for _, ns := range spec.Linux.Namespaces {
- switch ns.Type {
- case specs.CgroupNamespace:
- if ns.Path != "" {
- logrus.Debugf("unable to join cgroup namespace, sorry about that")
- } else {
- logrus.Debugf("unable to create cgroup namespace, sorry about that")
- }
- case specs.IPCNamespace:
- if ns.Path != "" {
- logrus.Debugf("unable to join IPC namespace, sorry about that")
- } else {
- logrus.Debugf("unable to create IPC namespace, sorry about that")
- }
- case specs.MountNamespace:
- if ns.Path != "" {
- logrus.Debugf("unable to join mount namespace %q, creating a new one", ns.Path)
- }
- sawMountNS = true
- case specs.NetworkNamespace:
- if ns.Path != "" {
- logrus.Debugf("unable to join network namespace, sorry about that")
- } else {
- logrus.Debugf("unable to create network namespace, sorry about that")
- }
- case specs.PIDNamespace:
- if ns.Path != "" {
- logrus.Debugf("unable to join PID namespace, sorry about that")
- } else {
- logrus.Debugf("unable to create PID namespace, sorry about that")
- }
- case specs.UserNamespace:
- if ns.Path != "" {
- logrus.Debugf("unable to join user namespace %q, creating a new one", ns.Path)
- }
- sawUserNS = true
- case specs.UTSNamespace:
- if ns.Path != "" {
- logrus.Debugf("unable to join UTS namespace %q, creating a new one", ns.Path)
- }
- sawUTSNS = true
- }
- }
- if !sawMountNS {
- logrus.Debugf("mount namespace not requested, but creating a new one anyway")
- }
- if !sawUserNS {
- logrus.Debugf("user namespace not requested, but creating a new one anyway")
- }
- if !sawUTSNS {
- logrus.Debugf("UTS namespace not requested, but creating a new one anyway")
- }
-}
-
-// setApparmorProfile sets the apparmor profile for ourselves, and hopefully any child processes that we'll start.
-func setApparmorProfile(spec *specs.Spec) error {
- if !apparmor.IsEnabled() || spec.Process.ApparmorProfile == "" {
- return nil
- }
- if err := apparmor.ApplyProfile(spec.Process.ApparmorProfile); err != nil {
- return errors.Wrapf(err, "error setting apparmor profile to %q", spec.Process.ApparmorProfile)
- }
- return nil
-}
-
-// setCapabilities sets capabilities for ourselves, to be more or less inherited by any processes that we'll start.
-func setCapabilities(spec *specs.Spec) error {
- caps, err := capability.NewPid(0)
- if err != nil {
- return errors.Wrapf(err, "error reading capabilities of current process")
- }
- capMap := map[capability.CapType][]string{
- capability.BOUNDING: spec.Process.Capabilities.Bounding,
- capability.EFFECTIVE: spec.Process.Capabilities.Effective,
- capability.INHERITABLE: spec.Process.Capabilities.Inheritable,
- capability.PERMITTED: spec.Process.Capabilities.Permitted,
- capability.AMBIENT: spec.Process.Capabilities.Ambient,
- }
- knownCaps := capability.List()
- for capType, capList := range capMap {
- caps.Clear(capType)
- for _, capToSet := range capList {
- cap := capability.CAP_LAST_CAP
- for _, c := range knownCaps {
- if strings.EqualFold("CAP_"+c.String(), capToSet) {
- cap = c
- break
- }
- }
- if cap == capability.CAP_LAST_CAP {
- return errors.Errorf("error mapping capability %q to a number", capToSet)
- }
- caps.Set(capType, cap)
- }
- }
- for capType := range capMap {
- if err = caps.Apply(capType); err != nil {
- return errors.Wrapf(err, "error setting %s capabilities to %#v", capType.String(), capMap[capType])
- }
- }
- return nil
-}
-
-// parses the resource limits for ourselves and any processes that
-// we'll start into a format that's more in line with the kernel APIs
-func parseRlimits(spec *specs.Spec) (map[int]unix.Rlimit, error) {
- if spec.Process == nil {
- return nil, nil
- }
- parsed := make(map[int]unix.Rlimit)
- for _, limit := range spec.Process.Rlimits {
- resource, recognized := rlimitsMap[strings.ToUpper(limit.Type)]
- if !recognized {
- return nil, errors.Errorf("error parsing limit type %q", limit.Type)
- }
- parsed[resource] = unix.Rlimit{Cur: limit.Soft, Max: limit.Hard}
- }
- return parsed, nil
-}
-
-// setRlimits sets any resource limits that we want to apply to processes that
-// we'll start.
-func setRlimits(spec *specs.Spec, onlyLower, onlyRaise bool) error {
- limits, err := parseRlimits(spec)
- if err != nil {
- return err
- }
- for resource, desired := range limits {
- var current unix.Rlimit
- if err := unix.Getrlimit(resource, &current); err != nil {
- return errors.Wrapf(err, "error reading %q limit", rlimitsReverseMap[resource])
- }
- if desired.Max > current.Max && onlyLower {
- // this would raise a hard limit, and we're only here to lower them
- continue
- }
- if desired.Max < current.Max && onlyRaise {
- // this would lower a hard limit, and we're only here to raise them
- continue
- }
- if err := unix.Setrlimit(resource, &desired); err != nil {
- return errors.Wrapf(err, "error setting %q limit to soft=%d,hard=%d (was soft=%d,hard=%d)", rlimitsReverseMap[resource], desired.Cur, desired.Max, current.Cur, current.Max)
- }
- }
- return nil
-}
-
-// setupChrootBindMounts actually bind mounts things under the rootfs, and returns a
-// callback that will clean up its work.
-func setupChrootBindMounts(spec *specs.Spec, bundlePath string) (undoBinds func() error, err error) {
- var fs unix.Statfs_t
- removes := []string{}
- undoBinds = func() error {
- if err2 := bind.UnmountMountpoints(spec.Root.Path, removes); err2 != nil {
- logrus.Warnf("pkg/chroot: error unmounting %q: %v", spec.Root.Path, err2)
- if err == nil {
- err = err2
- }
- }
- return err
- }
-
- // Now bind mount all of those things to be under the rootfs's location in this
- // mount namespace.
- commonFlags := uintptr(unix.MS_BIND | unix.MS_REC | unix.MS_PRIVATE)
- bindFlags := commonFlags | unix.MS_NODEV
- devFlags := commonFlags | unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_RDONLY
- procFlags := devFlags | unix.MS_NODEV
- sysFlags := devFlags | unix.MS_NODEV | unix.MS_RDONLY
-
- // Bind /dev read-only.
- subDev := filepath.Join(spec.Root.Path, "/dev")
- if err := unix.Mount("/dev", subDev, "bind", devFlags, ""); err != nil {
- if os.IsNotExist(err) {
- err = os.Mkdir(subDev, 0700)
- if err == nil {
- err = unix.Mount("/dev", subDev, "bind", devFlags, "")
- }
- }
- if err != nil {
- return undoBinds, errors.Wrapf(err, "error bind mounting /dev from host into mount namespace")
- }
- }
- // Make sure it's read-only.
- if err = unix.Statfs(subDev, &fs); err != nil {
- return undoBinds, errors.Wrapf(err, "error checking if directory %q was bound read-only", subDev)
- }
- if fs.Flags&unix.ST_RDONLY == 0 {
- if err := unix.Mount(subDev, subDev, "bind", devFlags|unix.MS_REMOUNT, ""); err != nil {
- return undoBinds, errors.Wrapf(err, "error remounting /dev in mount namespace read-only")
- }
- }
- logrus.Debugf("bind mounted %q to %q", "/dev", filepath.Join(spec.Root.Path, "/dev"))
-
- // Bind /proc read-only.
- subProc := filepath.Join(spec.Root.Path, "/proc")
- if err := unix.Mount("/proc", subProc, "bind", procFlags, ""); err != nil {
- if os.IsNotExist(err) {
- err = os.Mkdir(subProc, 0700)
- if err == nil {
- err = unix.Mount("/proc", subProc, "bind", procFlags, "")
- }
- }
- if err != nil {
- return undoBinds, errors.Wrapf(err, "error bind mounting /proc from host into mount namespace")
- }
- }
- logrus.Debugf("bind mounted %q to %q", "/proc", filepath.Join(spec.Root.Path, "/proc"))
-
- // Bind /sys read-only.
- subSys := filepath.Join(spec.Root.Path, "/sys")
- if err := unix.Mount("/sys", subSys, "bind", sysFlags, ""); err != nil {
- if os.IsNotExist(err) {
- err = os.Mkdir(subSys, 0700)
- if err == nil {
- err = unix.Mount("/sys", subSys, "bind", sysFlags, "")
- }
- }
- if err != nil {
- return undoBinds, errors.Wrapf(err, "error bind mounting /sys from host into mount namespace")
- }
- }
- // Make sure it's read-only.
- if err = unix.Statfs(subSys, &fs); err != nil {
- return undoBinds, errors.Wrapf(err, "error checking if directory %q was bound read-only", subSys)
- }
- if fs.Flags&unix.ST_RDONLY == 0 {
- if err := unix.Mount(subSys, subSys, "bind", sysFlags|unix.MS_REMOUNT, ""); err != nil {
- return undoBinds, errors.Wrapf(err, "error remounting /sys in mount namespace read-only")
- }
- }
- logrus.Debugf("bind mounted %q to %q", "/sys", filepath.Join(spec.Root.Path, "/sys"))
-
- // Add /sys/fs/selinux to the set of masked paths, to ensure that we don't have processes
- // attempting to interact with labeling, when they aren't allowed to do so.
- spec.Linux.MaskedPaths = append(spec.Linux.MaskedPaths, "/sys/fs/selinux")
- // Add /sys/fs/cgroup to the set of masked paths, to ensure that we don't have processes
- // attempting to mess with cgroup configuration, when they aren't allowed to do so.
- spec.Linux.MaskedPaths = append(spec.Linux.MaskedPaths, "/sys/fs/cgroup")
-
- // Bind mount in everything we've been asked to mount.
- for _, m := range spec.Mounts {
- // Skip anything that we just mounted.
- switch m.Destination {
- case "/dev", "/proc", "/sys":
- logrus.Debugf("already bind mounted %q on %q", m.Destination, filepath.Join(spec.Root.Path, m.Destination))
- continue
- default:
- if strings.HasPrefix(m.Destination, "/dev/") {
- continue
- }
- if strings.HasPrefix(m.Destination, "/proc/") {
- continue
- }
- if strings.HasPrefix(m.Destination, "/sys/") {
- continue
- }
- }
- // Skip anything that isn't a bind or tmpfs mount.
- if m.Type != "bind" && m.Type != "tmpfs" {
- logrus.Debugf("skipping mount of type %q on %q", m.Type, m.Destination)
- continue
- }
- // If the target is there, we can just mount it.
- var srcinfo os.FileInfo
- switch m.Type {
- case "bind":
- srcinfo, err = os.Stat(m.Source)
- if err != nil {
- return undoBinds, errors.Wrapf(err, "error examining %q for mounting in mount namespace", m.Source)
- }
- case "tmpfs":
- srcinfo, err = os.Stat("/")
- if err != nil {
- return undoBinds, errors.Wrapf(err, "error examining / to use as a template for a tmpfs")
- }
- }
- target := filepath.Join(spec.Root.Path, m.Destination)
- if _, err := os.Stat(target); err != nil {
- // If the target can't be stat()ted, check the error.
- if !os.IsNotExist(err) {
- return undoBinds, errors.Wrapf(err, "error examining %q for mounting in mount namespace", target)
- }
- // The target isn't there yet, so create it, and make a
- // note to remove it later.
- if srcinfo.IsDir() {
- if err = os.MkdirAll(target, 0111); err != nil {
- return undoBinds, errors.Wrapf(err, "error creating mountpoint %q in mount namespace", target)
- }
- removes = append(removes, target)
- } else {
- if err = os.MkdirAll(filepath.Dir(target), 0111); err != nil {
- return undoBinds, errors.Wrapf(err, "error ensuring parent of mountpoint %q (%q) is present in mount namespace", target, filepath.Dir(target))
- }
- var file *os.File
- if file, err = os.OpenFile(target, os.O_WRONLY|os.O_CREATE, 0); err != nil {
- return undoBinds, errors.Wrapf(err, "error creating mountpoint %q in mount namespace", target)
- }
- file.Close()
- removes = append(removes, target)
- }
- }
- requestFlags := bindFlags
- expectedFlags := uintptr(0)
- if util.StringInSlice("nodev", m.Options) {
- requestFlags |= unix.MS_NODEV
- expectedFlags |= unix.ST_NODEV
- }
- if util.StringInSlice("noexec", m.Options) {
- requestFlags |= unix.MS_NOEXEC
- expectedFlags |= unix.ST_NOEXEC
- }
- if util.StringInSlice("nosuid", m.Options) {
- requestFlags |= unix.MS_NOSUID
- expectedFlags |= unix.ST_NOSUID
- }
- if util.StringInSlice("ro", m.Options) {
- requestFlags |= unix.MS_RDONLY
- expectedFlags |= unix.ST_RDONLY
- }
- switch m.Type {
- case "bind":
- // Do the bind mount.
- if err := unix.Mount(m.Source, target, "", requestFlags, ""); err != nil {
- return undoBinds, errors.Wrapf(err, "error bind mounting %q from host to %q in mount namespace (%q)", m.Source, m.Destination, target)
- }
- logrus.Debugf("bind mounted %q to %q", m.Source, target)
- case "tmpfs":
- // Mount a tmpfs.
- if err := mount.Mount(m.Source, target, m.Type, strings.Join(append(m.Options, "private"), ",")); err != nil {
- return undoBinds, errors.Wrapf(err, "error mounting tmpfs to %q in mount namespace (%q, %q)", m.Destination, target, strings.Join(m.Options, ","))
- }
- logrus.Debugf("mounted a tmpfs to %q", target)
- }
- if err = unix.Statfs(target, &fs); err != nil {
- return undoBinds, errors.Wrapf(err, "error checking if directory %q was bound read-only", subSys)
- }
- if uintptr(fs.Flags)&expectedFlags != expectedFlags {
- if err := unix.Mount(target, target, "bind", requestFlags|unix.MS_REMOUNT, ""); err != nil {
- return undoBinds, errors.Wrapf(err, "error remounting %q in mount namespace with expected flags")
- }
- }
- }
-
- // Set up any read-only paths that we need to. If we're running inside
- // of a container, some of these locations will already be read-only.
- for _, roPath := range spec.Linux.ReadonlyPaths {
- r := filepath.Join(spec.Root.Path, roPath)
- target, err := filepath.EvalSymlinks(r)
- if err != nil {
- if os.IsNotExist(err) {
- // No target, no problem.
- continue
- }
- return undoBinds, errors.Wrapf(err, "error checking %q for symlinks before marking it read-only", r)
- }
- // Check if the location is already read-only.
- var fs unix.Statfs_t
- if err = unix.Statfs(target, &fs); err != nil {
- if os.IsNotExist(err) {
- // No target, no problem.
- continue
- }
- return undoBinds, errors.Wrapf(err, "error checking if directory %q is already read-only", target)
- }
- if fs.Flags&unix.ST_RDONLY != 0 {
- continue
- }
- // Mount the location over itself, so that we can remount it as read-only.
- roFlags := uintptr(unix.MS_NODEV | unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_RDONLY)
- if err := unix.Mount(target, target, "", roFlags|unix.MS_BIND|unix.MS_REC, ""); err != nil {
- if os.IsNotExist(err) {
- // No target, no problem.
- continue
- }
- return undoBinds, errors.Wrapf(err, "error bind mounting %q onto itself in preparation for making it read-only", target)
- }
- // Remount the location read-only.
- if err = unix.Statfs(target, &fs); err != nil {
- return undoBinds, errors.Wrapf(err, "error checking if directory %q was bound read-only", target)
- }
- if fs.Flags&unix.ST_RDONLY == 0 {
- if err := unix.Mount(target, target, "", roFlags|unix.MS_BIND|unix.MS_REMOUNT, ""); err != nil {
- return undoBinds, errors.Wrapf(err, "error remounting %q in mount namespace read-only", target)
- }
- }
- // Check again.
- if err = unix.Statfs(target, &fs); err != nil {
- return undoBinds, errors.Wrapf(err, "error checking if directory %q was remounted read-only", target)
- }
- if fs.Flags&unix.ST_RDONLY == 0 {
- return undoBinds, errors.Wrapf(err, "error verifying that %q in mount namespace was remounted read-only", target)
- }
- }
-
- // Create an empty directory for to use for masking directories.
- roEmptyDir := filepath.Join(bundlePath, "empty")
- if len(spec.Linux.MaskedPaths) > 0 {
- if err := os.Mkdir(roEmptyDir, 0700); err != nil {
- return undoBinds, errors.Wrapf(err, "error creating empty directory %q", roEmptyDir)
- }
- removes = append(removes, roEmptyDir)
- }
-
- // Set up any masked paths that we need to. If we're running inside of
- // a container, some of these locations will already be read-only tmpfs
- // filesystems or bind mounted to os.DevNull. If we're not running
- // inside of a container, and nobody else has done that, we'll do it.
- for _, masked := range spec.Linux.MaskedPaths {
- t := filepath.Join(spec.Root.Path, masked)
- target, err := filepath.EvalSymlinks(t)
- if err != nil {
- target = t
- }
- // Get some info about the null device.
- nullinfo, err := os.Stat(os.DevNull)
- if err != nil {
- return undoBinds, errors.Wrapf(err, "error examining %q for masking in mount namespace", os.DevNull)
- }
- // Get some info about the target.
- targetinfo, err := os.Stat(target)
- if err != nil {
- if os.IsNotExist(err) {
- // No target, no problem.
- continue
- }
- return undoBinds, errors.Wrapf(err, "error examining %q for masking in mount namespace", target)
- }
- if targetinfo.IsDir() {
- // The target's a directory. Check if it's a read-only filesystem.
- var statfs unix.Statfs_t
- if err = unix.Statfs(target, &statfs); err != nil {
- return undoBinds, errors.Wrapf(err, "error checking if directory %q is a mountpoint", target)
- }
- isReadOnly := statfs.Flags&unix.MS_RDONLY != 0
- // Check if any of the IDs we're mapping could read it.
- isAccessible := true
- var stat unix.Stat_t
- if err = unix.Stat(target, &stat); err != nil {
- return undoBinds, errors.Wrapf(err, "error checking permissions on directory %q", target)
- }
- isAccessible = false
- if stat.Mode&unix.S_IROTH|unix.S_IXOTH != 0 {
- isAccessible = true
- }
- if !isAccessible && stat.Mode&unix.S_IROTH|unix.S_IXOTH != 0 {
- if len(spec.Linux.GIDMappings) > 0 {
- for _, mapping := range spec.Linux.GIDMappings {
- if stat.Gid >= mapping.ContainerID && stat.Gid < mapping.ContainerID+mapping.Size {
- isAccessible = true
- break
- }
- }
- }
- }
- if !isAccessible && stat.Mode&unix.S_IRUSR|unix.S_IXUSR != 0 {
- if len(spec.Linux.UIDMappings) > 0 {
- for _, mapping := range spec.Linux.UIDMappings {
- if stat.Uid >= mapping.ContainerID && stat.Uid < mapping.ContainerID+mapping.Size {
- isAccessible = true
- break
- }
- }
- }
- }
- // Check if it's empty.
- hasContent := false
- directory, err := os.Open(target)
- if err != nil {
- if !os.IsPermission(err) {
- return undoBinds, errors.Wrapf(err, "error opening directory %q", target)
- }
- } else {
- names, err := directory.Readdirnames(0)
- directory.Close()
- if err != nil {
- return undoBinds, errors.Wrapf(err, "error reading contents of directory %q", target)
- }
- hasContent = false
- for _, name := range names {
- switch name {
- case ".", "..":
- continue
- default:
- hasContent = true
- }
- if hasContent {
- break
- }
- }
- }
- // The target's a directory, so read-only bind mount an empty directory on it.
- roFlags := uintptr(syscall.MS_BIND | syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_NOEXEC | syscall.MS_RDONLY)
- if !isReadOnly || (hasContent && isAccessible) {
- if err = unix.Mount(roEmptyDir, target, "bind", roFlags, ""); err != nil {
- return undoBinds, errors.Wrapf(err, "error masking directory %q in mount namespace", target)
- }
- if err = unix.Statfs(target, &fs); err != nil {
- return undoBinds, errors.Wrapf(err, "error checking if directory %q was mounted read-only in mount namespace", target)
- }
- if fs.Flags&unix.ST_RDONLY == 0 {
- if err = unix.Mount(target, target, "", roFlags|syscall.MS_REMOUNT, ""); err != nil {
- return undoBinds, errors.Wrapf(err, "error making sure directory %q in mount namespace is read only", target)
- }
- }
- }
- } else {
- // The target's not a directory, so bind mount os.DevNull over it, unless it's already os.DevNull.
- if !os.SameFile(nullinfo, targetinfo) {
- if err = unix.Mount(os.DevNull, target, "", uintptr(syscall.MS_BIND|syscall.MS_RDONLY|syscall.MS_PRIVATE), ""); err != nil {
- return undoBinds, errors.Wrapf(err, "error masking non-directory %q in mount namespace", target)
- }
- }
- }
- }
- return undoBinds, nil
-}
diff --git a/vendor/github.com/projectatomic/buildah/chroot/seccomp.go b/vendor/github.com/projectatomic/buildah/chroot/seccomp.go
deleted file mode 100644
index f2c55017f..000000000
--- a/vendor/github.com/projectatomic/buildah/chroot/seccomp.go
+++ /dev/null
@@ -1,142 +0,0 @@
-// +build linux,seccomp
-
-package chroot
-
-import (
- "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/pkg/errors"
- libseccomp "github.com/seccomp/libseccomp-golang"
- "github.com/sirupsen/logrus"
-)
-
-// setSeccomp sets the seccomp filter for ourselves and any processes that we'll start.
-func setSeccomp(spec *specs.Spec) error {
- logrus.Debugf("setting seccomp configuration")
- if spec.Linux.Seccomp == nil {
- return nil
- }
- mapAction := func(specAction specs.LinuxSeccompAction) libseccomp.ScmpAction {
- switch specAction {
- case specs.ActKill:
- return libseccomp.ActKill
- case specs.ActTrap:
- return libseccomp.ActTrap
- case specs.ActErrno:
- return libseccomp.ActErrno
- case specs.ActTrace:
- return libseccomp.ActTrace
- case specs.ActAllow:
- return libseccomp.ActAllow
- }
- return libseccomp.ActInvalid
- }
- mapArch := func(specArch specs.Arch) libseccomp.ScmpArch {
- switch specArch {
- case specs.ArchX86:
- return libseccomp.ArchX86
- case specs.ArchX86_64:
- return libseccomp.ArchAMD64
- case specs.ArchX32:
- return libseccomp.ArchX32
- case specs.ArchARM:
- return libseccomp.ArchARM
- case specs.ArchAARCH64:
- return libseccomp.ArchARM64
- case specs.ArchMIPS:
- return libseccomp.ArchMIPS
- case specs.ArchMIPS64:
- return libseccomp.ArchMIPS64
- case specs.ArchMIPS64N32:
- return libseccomp.ArchMIPS64N32
- case specs.ArchMIPSEL:
- return libseccomp.ArchMIPSEL
- case specs.ArchMIPSEL64:
- return libseccomp.ArchMIPSEL64
- case specs.ArchMIPSEL64N32:
- return libseccomp.ArchMIPSEL64N32
- case specs.ArchPPC:
- return libseccomp.ArchPPC
- case specs.ArchPPC64:
- return libseccomp.ArchPPC64
- case specs.ArchPPC64LE:
- return libseccomp.ArchPPC64LE
- case specs.ArchS390:
- return libseccomp.ArchS390
- case specs.ArchS390X:
- return libseccomp.ArchS390X
- case specs.ArchPARISC:
- /* fallthrough */ /* for now */
- case specs.ArchPARISC64:
- /* fallthrough */ /* for now */
- }
- return libseccomp.ArchInvalid
- }
- mapOp := func(op specs.LinuxSeccompOperator) libseccomp.ScmpCompareOp {
- switch op {
- case specs.OpNotEqual:
- return libseccomp.CompareNotEqual
- case specs.OpLessThan:
- return libseccomp.CompareLess
- case specs.OpLessEqual:
- return libseccomp.CompareLessOrEqual
- case specs.OpEqualTo:
- return libseccomp.CompareEqual
- case specs.OpGreaterEqual:
- return libseccomp.CompareGreaterEqual
- case specs.OpGreaterThan:
- return libseccomp.CompareGreater
- case specs.OpMaskedEqual:
- return libseccomp.CompareMaskedEqual
- }
- return libseccomp.CompareInvalid
- }
-
- filter, err := libseccomp.NewFilter(mapAction(spec.Linux.Seccomp.DefaultAction))
- if err != nil {
- return errors.Wrapf(err, "error creating seccomp filter with default action %q", spec.Linux.Seccomp.DefaultAction)
- }
- for _, arch := range spec.Linux.Seccomp.Architectures {
- if err = filter.AddArch(mapArch(arch)); err != nil {
- return errors.Wrapf(err, "error adding architecture %q(%q) to seccomp filter", arch, mapArch(arch))
- }
- }
- for _, rule := range spec.Linux.Seccomp.Syscalls {
- scnames := make(map[libseccomp.ScmpSyscall]string)
- for _, name := range rule.Names {
- scnum, err := libseccomp.GetSyscallFromName(name)
- if err != nil {
- logrus.Debugf("error mapping syscall %q to a syscall, ignoring %q rule for %q", name, rule.Action, name)
- continue
- }
- scnames[scnum] = name
- }
- for scnum := range scnames {
- if len(rule.Args) == 0 {
- if err = filter.AddRule(scnum, mapAction(rule.Action)); err != nil {
- return errors.Wrapf(err, "error adding a rule (%q:%q) to seccomp filter", scnames[scnum], rule.Action)
- }
- continue
- }
- var conditions []libseccomp.ScmpCondition
- for _, arg := range rule.Args {
- condition, err := libseccomp.MakeCondition(arg.Index, mapOp(arg.Op), arg.Value, arg.ValueTwo)
- if err != nil {
- return errors.Wrapf(err, "error building a seccomp condition %d:%v:%d:%d", arg.Index, arg.Op, arg.Value, arg.ValueTwo)
- }
- conditions = append(conditions, condition)
- }
- if err = filter.AddRuleConditional(scnum, mapAction(rule.Action), conditions); err != nil {
- return errors.Wrapf(err, "error adding a conditional rule (%q:%q) to seccomp filter", scnames[scnum], rule.Action)
- }
- }
- }
- if err = filter.SetNoNewPrivsBit(spec.Process.NoNewPrivileges); err != nil {
- return errors.Wrapf(err, "error setting no-new-privileges bit to %v", spec.Process.NoNewPrivileges)
- }
- err = filter.Load()
- filter.Release()
- if err != nil {
- return errors.Wrapf(err, "error activating seccomp filter")
- }
- return nil
-}
diff --git a/vendor/github.com/projectatomic/buildah/chroot/seccomp_unsupported.go b/vendor/github.com/projectatomic/buildah/chroot/seccomp_unsupported.go
deleted file mode 100644
index a5b74bf09..000000000
--- a/vendor/github.com/projectatomic/buildah/chroot/seccomp_unsupported.go
+++ /dev/null
@@ -1,15 +0,0 @@
-// +build !linux !seccomp
-
-package chroot
-
-import (
- "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/pkg/errors"
-)
-
-func setSeccomp(spec *specs.Spec) error {
- if spec.Linux.Seccomp != nil {
- return errors.New("configured a seccomp filter without seccomp support?")
- }
- return nil
-}
diff --git a/vendor/github.com/projectatomic/buildah/chroot/selinux.go b/vendor/github.com/projectatomic/buildah/chroot/selinux.go
deleted file mode 100644
index 3e62d743d..000000000
--- a/vendor/github.com/projectatomic/buildah/chroot/selinux.go
+++ /dev/null
@@ -1,22 +0,0 @@
-// +build linux,selinux
-
-package chroot
-
-import (
- "github.com/opencontainers/runtime-spec/specs-go"
- selinux "github.com/opencontainers/selinux/go-selinux"
- "github.com/opencontainers/selinux/go-selinux/label"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
-)
-
-// setSelinuxLabel sets the process label for child processes that we'll start.
-func setSelinuxLabel(spec *specs.Spec) error {
- logrus.Debugf("setting selinux label")
- if spec.Process.SelinuxLabel != "" && selinux.EnforceMode() != selinux.Disabled {
- if err := label.SetProcessLabel(spec.Process.SelinuxLabel); err != nil {
- return errors.Wrapf(err, "error setting process label to %q", spec.Process.SelinuxLabel)
- }
- }
- return nil
-}
diff --git a/vendor/github.com/projectatomic/buildah/chroot/selinux_unsupported.go b/vendor/github.com/projectatomic/buildah/chroot/selinux_unsupported.go
deleted file mode 100644
index 1c6f48912..000000000
--- a/vendor/github.com/projectatomic/buildah/chroot/selinux_unsupported.go
+++ /dev/null
@@ -1,18 +0,0 @@
-// +build !linux !selinux
-
-package chroot
-
-import (
- "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/pkg/errors"
-)
-
-func setSelinuxLabel(spec *specs.Spec) error {
- if spec.Linux.MountLabel != "" {
- return errors.New("configured an SELinux mount label without SELinux support?")
- }
- if spec.Process.SelinuxLabel != "" {
- return errors.New("configured an SELinux process label without SELinux support?")
- }
- return nil
-}
diff --git a/vendor/github.com/projectatomic/buildah/chroot/unsupported.go b/vendor/github.com/projectatomic/buildah/chroot/unsupported.go
deleted file mode 100644
index 5312c0024..000000000
--- a/vendor/github.com/projectatomic/buildah/chroot/unsupported.go
+++ /dev/null
@@ -1,15 +0,0 @@
-// +build !linux
-
-package chroot
-
-import (
- "io"
-
- "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/pkg/errors"
-)
-
-// RunUsingChroot is not supported.
-func RunUsingChroot(spec *specs.Spec, bundlePath string, stdin io.Reader, stdout, stderr io.Writer) (err error) {
- return errors.Errorf("--isolation chroot is not supported on this platform")
-}
diff --git a/vendor/github.com/projectatomic/buildah/chroot/util.go b/vendor/github.com/projectatomic/buildah/chroot/util.go
deleted file mode 100644
index 34cc77260..000000000
--- a/vendor/github.com/projectatomic/buildah/chroot/util.go
+++ /dev/null
@@ -1,15 +0,0 @@
-// +build linux
-
-package chroot
-
-func dedupeStringSlice(slice []string) []string {
- done := make([]string, 0, len(slice))
- m := make(map[string]struct{})
- for _, s := range slice {
- if _, present := m[s]; !present {
- m[s] = struct{}{}
- done = append(done, s)
- }
- }
- return done
-}