chroot: Move runUsingChrootExecMain to run_common.go
Again, this breaks the FreeBSD build and this will be addressed by refactoring in the next commit. Signed-off-by: Doug Rabson <dfr@rabson.org>
This commit is contained in:
parent
0a61e4b280
commit
4d963eb5e1
|
@ -8,6 +8,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
|
@ -563,3 +564,204 @@ func runUsingChroot(spec *specs.Spec, bundlePath string, ctty *os.File, stdin io
|
|||
|
||||
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 == nil || options.Spec.Process == nil {
|
||||
fmt.Fprintf(os.Stderr, "invalid options spec passed in\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
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 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 {
|
||||
setgroups, _ := ioutil.ReadFile("/proc/self/setgroups")
|
||||
if strings.Trim(string(setgroups), "\n") != "deny" {
|
||||
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)
|
||||
}
|
||||
|
||||
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 capabilities")
|
||||
var keepCaps []string
|
||||
if user.UID != 0 {
|
||||
keepCaps = []string{"CAP_SETUID"}
|
||||
}
|
||||
if err := setCapabilities(options.Spec, keepCaps...); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error setting capabilities for process: %v\n", 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:]...)
|
||||
setPdeathsig(cmd)
|
||||
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"))
|
||||
interrupted := make(chan os.Signal, 100)
|
||||
if err = cmd.Start(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "process failed to start with error: %v", err)
|
||||
}
|
||||
go func() {
|
||||
for range interrupted {
|
||||
if err := cmd.Process.Signal(syscall.SIGKILL); err != nil {
|
||||
logrus.Infof("%v while attempting to send SIGKILL to child process", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
signal.Notify(interrupted, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
|
||||
err = cmd.Wait()
|
||||
signal.Stop(interrupted)
|
||||
close(interrupted)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,187 +68,6 @@ func createJail(options runUsingChrootExecSubprocOptions) error {
|
|||
return 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 == nil || options.Spec.Process == nil {
|
||||
fmt.Fprintf(os.Stderr, "invalid options spec passed in\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
/*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 create a jail and if that fails, fall back to chroot
|
||||
if err := createJail(options); err == nil {
|
||||
logrus.Debugf("jailed into %q", options.Spec.Root.Path)
|
||||
} else {
|
||||
// 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 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 {
|
||||
setgroups, _ := ioutil.ReadFile("/proc/self/setgroups")
|
||||
if strings.Trim(string(setgroups), "\n") != "deny" {
|
||||
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 = unix.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 = unix.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:]...)
|
||||
setPdeathsig(cmd)
|
||||
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"))
|
||||
interrupted := make(chan os.Signal, 100)
|
||||
if err = cmd.Start(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "process failed to start with error: %v", err)
|
||||
}
|
||||
go func() {
|
||||
for range interrupted {
|
||||
if err := cmd.Process.Signal(syscall.SIGKILL); err != nil {
|
||||
logrus.Infof("%v while attempting to send SIGKILL to child process", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
signal.Notify(interrupted, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
|
||||
err = cmd.Wait()
|
||||
signal.Stop(interrupted)
|
||||
close(interrupted)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
|
|
@ -4,16 +4,11 @@
|
|||
package chroot
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
@ -96,207 +91,6 @@ func setPlatformUnshareOptions(spec *specs.Spec, cmd *unshare.Cmd) error {
|
|||
return 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 == nil || options.Spec.Process == nil {
|
||||
fmt.Fprintf(os.Stderr, "invalid options spec passed in\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
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 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 {
|
||||
setgroups, _ := ioutil.ReadFile("/proc/self/setgroups")
|
||||
if strings.Trim(string(setgroups), "\n") != "deny" {
|
||||
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)
|
||||
}
|
||||
|
||||
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 capabilities")
|
||||
var keepCaps []string
|
||||
if user.UID != 0 {
|
||||
keepCaps = []string{"CAP_SETUID"}
|
||||
}
|
||||
if err := setCapabilities(options.Spec, keepCaps...); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error setting capabilities for process: %v\n", 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:]...)
|
||||
setPdeathsig(cmd)
|
||||
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"))
|
||||
interrupted := make(chan os.Signal, 100)
|
||||
if err = cmd.Start(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "process failed to start with error: %v", err)
|
||||
}
|
||||
go func() {
|
||||
for range interrupted {
|
||||
if err := cmd.Process.Signal(syscall.SIGKILL); err != nil {
|
||||
logrus.Infof("%v while attempting to send SIGKILL to child process", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
signal.Notify(interrupted, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
|
||||
err = cmd.Wait()
|
||||
signal.Stop(interrupted)
|
||||
close(interrupted)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
|
Loading…
Reference in New Issue