Bläddra i källkod

Implemented a self-injecting process wrapper that runs inside the container

- Before starting the container, docker injects itself inside the container by mount binding the dockerd binary into /sbin/init
- Instead of running the user process directly inside the container, we run /sbin/init targetprocess [args...]
- When docker is run as /sbin/init (e.g. argv[0] == "/sbin/init"), then its own sys init code kicks in
- The sys init code will be responsible for setting up the process environment prior to its execution (setuid, networking, ...).
- Finally, docker's sys init will exec() the container's process, thus replacing itself with the target binary (which will be running as pid 1)
Andrea Luzzardi 12 år sedan
förälder
incheckning
58a2294260
5 ändrade filer med 53 tillägg och 0 borttagningar
  1. 9 0
      container.go
  2. 7 0
      docker_test.go
  3. 5 0
      dockerd/dockerd.go
  4. 3 0
      lxc_template.go
  5. 29 0
      sysinit.go

+ 9 - 0
container.go

@@ -16,6 +16,12 @@ import (
 	"time"
 )
 
+var sysInitPath string
+
+func init() {
+	sysInitPath = SelfPath()
+}
+
 type Container struct {
 	Id   string
 	Root string
@@ -29,6 +35,7 @@ type Container struct {
 	Filesystem *Filesystem
 	State      *State
 
+	SysInitPath   string
 	lxcConfigPath string
 	cmd           *exec.Cmd
 	stdout        *writeBroadcaster
@@ -58,6 +65,7 @@ func createContainer(id string, root string, command string, args []string, laye
 		Filesystem: newFilesystem(path.Join(root, "rootfs"), path.Join(root, "rw"), layers),
 		State:      newState(),
 
+		SysInitPath:   sysInitPath,
 		lxcConfigPath: path.Join(root, "config.lxc"),
 		stdout:        newWriteBroadcaster(),
 		stderr:        newWriteBroadcaster(),
@@ -261,6 +269,7 @@ func (container *Container) Start() error {
 		"-n", container.Id,
 		"-f", container.lxcConfigPath,
 		"--",
+		"/sbin/init",
 		container.Path,
 	}
 	params = append(params, container.Args...)

+ 7 - 0
docker_test.go

@@ -6,6 +6,13 @@ import (
 	"testing"
 )
 
+// Hack to run sys init during unit testing
+func init() {
+	if SelfPath() == "/sbin/init" {
+		SysInit()
+	}
+}
+
 func newTestDocker() (*Docker, error) {
 	root, err := ioutil.TempDir("", "docker-test")
 	if err != nil {

+ 5 - 0
dockerd/dockerd.go

@@ -705,6 +705,11 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
 }
 
 func main() {
+	if docker.SelfPath() == "/sbin/init" {
+		// Running in init mode
+		docker.SysInit()
+		return
+	}
 	future.Seed()
 	flag.Parse()
 	d, err := New()

+ 3 - 0
lxc_template.go

@@ -74,6 +74,9 @@ lxc.mount.entry = devpts {{$ROOTFS}}/dev/pts devpts newinstance,ptmxmode=0666,no
 #lxc.mount.entry = varlock {{$ROOTFS}}/var/lock tmpfs size=1024k,nosuid,nodev,noexec 0 0
 #lxc.mount.entry = shm {{$ROOTFS}}/dev/shm tmpfs size=65536k,nosuid,nodev,noexec 0 0
 
+# Inject docker-init
+lxc.mount.entry = {{.SysInitPath}} {{$ROOTFS}}/sbin/init none bind,ro 0 0
+
 # In order to get a working DNS environment, mount bind (ro) the host's /etc/resolv.conf into the container
 lxc.mount.entry = /etc/resolv.conf {{$ROOTFS}}/etc/resolv.conf none bind,ro 0 0
 

+ 29 - 0
sysinit.go

@@ -0,0 +1,29 @@
+package docker
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"os/exec"
+	"syscall"
+)
+
+// Sys Init code
+// This code is run INSIDE the container and is responsible for setting
+// up the environment before running the actual process
+func SysInit() {
+	if len(os.Args) <= 1 {
+		fmt.Println("You should not invoke docker-init manually")
+		os.Exit(1)
+	}
+
+	path, err := exec.LookPath(os.Args[1])
+	if err != nil {
+		log.Printf("Unable to locate %v", os.Args[1])
+		os.Exit(127)
+	}
+
+	if err := syscall.Exec(path, os.Args[1:], os.Environ()); err != nil {
+		panic(err)
+	}
+}