mirror of
https://github.com/opencontainers/runc.git
synced 2025-10-29 18:21:47 +08:00
Remove userns sidecar process
Move the network setup back into the standard init even for user namespaces now that mounts are fully supported and working. Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
8
error.go
8
error.go
@@ -57,11 +57,3 @@ type Error interface {
|
||||
// Returns the error code for this error.
|
||||
Code() ErrorCode
|
||||
}
|
||||
|
||||
type initError struct {
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
func (i initError) Error() string {
|
||||
return i.Message
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
|
||||
var errorTemplate = template.Must(template.New("error").Parse(`Timestamp: {{.Timestamp}}
|
||||
Code: {{.ECode}}
|
||||
{{if .Err }}
|
||||
Message: {{.Err.Error}}
|
||||
{{if .Message }}
|
||||
Message: {{.Message}}
|
||||
{{end}}
|
||||
Frames:{{range $i, $frame := .Stack.Frames}}
|
||||
---
|
||||
@@ -28,6 +28,7 @@ func newGenericError(err error, c ErrorCode) Error {
|
||||
return &genericError{
|
||||
Timestamp: time.Now(),
|
||||
Err: err,
|
||||
Message: err.Error(),
|
||||
ECode: c,
|
||||
Stack: stacktrace.Capture(1),
|
||||
}
|
||||
@@ -41,6 +42,7 @@ func newSystemError(err error) Error {
|
||||
Timestamp: time.Now(),
|
||||
Err: err,
|
||||
ECode: SystemError,
|
||||
Message: err.Error(),
|
||||
Stack: stacktrace.Capture(1),
|
||||
}
|
||||
}
|
||||
@@ -48,12 +50,13 @@ func newSystemError(err error) Error {
|
||||
type genericError struct {
|
||||
Timestamp time.Time
|
||||
ECode ErrorCode
|
||||
Err error
|
||||
Err error `json:"-"`
|
||||
Message string
|
||||
Stack stacktrace.Stacktrace
|
||||
}
|
||||
|
||||
func (e *genericError) Error() string {
|
||||
return fmt.Sprintf("[%d] %s: %s", e.ECode, e.ECode, e.Err)
|
||||
return fmt.Sprintf("[%d] %s: %s", e.ECode, e.ECode, e.Message)
|
||||
}
|
||||
|
||||
func (e *genericError) Code() ErrorCode {
|
||||
|
||||
@@ -147,7 +147,6 @@ func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, c
|
||||
if cmd.SysProcAttr.Credential == nil {
|
||||
cmd.SysProcAttr.Credential = &syscall.Credential{}
|
||||
}
|
||||
t = "_LIBCONTAINER_INITTYPE=userns"
|
||||
}
|
||||
cmd.Env = append(cmd.Env, t)
|
||||
cmd.SysProcAttr.Cloneflags = cloneFlags
|
||||
|
||||
@@ -166,9 +166,7 @@ func (l *LinuxFactory) StartInitialization(pipefd uintptr) (err error) {
|
||||
// ensure that any data sent from the parent is consumed so it doesn't
|
||||
// receive ECONNRESET when the child writes to the pipe.
|
||||
ioutil.ReadAll(pipe)
|
||||
if err := json.NewEncoder(pipe).Encode(initError{
|
||||
Message: err.Error(),
|
||||
}); err != nil {
|
||||
if err := json.NewEncoder(pipe).Encode(newSystemError(err)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,6 @@ type initType string
|
||||
const (
|
||||
initSetns initType = "setns"
|
||||
initStandard initType = "standard"
|
||||
initUserns initType = "userns"
|
||||
initUsernsSetup initType = "userns_setup"
|
||||
)
|
||||
|
||||
type pid struct {
|
||||
@@ -67,14 +65,6 @@ func newContainerInit(t initType, pipe *os.File) (initer, error) {
|
||||
return &linuxSetnsInit{
|
||||
config: config,
|
||||
}, nil
|
||||
case initUserns:
|
||||
return &linuxUsernsInit{
|
||||
config: config,
|
||||
}, nil
|
||||
case initUsernsSetup:
|
||||
return &linuxUsernsSideCar{
|
||||
config: config,
|
||||
}, nil
|
||||
case initStandard:
|
||||
return &linuxStandardInit{
|
||||
config: config,
|
||||
|
||||
@@ -4,13 +4,11 @@ package libcontainer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libcontainer/cgroups"
|
||||
"github.com/docker/libcontainer/system"
|
||||
)
|
||||
@@ -145,28 +143,12 @@ func (p *initProcess) start() error {
|
||||
if err := p.createNetworkInterfaces(); err != nil {
|
||||
return newSystemError(err)
|
||||
}
|
||||
// Start the setup process to setup the init process
|
||||
if p.cmd.SysProcAttr.Cloneflags&syscall.CLONE_NEWUSER != 0 {
|
||||
parent, err := p.newUsernsSetupProcess()
|
||||
if err != nil {
|
||||
return newSystemError(err)
|
||||
}
|
||||
if err := parent.start(); err != nil {
|
||||
if err := parent.terminate(); err != nil {
|
||||
log.Warn(err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
if _, err := parent.wait(); err != nil {
|
||||
return newSystemError(err)
|
||||
}
|
||||
}
|
||||
if err := p.sendConfig(); err != nil {
|
||||
return newSystemError(err)
|
||||
}
|
||||
// wait for the child process to fully complete and receive an error message
|
||||
// if one was encoutered
|
||||
var ierr *initError
|
||||
var ierr *genericError
|
||||
if err := json.NewDecoder(p.parentPipe).Decode(&ierr); err != nil && err != io.EOF {
|
||||
return newSystemError(err)
|
||||
}
|
||||
@@ -229,26 +211,6 @@ func (p *initProcess) createNetworkInterfaces() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *initProcess) newUsernsSetupProcess() (parentProcess, error) {
|
||||
parentPipe, childPipe, err := newPipe()
|
||||
if err != nil {
|
||||
return nil, newSystemError(err)
|
||||
}
|
||||
cmd := exec.Command(p.cmd.Args[0], p.cmd.Args[1:]...)
|
||||
cmd.ExtraFiles = []*os.File{childPipe}
|
||||
cmd.Dir = p.cmd.Dir
|
||||
cmd.Env = append(cmd.Env,
|
||||
fmt.Sprintf("_LIBCONTAINER_INITPID=%d", p.pid()),
|
||||
fmt.Sprintf("_LIBCONTAINER_INITTYPE=userns_setup"),
|
||||
)
|
||||
return &setnsProcess{
|
||||
cmd: cmd,
|
||||
childPipe: childPipe,
|
||||
parentPipe: parentPipe,
|
||||
config: p.config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *initProcess) signal(s os.Signal) error {
|
||||
return p.cmd.Process.Signal(s)
|
||||
}
|
||||
|
||||
@@ -199,30 +199,27 @@ func createDevices(config *configs.Config) error {
|
||||
|
||||
// Creates the device node in the rootfs of the container.
|
||||
func createDeviceNode(rootfs string, node *configs.Device) error {
|
||||
var (
|
||||
dest = filepath.Join(rootfs, node.Path)
|
||||
parent = filepath.Dir(dest)
|
||||
)
|
||||
if err := os.MkdirAll(parent, 0755); err != nil {
|
||||
dest := filepath.Join(rootfs, node.Path)
|
||||
if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := mknodDevice(dest, node); err != nil {
|
||||
if os.IsExist(err) {
|
||||
return nil
|
||||
}
|
||||
// containers running in a user namespace are not allowed to mknod
|
||||
// devices so we can just bind mount it from the host.
|
||||
if err == syscall.EPERM {
|
||||
f, err := os.Create(dest)
|
||||
if err != nil {
|
||||
if os.IsExist(err) {
|
||||
return nil
|
||||
}
|
||||
if err != syscall.EPERM {
|
||||
return err
|
||||
}
|
||||
f.Close()
|
||||
return syscall.Mount(node.Path, dest, "bind", syscall.MS_BIND, "")
|
||||
// containers running in a user namespace are not allowed to mknod
|
||||
// devices so we can just bind mount it from the host.
|
||||
f, err := os.Create(dest)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
return err
|
||||
}
|
||||
if f != nil {
|
||||
f.Close()
|
||||
}
|
||||
return syscall.Mount(node.Path, dest, "bind", syscall.MS_BIND, "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package libcontainer
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/docker/libcontainer/apparmor"
|
||||
"github.com/docker/libcontainer/configs"
|
||||
"github.com/docker/libcontainer/label"
|
||||
"github.com/docker/libcontainer/system"
|
||||
)
|
||||
|
||||
type linuxUsernsInit struct {
|
||||
config *initConfig
|
||||
}
|
||||
|
||||
func (l *linuxUsernsInit) Init() error {
|
||||
// join any namespaces via a path to the namespace fd if provided
|
||||
if err := joinExistingNamespaces(l.config.Config.Namespaces); err != nil {
|
||||
return newSystemError(err)
|
||||
}
|
||||
consolePath := l.config.Config.Console
|
||||
if consolePath != "" {
|
||||
// We use the containerConsolePath here, because the console has already been
|
||||
// setup by the side car process for the user namespace scenario.
|
||||
console := newConsoleFromPath(consolePath)
|
||||
if err := console.dupStdio(); err != nil {
|
||||
return newSystemError(err)
|
||||
}
|
||||
}
|
||||
if _, err := syscall.Setsid(); err != nil {
|
||||
return newSystemError(err)
|
||||
}
|
||||
if consolePath != "" {
|
||||
if err := system.Setctty(); err != nil {
|
||||
return newSystemError(err)
|
||||
}
|
||||
}
|
||||
if l.config.Cwd == "" {
|
||||
l.config.Cwd = "/"
|
||||
}
|
||||
if err := setupRlimits(l.config.Config); err != nil {
|
||||
return newSystemError(err)
|
||||
}
|
||||
// InitializeMountNamespace() can be executed only for a new mount namespace
|
||||
if l.config.Config.Namespaces.Contains(configs.NEWNS) {
|
||||
if err := setupRootfs(l.config.Config); err != nil {
|
||||
return newSystemError(err)
|
||||
}
|
||||
}
|
||||
if hostname := l.config.Config.Hostname; hostname != "" {
|
||||
if err := syscall.Sethostname([]byte(hostname)); err != nil {
|
||||
return newSystemError(err)
|
||||
}
|
||||
}
|
||||
if err := apparmor.ApplyProfile(l.config.Config.AppArmorProfile); err != nil {
|
||||
return newSystemError(err)
|
||||
}
|
||||
if err := label.SetProcessLabel(l.config.Config.ProcessLabel); err != nil {
|
||||
return newSystemError(err)
|
||||
}
|
||||
for _, path := range l.config.Config.ReadonlyPaths {
|
||||
if err := remountReadonly(path); err != nil {
|
||||
return newSystemError(err)
|
||||
}
|
||||
}
|
||||
for _, path := range l.config.Config.MaskPaths {
|
||||
if err := maskFile(path); err != nil {
|
||||
return newSystemError(err)
|
||||
}
|
||||
}
|
||||
pdeath, err := system.GetParentDeathSignal()
|
||||
if err != nil {
|
||||
return newSystemError(err)
|
||||
}
|
||||
if err := finalizeNamespace(l.config); err != nil {
|
||||
return newSystemError(err)
|
||||
}
|
||||
// finalizeNamespace can change user/group which clears the parent death
|
||||
// signal, so we restore it here.
|
||||
if err := pdeath.Restore(); err != nil {
|
||||
return newSystemError(err)
|
||||
}
|
||||
// Signal self if parent is already dead. Does nothing if running in a new
|
||||
// PID namespace, as Getppid will always return 0.
|
||||
if syscall.Getppid() == 1 {
|
||||
return syscall.Kill(syscall.Getpid(), syscall.SIGKILL)
|
||||
}
|
||||
return system.Execv(l.config.Args[0], l.config.Args[0:], l.config.Env)
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package libcontainer
|
||||
|
||||
// linuxUsernsSideCar is run to setup mounts and networking related operations
|
||||
// for a user namespace enabled process as a user namespace root doesn't
|
||||
// have permissions to perform these operations.
|
||||
// The setup process joins all the namespaces of user namespace enabled init
|
||||
// except the user namespace, so it run as root in the root user namespace
|
||||
// to perform these operations.
|
||||
type linuxUsernsSideCar struct {
|
||||
config *initConfig
|
||||
}
|
||||
|
||||
func (l *linuxUsernsSideCar) Init() error {
|
||||
if err := setupNetwork(l.config); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := setupRoute(l.config.Config); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -33,6 +33,7 @@ var createFlags = []cli.Flag{
|
||||
cli.StringFlag{Name: "mount-label", Usage: "set the mount label"},
|
||||
cli.StringFlag{Name: "rootfs", Usage: "set the rootfs"},
|
||||
cli.IntFlag{Name: "userns-root-uid", Usage: "set the user namespace root uid"},
|
||||
cli.StringFlag{Name: "hostname", Value: "nsinit", Usage: "hostname value for the container"},
|
||||
cli.StringFlag{Name: "net", Value: "", Usage: "network namespace"},
|
||||
cli.StringFlag{Name: "ipc", Value: "", Usage: "ipc namespace"},
|
||||
cli.StringFlag{Name: "pid", Value: "", Usage: "pid namespace"},
|
||||
@@ -149,7 +150,7 @@ func modify(config *configs.Config, context *cli.Context) {
|
||||
if !config.Namespaces.Contains(value) {
|
||||
config.Namespaces.Add(value, "")
|
||||
}
|
||||
if v == "net" {
|
||||
if flag == "net" {
|
||||
config.Networks = []*configs.Network{
|
||||
{
|
||||
Type: "loopback",
|
||||
@@ -158,6 +159,9 @@ func modify(config *configs.Config, context *cli.Context) {
|
||||
},
|
||||
}
|
||||
}
|
||||
if flag == "uts" {
|
||||
config.Hostname = context.String("hostname")
|
||||
}
|
||||
default:
|
||||
config.Namespaces.Remove(value)
|
||||
config.Namespaces.Add(value, v)
|
||||
@@ -219,7 +223,6 @@ func getTemplate() *configs.Config {
|
||||
AllowedDevices: configs.DefaultAllowedDevices,
|
||||
},
|
||||
Devices: configs.DefaultAutoCreatedDevices,
|
||||
Hostname: "nsinit",
|
||||
MaskPaths: []string{
|
||||
"/proc/kcore",
|
||||
},
|
||||
|
||||
@@ -36,18 +36,21 @@ func execAction(context *cli.Context) {
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
tty, err := newTty(context)
|
||||
config, err := loadConfig(context)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
rootuid, err := config.HostUID()
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
tty, err := newTty(context, rootuid)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
created := false
|
||||
container, err := factory.Load(context.String("id"))
|
||||
if err != nil {
|
||||
config, err := loadConfig(context)
|
||||
if err != nil {
|
||||
tty.Close()
|
||||
fatal(err)
|
||||
}
|
||||
if tty.console != nil {
|
||||
config.Console = tty.console.Path()
|
||||
}
|
||||
|
||||
@@ -9,10 +9,9 @@ import (
|
||||
"github.com/docker/libcontainer"
|
||||
)
|
||||
|
||||
func newTty(context *cli.Context) (*tty, error) {
|
||||
func newTty(context *cli.Context, rootuid int) (*tty, error) {
|
||||
if context.Bool("tty") {
|
||||
rootid := context.Int("userns-root-uid")
|
||||
console, err := libcontainer.NewConsole(rootid, rootid)
|
||||
console, err := libcontainer.NewConsole(rootuid, rootuid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user