summaryrefslogtreecommitdiffhomepage
path: root/runsc
diff options
context:
space:
mode:
Diffstat (limited to 'runsc')
-rw-r--r--runsc/boot/loader.go16
-rw-r--r--runsc/cmd/exec.go6
-rw-r--r--runsc/container/container.go9
-rw-r--r--runsc/sandbox/sandbox.go135
4 files changed, 102 insertions, 64 deletions
diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go
index 165fb2ebb..5bfb15971 100644
--- a/runsc/boot/loader.go
+++ b/runsc/boot/loader.go
@@ -715,7 +715,7 @@ func (l *Loader) startContainer(spec *specs.Spec, conf *config.Config, cid strin
return fmt.Errorf("using TTY, stdios not expected: %d", l)
}
if ep.hostTTY == nil {
- return fmt.Errorf("terminal enabled but no TTY provided (--console-socket possibly passed)")
+ return fmt.Errorf("terminal enabled but no TTY provided. Did you set --console-socket on create?")
}
info.stdioFDs = []*fd.FD{ep.hostTTY, ep.hostTTY, ep.hostTTY}
ep.hostTTY = nil
@@ -734,7 +734,7 @@ func (l *Loader) startContainer(spec *specs.Spec, conf *config.Config, cid strin
func (l *Loader) createContainerProcess(root bool, cid string, info *containerInfo) (*kernel.ThreadGroup, *host.TTYFileOperations, *hostvfs2.TTYFileDescription, error) {
// Create the FD map, which will set stdin, stdout, and stderr.
ctx := info.procArgs.NewContext(l.k)
- fdTable, ttyFile, ttyFileVFS2, err := createFDTable(ctx, info.spec.Process.Terminal, info.stdioFDs)
+ fdTable, ttyFile, ttyFileVFS2, err := createFDTable(ctx, info.spec.Process.Terminal, info.stdioFDs, info.spec.Process.User)
if err != nil {
return nil, nil, nil, fmt.Errorf("importing fds: %w", err)
}
@@ -980,7 +980,7 @@ func (l *Loader) executeAsync(args *control.ExecArgs) (kernel.ThreadID, error) {
tty: ttyFile,
ttyVFS2: ttyFileVFS2,
}
- log.Debugf("updated processes: %s", l.processes)
+ log.Debugf("updated processes: %v", l.processes)
return tgid, nil
}
@@ -1024,7 +1024,7 @@ func (l *Loader) waitPID(tgid kernel.ThreadID, cid string, waitStatus *uint32) e
l.mu.Lock()
delete(l.processes, eid)
- log.Debugf("updated processes (removal): %s", l.processes)
+ log.Debugf("updated processes (removal): %v", l.processes)
l.mu.Unlock()
return nil
}
@@ -1092,7 +1092,7 @@ func newRootNetworkNamespace(conf *config.Config, clock tcpip.Clock, uniqueID st
return inet.NewRootNamespace(s, creator), nil
default:
- panic(fmt.Sprintf("invalid network configuration: %d", conf.Network))
+ panic(fmt.Sprintf("invalid network configuration: %v", conf.Network))
}
}
@@ -1212,7 +1212,7 @@ func (l *Loader) signal(cid string, pid, signo int32, mode SignalDeliveryMode) e
return nil
default:
- panic(fmt.Sprintf("unknown signal delivery mode %s", mode))
+ panic(fmt.Sprintf("unknown signal delivery mode %v", mode))
}
}
@@ -1337,14 +1337,14 @@ func (l *Loader) ttyFromIDLocked(key execID) (*host.TTYFileOperations, *hostvfs2
return ep.tty, ep.ttyVFS2, nil
}
-func createFDTable(ctx context.Context, console bool, stdioFDs []*fd.FD) (*kernel.FDTable, *host.TTYFileOperations, *hostvfs2.TTYFileDescription, error) {
+func createFDTable(ctx context.Context, console bool, stdioFDs []*fd.FD, user specs.User) (*kernel.FDTable, *host.TTYFileOperations, *hostvfs2.TTYFileDescription, error) {
if len(stdioFDs) != 3 {
return nil, nil, nil, fmt.Errorf("stdioFDs should contain exactly 3 FDs (stdin, stdout, and stderr), but %d FDs received", len(stdioFDs))
}
k := kernel.KernelFromContext(ctx)
fdTable := k.NewFDTable()
- ttyFile, ttyFileVFS2, err := fdimport.Import(ctx, fdTable, console, stdioFDs)
+ ttyFile, ttyFileVFS2, err := fdimport.Import(ctx, fdTable, console, auth.KUID(user.UID), auth.KGID(user.GID), stdioFDs)
if err != nil {
fdTable.DecRef(ctx)
return nil, nil, nil, err
diff --git a/runsc/cmd/exec.go b/runsc/cmd/exec.go
index 242d474b8..2139fdf53 100644
--- a/runsc/cmd/exec.go
+++ b/runsc/cmd/exec.go
@@ -146,12 +146,12 @@ func (ex *Exec) Execute(_ context.Context, f *flag.FlagSet, args ...interface{})
if ex.detach {
return ex.execChildAndWait(waitStatus)
}
- return ex.exec(c, e, waitStatus)
+ return ex.exec(conf, c, e, waitStatus)
}
-func (ex *Exec) exec(c *container.Container, e *control.ExecArgs, waitStatus *unix.WaitStatus) subcommands.ExitStatus {
+func (ex *Exec) exec(conf *config.Config, c *container.Container, e *control.ExecArgs, waitStatus *unix.WaitStatus) subcommands.ExitStatus {
// Start the new process and get its pid.
- pid, err := c.Execute(e)
+ pid, err := c.Execute(conf, e)
if err != nil {
return Errorf("executing processes for container: %v", err)
}
diff --git a/runsc/container/container.go b/runsc/container/container.go
index b789bc7da..213fbc771 100644
--- a/runsc/container/container.go
+++ b/runsc/container/container.go
@@ -310,7 +310,7 @@ func New(conf *config.Config, args Args) (*Container, error) {
defer tty.Close()
}
- if err := c.Sandbox.CreateContainer(c.ID, tty); err != nil {
+ if err := c.Sandbox.CreateContainer(conf, c.ID, tty); err != nil {
return nil, err
}
}
@@ -480,13 +480,13 @@ func Run(conf *config.Config, args Args) (unix.WaitStatus, error) {
// Execute runs the specified command in the container. It returns the PID of
// the newly created process.
-func (c *Container) Execute(args *control.ExecArgs) (int32, error) {
+func (c *Container) Execute(conf *config.Config, args *control.ExecArgs) (int32, error) {
log.Debugf("Execute in container, cid: %s, args: %+v", c.ID, args)
if err := c.requireStatus("execute in", Created, Running); err != nil {
return 0, err
}
args.ContainerID = c.ID
- return c.Sandbox.Execute(args)
+ return c.Sandbox.Execute(conf, args)
}
// Event returns events for the container.
@@ -910,6 +910,9 @@ func (c *Container) createGoferProcess(spec *specs.Spec, conf *config.Config, bu
binPath := specutils.ExePath
cmd := exec.Command(binPath, args...)
cmd.ExtraFiles = goferEnds
+
+ // Set Args[0] to make easier to spot the gofer process. Otherwise it's
+ // shown as `exe`.
cmd.Args[0] = "runsc-gofer"
if attached {
diff --git a/runsc/sandbox/sandbox.go b/runsc/sandbox/sandbox.go
index 9dea7c4d2..95b5d9615 100644
--- a/runsc/sandbox/sandbox.go
+++ b/runsc/sandbox/sandbox.go
@@ -65,6 +65,11 @@ type Sandbox struct {
// is not running.
Pid int `json:"pid"`
+ // UID is the user ID in the parent namespace that the sandbox is running as.
+ UID int `json:"uid"`
+ // GID is the group ID in the parent namespace that the sandbox is running as.
+ GID int `json:"gid"`
+
// Cgroup has the cgroup configuration for the sandbox.
Cgroup *cgroup.Cgroup `json:"cgroup"`
@@ -176,18 +181,22 @@ func New(conf *config.Config, args *Args) (*Sandbox, error) {
}
// CreateContainer creates a non-root container inside the sandbox.
-func (s *Sandbox) CreateContainer(cid string, tty *os.File) error {
+func (s *Sandbox) CreateContainer(conf *config.Config, cid string, tty *os.File) error {
log.Debugf("Create non-root container %q in sandbox %q, PID: %d", cid, s.ID, s.Pid)
- sandboxConn, err := s.sandboxConnect()
- if err != nil {
- return fmt.Errorf("couldn't connect to sandbox: %v", err)
- }
- defer sandboxConn.Close()
var files []*os.File
if tty != nil {
files = []*os.File{tty}
}
+ if err := s.configureStdios(conf, files); err != nil {
+ return err
+ }
+
+ sandboxConn, err := s.sandboxConnect()
+ if err != nil {
+ return fmt.Errorf("couldn't connect to sandbox: %v", err)
+ }
+ defer sandboxConn.Close()
args := boot.CreateArgs{
CID: cid,
@@ -225,6 +234,11 @@ func (s *Sandbox) StartRoot(spec *specs.Spec, conf *config.Config) error {
// StartContainer starts running a non-root container inside the sandbox.
func (s *Sandbox) StartContainer(spec *specs.Spec, conf *config.Config, cid string, stdios, goferFiles []*os.File) error {
log.Debugf("Start non-root container %q in sandbox %q, PID: %d", cid, s.ID, s.Pid)
+
+ if err := s.configureStdios(conf, stdios); err != nil {
+ return err
+ }
+
sandboxConn, err := s.sandboxConnect()
if err != nil {
return fmt.Errorf("couldn't connect to sandbox: %v", err)
@@ -318,8 +332,13 @@ func (s *Sandbox) NewCGroup() (*cgroup.Cgroup, error) {
// Execute runs the specified command in the container. It returns the PID of
// the newly created process.
-func (s *Sandbox) Execute(args *control.ExecArgs) (int32, error) {
+func (s *Sandbox) Execute(conf *config.Config, args *control.ExecArgs) (int32, error) {
log.Debugf("Executing new process in container %q in sandbox %q", args.ContainerID, s.ID)
+
+ if err := s.configureStdios(conf, args.Files); err != nil {
+ return 0, err
+ }
+
conn, err := s.sandboxConnect()
if err != nil {
return 0, s.connError(err)
@@ -505,6 +524,7 @@ func (s *Sandbox) createSandboxProcess(conf *config.Config, args *Args, startSyn
cmd.Stdin = nil
cmd.Stdout = nil
cmd.Stderr = nil
+ var stdios [3]*os.File
// If the console control socket file is provided, then create a new
// pty master/replica pair and set the TTY on the sandbox process.
@@ -525,11 +545,9 @@ func (s *Sandbox) createSandboxProcess(conf *config.Config, args *Args, startSyn
cmd.SysProcAttr.Ctty = nextFD
// Pass the tty as all stdio fds to sandbox.
- for i := 0; i < 3; i++ {
- cmd.ExtraFiles = append(cmd.ExtraFiles, tty)
- cmd.Args = append(cmd.Args, "--stdio-fds="+strconv.Itoa(nextFD))
- nextFD++
- }
+ stdios[0] = tty
+ stdios[1] = tty
+ stdios[2] = tty
if conf.Debug {
// If debugging, send the boot process stdio to the
@@ -541,11 +559,9 @@ func (s *Sandbox) createSandboxProcess(conf *config.Config, args *Args, startSyn
} else {
// If not using a console, pass our current stdio as the
// container stdio via flags.
- for _, f := range []*os.File{os.Stdin, os.Stdout, os.Stderr} {
- cmd.ExtraFiles = append(cmd.ExtraFiles, f)
- cmd.Args = append(cmd.Args, "--stdio-fds="+strconv.Itoa(nextFD))
- nextFD++
- }
+ stdios[0] = os.Stdin
+ stdios[1] = os.Stdout
+ stdios[2] = os.Stderr
if conf.Debug {
// If debugging, send the boot process stdio to the
@@ -595,6 +611,10 @@ func (s *Sandbox) createSandboxProcess(conf *config.Config, args *Args, startSyn
nss = append(nss, specs.LinuxNamespace{Type: specs.NetworkNamespace})
}
+ // These are set to the uid/gid that the sandbox process will use.
+ s.UID = os.Getuid()
+ s.GID = os.Getgid()
+
// User namespace depends on the network type. Host network requires to run
// inside the user namespace specified in the spec or the current namespace
// if none is configured.
@@ -636,51 +656,49 @@ func (s *Sandbox) createSandboxProcess(conf *config.Config, args *Args, startSyn
const nobody = 65534
if conf.Rootless {
log.Infof("Rootless mode: sandbox will run as nobody inside user namespace, mapped to the current user, uid: %d, gid: %d", os.Getuid(), os.Getgid())
- cmd.SysProcAttr.UidMappings = []syscall.SysProcIDMap{
- {
- ContainerID: nobody,
- HostID: os.Getuid(),
- Size: 1,
- },
- }
- cmd.SysProcAttr.GidMappings = []syscall.SysProcIDMap{
- {
- ContainerID: nobody,
- HostID: os.Getgid(),
- Size: 1,
- },
- }
-
} else {
// 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
- // CAP_SYS_ADMIN and CAP_SYS_CHROOT capabilities.
- cmd.SysProcAttr.UidMappings = []syscall.SysProcIDMap{
- {
- ContainerID: nobody,
- HostID: nobody,
- Size: 1,
- },
- }
- cmd.SysProcAttr.GidMappings = []syscall.SysProcIDMap{
- {
- ContainerID: nobody,
- HostID: nobody,
- Size: 1,
- },
- }
+ s.UID = nobody
+ s.GID = nobody
}
// Set credentials to run as user and group nobody.
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: nobody, Gid: nobody}
+ cmd.SysProcAttr.UidMappings = []syscall.SysProcIDMap{
+ {
+ ContainerID: nobody,
+ HostID: s.UID,
+ Size: 1,
+ },
+ }
+ cmd.SysProcAttr.GidMappings = []syscall.SysProcIDMap{
+ {
+ ContainerID: nobody,
+ HostID: s.GID,
+ Size: 1,
+ },
+ }
+
+ // A sandbox process will construct an empty root for itself, so it has
+ // to have CAP_SYS_ADMIN and CAP_SYS_CHROOT capabilities.
cmd.SysProcAttr.AmbientCaps = append(cmd.SysProcAttr.AmbientCaps, uintptr(capability.CAP_SYS_ADMIN), uintptr(capability.CAP_SYS_CHROOT))
+
} else {
return fmt.Errorf("can't run sandbox process as user nobody since we don't have CAP_SETUID or CAP_SETGID")
}
}
+ if err := s.configureStdios(conf, stdios[:]); err != nil {
+ return fmt.Errorf("configuring stdios: %w", err)
+ }
+ for _, file := range stdios {
+ cmd.ExtraFiles = append(cmd.ExtraFiles, file)
+ cmd.Args = append(cmd.Args, "--stdio-fds="+strconv.Itoa(nextFD))
+ nextFD++
+ }
+
+ // Set Args[0] to make easier to spot the sandbox process. Otherwise it's
+ // shown as `exe`.
cmd.Args[0] = "runsc-sandbox"
if s.Cgroup != nil {
@@ -1167,6 +1185,23 @@ func (s *Sandbox) waitForStopped() error {
return backoff.Retry(op, b)
}
+// configureStdios change stdios ownership to give access to the sandbox
+// process. This may be skipped depending on the configuration.
+func (s *Sandbox) configureStdios(conf *config.Config, stdios []*os.File) error {
+ if conf.Rootless || conf.TestOnlyAllowRunAsCurrentUserWithoutChroot {
+ // Cannot change ownership without CAP_CHOWN.
+ return nil
+ }
+
+ for _, file := range stdios {
+ log.Debugf("Changing %q ownership to %d/%d", file.Name(), s.UID, s.GID)
+ if err := file.Chown(s.UID, s.GID); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
// deviceFileForPlatform opens the device file for the given platform. If the
// platform does not need a device file, then nil is returned.
func deviceFileForPlatform(name string) (*os.File, error) {