diff --git a/README.md b/README.md index 2a3f1bef..f1997117 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ RootlessKit is a kind of Linux-native "fake root" utility, made for mainly runni - [Usage](#usage) - [State directory](#state-directory) - [Environment variables](#environment-variables) +- [PID Namespace](#pid-namespace) - [Network Drivers](#network-drivers) - [`--net=host` (default)](#--nethost-default) - [`--net=slirp4netns` (recommended)](#--netslirp4netns-recommended) @@ -135,7 +136,6 @@ allow Full CLI options: ```console - NAME: rootlesskit - the gate to the rootless world @@ -143,7 +143,7 @@ USAGE: rootlesskit [global options] command [command options] [arguments...] VERSION: - 0.3.0+dev + 0.4.1+dev COMMANDS: help, h Shows a list of commands or help for one command @@ -162,6 +162,7 @@ GLOBAL OPTIONS: --copy-up value mount a filesystem and copy-up the contents. e.g. "--copy-up=/etc" (typically required for non-host network) --copy-up-mode value copy-up mode [tmpfs+symlink] (default: "tmpfs+symlink") --port-driver value port driver for non-host network. [none, socat, slirp4netns, builtin(experimental)] (default: "none") + --pidns create a PID namespace --help, -h show help --version, -v print the version ``` @@ -184,6 +185,14 @@ The following environment variables will be set for the child process: Undocumented environment variables are subject to change. +## PID Namespace + +When `--pidns` (since v0.5.0) is specified, RootlessKit executes the child process in a new PID namespace. +The RootlessKit child process becomes the init (PID=1). +When RootlessKit terminates, all the processes in the namespace are killed with `SIGKILL`. + +See also [`pid_namespaces(7)`](http://man7.org/linux/man-pages/man7/pid_namespaces.7.html). + ## Network Drivers RootlessKit provides several drivers for providing network connectivity: diff --git a/cmd/rootlesskit/main.go b/cmd/rootlesskit/main.go index 040a8012..b69b52e6 100644 --- a/cmd/rootlesskit/main.go +++ b/cmd/rootlesskit/main.go @@ -101,6 +101,10 @@ func main() { Usage: "port driver for non-host network. [none, socat, slirp4netns, builtin(experimental)]", Value: "none", }, + cli.BoolFlag{ + Name: "pidns", + Usage: "create a PID namespace", + }, } app.Before = func(context *cli.Context) error { if debug { @@ -163,6 +167,7 @@ func createParentOpt(clicontext *cli.Context, pipeFDEnvKey, stateDirEnvKey strin opt := parent.Opt{ PipeFDEnvKey: pipeFDEnvKey, StateDirEnvKey: stateDirEnvKey, + CreatePIDNS: clicontext.Bool("pidns"), } opt.StateDir = clicontext.String("state-dir") if opt.StateDir == "" { @@ -298,6 +303,7 @@ func createChildOpt(clicontext *cli.Context, pipeFDEnvKey string, targetCmd []st opt := child.Opt{ PipeFDEnvKey: pipeFDEnvKey, TargetCmd: targetCmd, + MountProcfs: clicontext.Bool("pidns"), } switch s := clicontext.String("net"); s { case "host": diff --git a/pkg/child/child.go b/pkg/child/child.go index c184df74..53232be4 100644 --- a/pkg/child/child.go +++ b/pkg/child/child.go @@ -68,6 +68,19 @@ func mountSysfs() error { return nil } +func mountProcfs() error { + cmds := [][]string{{"mount", "-t", "proc", "none", "/proc"}} + if err := common.Execs(os.Stderr, os.Environ(), cmds); err != nil { + cmdsRo := [][]string{{"mount", "-t", "proc", "-o", "ro", "none", "/proc"}} + logrus.Warnf("failed to mount procfs (%v), falling back to read-only mount (%v): %v", + cmds, cmdsRo, err) + if err := common.Execs(os.Stderr, os.Environ(), cmdsRo); err != nil { + logrus.Warnf("failed to mount procfs (%v): %v", cmdsRo, err) + } + } + return nil +} + func activateLoopback() error { cmds := [][]string{ {"ip", "link", "set", "lo", "up"}, @@ -157,6 +170,7 @@ type Opt struct { CopyUpDriver copyup.ChildDriver // cannot be nil if len(CopyUpDirs) != 0 CopyUpDirs []string PortDriver port.ChildDriver + MountProcfs bool // needs to be set if (and only if) parent.Opt.CreatePIDNS is set } func Child(opt Opt) error { @@ -211,6 +225,11 @@ func Child(opt Opt) error { if err := setupNet(msg, etcWasCopied, opt.NetworkDriver); err != nil { return err } + if opt.MountProcfs { + if err := mountProcfs(); err != nil { + return err + } + } portQuitCh := make(chan struct{}) portErrCh := make(chan error) if opt.PortDriver != nil { diff --git a/pkg/parent/parent.go b/pkg/parent/parent.go index 93edb50d..91bc1345 100644 --- a/pkg/parent/parent.go +++ b/pkg/parent/parent.go @@ -30,6 +30,7 @@ type Opt struct { StateDirEnvKey string // optional env key to propagate StateDir value NetworkDriver network.ParentDriver // nil for HostNetwork PortDriver port.ParentDriver // nil for --port-driver=none + CreatePIDNS bool } // Documented state files. Undocumented ones are subject to change. @@ -85,6 +86,10 @@ func Parent(opt Opt) error { if opt.NetworkDriver != nil { cmd.SysProcAttr.Unshareflags |= syscall.CLONE_NEWNET } + if opt.CreatePIDNS { + // cannot be Unshareflags (panics) + cmd.SysProcAttr.Cloneflags |= syscall.CLONE_NEWPID + } cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr