diff --git a/execdriver/chroot/driver.go b/execdriver/chroot/driver.go index 6244d2a93f..1f98bba0ac 100644 --- a/execdriver/chroot/driver.go +++ b/execdriver/chroot/driver.go @@ -2,9 +2,29 @@ package chroot import ( "github.com/dotcloud/docker/execdriver" + "github.com/dotcloud/docker/mount" + "os" "os/exec" ) +const DriverName = "chroot" + +func init() { + execdriver.RegisterDockerInitFct(DriverName, func(args *execdriver.DockerInitArgs) error { + if err := mount.ForceMount("proc", "proc", "proc", ""); err != nil { + return err + } + defer mount.ForceUnmount("proc") + cmd := exec.Command(args.Args[0], args.Args[1:]...) + + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + cmd.Stdin = os.Stdin + + return cmd.Run() + }) +} + type driver struct { } @@ -13,7 +33,7 @@ func NewDriver() (*driver, error) { } func (d *driver) Name() string { - return "chroot" + return DriverName } func (d *driver) Run(c *execdriver.Process, startCallback execdriver.StartCallback) (int, error) { diff --git a/execdriver/driver.go b/execdriver/driver.go index d68123d9ba..ca0a2991f5 100644 --- a/execdriver/driver.go +++ b/execdriver/driver.go @@ -7,11 +7,49 @@ import ( ) var ( - ErrNotRunning = errors.New("Process could not be started") - ErrWaitTimeoutReached = errors.New("Wait timeout reached") + ErrNotRunning = errors.New("Process could not be started") + ErrWaitTimeoutReached = errors.New("Wait timeout reached") + ErrDriverAlreadyRegistered = errors.New("A driver already registered this docker init function") + ErrDriverNotFound = errors.New("The requested docker init has not been found") ) -type StartCallback func(*Process) +var dockerInitFcts map[string]DockerInitFct + +type ( + StartCallback func(*Process) + DockerInitFct func(i *DockerInitArgs) error +) + +func RegisterDockerInitFct(name string, fct DockerInitFct) error { + if dockerInitFcts == nil { + dockerInitFcts = make(map[string]DockerInitFct) + } + if _, ok := dockerInitFcts[name]; ok { + return ErrDriverAlreadyRegistered + } + dockerInitFcts[name] = fct + return nil +} + +func GetDockerInitFct(name string) (DockerInitFct, error) { + fct, ok := dockerInitFcts[name] + if !ok { + return nil, ErrDriverNotFound + } + return fct, nil +} + +type DockerInitArgs struct { + User string + Gateway string + Ip string + WorkDir string + Privileged bool + Env []string + Args []string + Mtu int + Driver string +} type Driver interface { Run(c *Process, startCallback StartCallback) (int, error) // Run executes the process and blocks until the process exits and returns the exit code diff --git a/execdriver/lxc/driver.go b/execdriver/lxc/driver.go index 3939841320..61fc99fae0 100644 --- a/execdriver/lxc/driver.go +++ b/execdriver/lxc/driver.go @@ -5,14 +5,50 @@ import ( "github.com/dotcloud/docker/execdriver" "github.com/dotcloud/docker/utils" "io/ioutil" + "log" "os" "os/exec" "path" "strconv" "strings" + "syscall" "time" ) +const DriverName = "lxc" + +func init() { + execdriver.RegisterDockerInitFct(DriverName, func(args *execdriver.DockerInitArgs) error { + if err := setupHostname(args); err != nil { + return err + } + + if err := setupNetworking(args); err != nil { + return err + } + + if err := setupCapabilities(args); err != nil { + return err + } + if err := setupWorkingDirectory(args); err != nil { + return err + } + + if err := changeUser(args); err != nil { + return err + } + path, err := exec.LookPath(args.Args[0]) + if err != nil { + log.Printf("Unable to locate %v", args.Args[0]) + os.Exit(127) + } + if err := syscall.Exec(path, args.Args, os.Environ()); err != nil { + panic(err) + } + panic("Unreachable") + }) +} + type driver struct { root string // root path for the driver to use apparmor bool @@ -32,7 +68,7 @@ func NewDriver(root string, apparmor bool) (*driver, error) { } func (d *driver) Name() string { - return "lxc" + return DriverName } func (d *driver) Run(c *execdriver.Process, startCallback execdriver.StartCallback) (int, error) { diff --git a/execdriver/lxc/init.go b/execdriver/lxc/init.go new file mode 100644 index 0000000000..dec33e3ad6 --- /dev/null +++ b/execdriver/lxc/init.go @@ -0,0 +1,153 @@ +package lxc + +import ( + "fmt" + "github.com/dotcloud/docker/execdriver" + "github.com/dotcloud/docker/pkg/netlink" + "github.com/dotcloud/docker/utils" + "github.com/syndtr/gocapability/capability" + "net" + "os" + "strconv" + "strings" + "syscall" +) + +func setupHostname(args *execdriver.DockerInitArgs) error { + hostname := getEnv(args, "HOSTNAME") + if hostname == "" { + return nil + } + return setHostname(hostname) +} + +// Setup networking +func setupNetworking(args *execdriver.DockerInitArgs) error { + if args.Ip != "" { + // eth0 + iface, err := net.InterfaceByName("eth0") + if err != nil { + return fmt.Errorf("Unable to set up networking: %v", err) + } + ip, ipNet, err := net.ParseCIDR(args.Ip) + if err != nil { + return fmt.Errorf("Unable to set up networking: %v", err) + } + if err := netlink.NetworkLinkAddIp(iface, ip, ipNet); err != nil { + return fmt.Errorf("Unable to set up networking: %v", err) + } + if err := netlink.NetworkSetMTU(iface, args.Mtu); err != nil { + return fmt.Errorf("Unable to set MTU: %v", err) + } + if err := netlink.NetworkLinkUp(iface); err != nil { + return fmt.Errorf("Unable to set up networking: %v", err) + } + + // loopback + iface, err = net.InterfaceByName("lo") + if err != nil { + return fmt.Errorf("Unable to set up networking: %v", err) + } + if err := netlink.NetworkLinkUp(iface); err != nil { + return fmt.Errorf("Unable to set up networking: %v", err) + } + } + if args.Gateway != "" { + gw := net.ParseIP(args.Gateway) + if gw == nil { + return fmt.Errorf("Unable to set up networking, %s is not a valid gateway IP", args.Gateway) + } + + if err := netlink.AddDefaultGw(gw); err != nil { + return fmt.Errorf("Unable to set up networking: %v", err) + } + } + + return nil +} + +// Setup working directory +func setupWorkingDirectory(args *execdriver.DockerInitArgs) error { + if args.WorkDir == "" { + return nil + } + if err := syscall.Chdir(args.WorkDir); err != nil { + return fmt.Errorf("Unable to change dir to %v: %v", args.WorkDir, err) + } + return nil +} + +// Takes care of dropping privileges to the desired user +func changeUser(args *execdriver.DockerInitArgs) error { + if args.User == "" { + return nil + } + userent, err := utils.UserLookup(args.User) + if err != nil { + return fmt.Errorf("Unable to find user %v: %v", args.User, err) + } + + uid, err := strconv.Atoi(userent.Uid) + if err != nil { + return fmt.Errorf("Invalid uid: %v", userent.Uid) + } + gid, err := strconv.Atoi(userent.Gid) + if err != nil { + return fmt.Errorf("Invalid gid: %v", userent.Gid) + } + + if err := syscall.Setgid(gid); err != nil { + return fmt.Errorf("setgid failed: %v", err) + } + if err := syscall.Setuid(uid); err != nil { + return fmt.Errorf("setuid failed: %v", err) + } + + return nil +} + +func setupCapabilities(args *execdriver.DockerInitArgs) error { + + if args.Privileged { + return nil + } + + drop := []capability.Cap{ + capability.CAP_SETPCAP, + capability.CAP_SYS_MODULE, + capability.CAP_SYS_RAWIO, + capability.CAP_SYS_PACCT, + capability.CAP_SYS_ADMIN, + capability.CAP_SYS_NICE, + capability.CAP_SYS_RESOURCE, + capability.CAP_SYS_TIME, + capability.CAP_SYS_TTY_CONFIG, + capability.CAP_MKNOD, + capability.CAP_AUDIT_WRITE, + capability.CAP_AUDIT_CONTROL, + capability.CAP_MAC_OVERRIDE, + capability.CAP_MAC_ADMIN, + } + + c, err := capability.NewPid(os.Getpid()) + if err != nil { + return err + } + + c.Unset(capability.CAPS|capability.BOUNDS, drop...) + + if err := c.Apply(capability.CAPS | capability.BOUNDS); err != nil { + return err + } + return nil +} + +func getEnv(args *execdriver.DockerInitArgs, key string) string { + for _, kv := range args.Env { + parts := strings.SplitN(kv, "=", 2) + if parts[0] == key && len(parts) == 2 { + return parts[1] + } + } + return "" +} diff --git a/sysinit/sysinit_darwin.go b/execdriver/lxc/lxc_init_darwin.go similarity index 83% rename from sysinit/sysinit_darwin.go rename to execdriver/lxc/lxc_init_darwin.go index 64566afb3c..c066fead93 100644 --- a/sysinit/sysinit_darwin.go +++ b/execdriver/lxc/lxc_init_darwin.go @@ -1,4 +1,4 @@ -package sysinit +package lxc func setHostname(hostname string) error { panic("Not supported on darwin") diff --git a/sysinit/sysinit_linux.go b/execdriver/lxc/lxc_init_linux.go similarity index 87% rename from sysinit/sysinit_linux.go rename to execdriver/lxc/lxc_init_linux.go index d18d2fab8b..b0055c3668 100644 --- a/sysinit/sysinit_linux.go +++ b/execdriver/lxc/lxc_init_linux.go @@ -1,4 +1,4 @@ -package sysinit +package lxc import ( "syscall" diff --git a/sysinit/sysinit.go b/sysinit/sysinit.go index 6701717fe3..96339018ce 100644 --- a/sysinit/sysinit.go +++ b/sysinit/sysinit.go @@ -4,169 +4,17 @@ import ( "encoding/json" "flag" "fmt" - "github.com/dotcloud/docker/mount" - "github.com/dotcloud/docker/pkg/netlink" - "github.com/dotcloud/docker/utils" - "github.com/syndtr/gocapability/capability" + "github.com/dotcloud/docker/execdriver" "io/ioutil" "log" - "net" "os" - "os/exec" - "strconv" "strings" - "syscall" ) -type DockerInitArgs struct { - user string - gateway string - ip string - workDir string - privileged bool - env []string - args []string - mtu int - driver string -} - -func setupHostname(args *DockerInitArgs) error { - hostname := getEnv(args, "HOSTNAME") - if hostname == "" { - return nil - } - return setHostname(hostname) -} - -// Setup networking -func setupNetworking(args *DockerInitArgs) error { - if args.ip != "" { - // eth0 - iface, err := net.InterfaceByName("eth0") - if err != nil { - return fmt.Errorf("Unable to set up networking: %v", err) - } - ip, ipNet, err := net.ParseCIDR(args.ip) - if err != nil { - return fmt.Errorf("Unable to set up networking: %v", err) - } - if err := netlink.NetworkLinkAddIp(iface, ip, ipNet); err != nil { - return fmt.Errorf("Unable to set up networking: %v", err) - } - if err := netlink.NetworkSetMTU(iface, args.mtu); err != nil { - return fmt.Errorf("Unable to set MTU: %v", err) - } - if err := netlink.NetworkLinkUp(iface); err != nil { - return fmt.Errorf("Unable to set up networking: %v", err) - } - - // loopback - iface, err = net.InterfaceByName("lo") - if err != nil { - return fmt.Errorf("Unable to set up networking: %v", err) - } - if err := netlink.NetworkLinkUp(iface); err != nil { - return fmt.Errorf("Unable to set up networking: %v", err) - } - } - if args.gateway != "" { - gw := net.ParseIP(args.gateway) - if gw == nil { - return fmt.Errorf("Unable to set up networking, %s is not a valid gateway IP", args.gateway) - } - - if err := netlink.AddDefaultGw(gw); err != nil { - return fmt.Errorf("Unable to set up networking: %v", err) - } - } - - return nil -} - -// Setup working directory -func setupWorkingDirectory(args *DockerInitArgs) error { - if args.workDir == "" { - return nil - } - if err := syscall.Chdir(args.workDir); err != nil { - return fmt.Errorf("Unable to change dir to %v: %v", args.workDir, err) - } - return nil -} - -func setupMounts(args *DockerInitArgs) error { - return mount.ForceMount("proc", "proc", "proc", "") -} - -// Takes care of dropping privileges to the desired user -func changeUser(args *DockerInitArgs) error { - if args.user == "" { - return nil - } - userent, err := utils.UserLookup(args.user) - if err != nil { - return fmt.Errorf("Unable to find user %v: %v", args.user, err) - } - - uid, err := strconv.Atoi(userent.Uid) - if err != nil { - return fmt.Errorf("Invalid uid: %v", userent.Uid) - } - gid, err := strconv.Atoi(userent.Gid) - if err != nil { - return fmt.Errorf("Invalid gid: %v", userent.Gid) - } - - if err := syscall.Setgid(gid); err != nil { - return fmt.Errorf("setgid failed: %v", err) - } - if err := syscall.Setuid(uid); err != nil { - return fmt.Errorf("setuid failed: %v", err) - } - - return nil -} - -func setupCapabilities(args *DockerInitArgs) error { - - if args.privileged { - return nil - } - - drop := []capability.Cap{ - capability.CAP_SETPCAP, - capability.CAP_SYS_MODULE, - capability.CAP_SYS_RAWIO, - capability.CAP_SYS_PACCT, - capability.CAP_SYS_ADMIN, - capability.CAP_SYS_NICE, - capability.CAP_SYS_RESOURCE, - capability.CAP_SYS_TIME, - capability.CAP_SYS_TTY_CONFIG, - capability.CAP_MKNOD, - capability.CAP_AUDIT_WRITE, - capability.CAP_AUDIT_CONTROL, - capability.CAP_MAC_OVERRIDE, - capability.CAP_MAC_ADMIN, - } - - c, err := capability.NewPid(os.Getpid()) - if err != nil { - return err - } - - c.Unset(capability.CAPS|capability.BOUNDS, drop...) - - if err := c.Apply(capability.CAPS | capability.BOUNDS); err != nil { - return err - } - return nil -} - // Clear environment pollution introduced by lxc-start -func setupEnv(args *DockerInitArgs) { +func setupEnv(args *execdriver.DockerInitArgs) { os.Clearenv() - for _, kv := range args.env { + for _, kv := range args.Env { parts := strings.SplitN(kv, "=", 2) if len(parts) == 1 { parts = append(parts, "") @@ -175,66 +23,18 @@ func setupEnv(args *DockerInitArgs) { } } -func getEnv(args *DockerInitArgs, key string) string { - for _, kv := range args.env { - parts := strings.SplitN(kv, "=", 2) - if parts[0] == key && len(parts) == 2 { - return parts[1] - } - } - return "" -} - -func executeProgram(args *DockerInitArgs) error { +func executeProgram(args *execdriver.DockerInitArgs) error { setupEnv(args) - - if args.driver == "lxc" { - if err := setupHostname(args); err != nil { - return err - } - - if err := setupNetworking(args); err != nil { - return err - } - - if err := setupCapabilities(args); err != nil { - return err - } - if err := setupWorkingDirectory(args); err != nil { - return err - } - - if err := changeUser(args); err != nil { - return err - } - } else if args.driver == "chroot" { - if err := setupMounts(args); err != nil { - return err - } - defer mount.ForceUnmount("proc") - } - - path, err := exec.LookPath(args.args[0]) + dockerInitFct, err := execdriver.GetDockerInitFct(args.Driver) if err != nil { - log.Printf("Unable to locate %v", args.args[0]) - os.Exit(127) + panic(err) } + return dockerInitFct(args) - if args.driver == "lxc" { - if err := syscall.Exec(path, args.args, os.Environ()); err != nil { - panic(err) - } + if args.Driver == "lxc" { // Will never reach - } else if args.driver == "chroot" { - cmd := exec.Command(path, args.args[1:]...) - - cmd.Stderr = os.Stderr - cmd.Stdout = os.Stdout - cmd.Stdin = os.Stdin - - return cmd.Run() + } else if args.Driver == "chroot" { } - panic("Should not be here") return nil } @@ -271,16 +71,16 @@ func SysInit() { // Propagate the plugin-specific container env variable env = append(env, "container="+os.Getenv("container")) - args := &DockerInitArgs{ - user: *user, - gateway: *gateway, - ip: *ip, - workDir: *workDir, - privileged: *privileged, - env: env, - args: flag.Args(), - mtu: *mtu, - driver: *driver, + args := &execdriver.DockerInitArgs{ + User: *user, + Gateway: *gateway, + Ip: *ip, + WorkDir: *workDir, + Privileged: *privileged, + Env: env, + Args: flag.Args(), + Mtu: *mtu, + Driver: *driver, } if err := executeProgram(args); err != nil {