diff --git a/container.go b/container.go index c1c98c5a07..264d404570 100644 --- a/container.go +++ b/container.go @@ -49,6 +49,7 @@ type Container struct { type Config struct { Hostname string + User string Ram int64 Tty bool // Attach standard streams to a tty, including stdin if it is not closed. OpenStdin bool // Open stdin @@ -270,10 +271,12 @@ func (container *Container) Start() error { "-f", container.lxcConfigPath, "--", "/sbin/init", - container.Path, } + if container.Config.User != "" { + params = append(params, "-u", container.Config.User) + } + params = append(params, "--", container.Path) params = append(params, container.Args...) - container.cmd = exec.Command("/usr/bin/lxc-start", params...) var err error diff --git a/container_test.go b/container_test.go index 790c03d8e1..6e1b91ba40 100644 --- a/container_test.go +++ b/container_test.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "io/ioutil" + "strings" "testing" "time" ) @@ -186,6 +187,121 @@ func TestExitCode(t *testing.T) { } } +func TestUser(t *testing.T) { + docker, err := newTestDocker() + if err != nil { + t.Fatal(err) + } + + // Default user must be root + container, err := docker.Create( + "user_default", + "id", + []string{}, + []string{"/var/lib/docker/images/ubuntu"}, + &Config{}, + ) + if err != nil { + t.Fatal(err) + } + defer docker.Destroy(container) + output, err := container.Output() + if err != nil { + t.Fatal(err) + } + if !strings.Contains(string(output), "uid=0(root) gid=0(root)") { + t.Error(string(output)) + } + + // Set a username + container, err = docker.Create( + "user_root", + "id", + []string{}, + []string{"/var/lib/docker/images/ubuntu"}, + &Config{ + User: "root", + }, + ) + if err != nil { + t.Fatal(err) + } + defer docker.Destroy(container) + output, err = container.Output() + if err != nil { + t.Fatal(err) + } + if !strings.Contains(string(output), "uid=0(root) gid=0(root)") { + t.Error(string(output)) + } + + // Set a UID + container, err = docker.Create( + "user_uid0", + "id", + []string{}, + []string{"/var/lib/docker/images/ubuntu"}, + &Config{ + User: "0", + }, + ) + if err != nil { + t.Fatal(err) + } + defer docker.Destroy(container) + output, err = container.Output() + if err != nil { + t.Fatal(err) + } + if !strings.Contains(string(output), "uid=0(root) gid=0(root)") { + t.Error(string(output)) + } + + // Set a different user by uid + container, err = docker.Create( + "user_uid1", + "id", + []string{}, + []string{"/var/lib/docker/images/ubuntu"}, + &Config{ + User: "1", + }, + ) + if err != nil { + t.Fatal(err) + } + defer docker.Destroy(container) + output, err = container.Output() + if err != nil { + t.Fatal(err) + } + if !strings.Contains(string(output), "uid=1(daemon) gid=1(daemon)") { + t.Error(string(output)) + } + + // Set a different user by username + container, err = docker.Create( + "user_daemon", + "id", + []string{}, + []string{"/var/lib/docker/images/ubuntu"}, + &Config{ + User: "daemon", + }, + ) + if err != nil { + t.Fatal(err) + } + defer docker.Destroy(container) + output, err = container.Output() + if err != nil { + t.Fatal(err) + } + if !strings.Contains(string(output), "uid=1(daemon) gid=1(daemon)") { + t.Error(string(output)) + } +} + func TestMultipleContainers(t *testing.T) { docker, err := newTestDocker() if err != nil { diff --git a/dockerd/dockerd.go b/dockerd/dockerd.go index 41a81f19d5..d8bd933af5 100644 --- a/dockerd/dockerd.go +++ b/dockerd/dockerd.go @@ -611,10 +611,10 @@ func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string return errors.New("No such container: " + cmd.Arg(0)) } -func (srv *Server) CreateContainer(img *image.Image, tty bool, openStdin bool, comment string, cmd string, args ...string) (*docker.Container, error) { +func (srv *Server) CreateContainer(img *image.Image, user string, tty bool, openStdin bool, comment string, cmd string, args ...string) (*docker.Container, error) { id := future.RandomId()[:8] container, err := srv.containers.Create(id, cmd, args, img.Layers, - &docker.Config{Hostname: id, Tty: tty, OpenStdin: openStdin}) + &docker.Config{Hostname: id, User: user, Tty: tty, OpenStdin: openStdin}) if err != nil { return nil, err } @@ -680,6 +680,7 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) fl_attach := cmd.Bool("a", false, "Attach stdin and stdout") fl_stdin := cmd.Bool("i", false, "Keep stdin open even if not attached") fl_tty := cmd.Bool("t", false, "Allocate a pseudo-tty") + fl_user := cmd.String("u", "0", "Username or UID") fl_comment := cmd.String("c", "", "Comment") if err := cmd.Parse(args); err != nil { return nil @@ -706,7 +707,7 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) return errors.New("No such image: " + name) } // Create new container - container, err := srv.CreateContainer(img, *fl_tty, *fl_stdin, *fl_comment, cmdline[0], cmdline[1:]...) + container, err := srv.CreateContainer(img, *fl_user, *fl_tty, *fl_stdin, *fl_comment, cmdline[0], cmdline[1:]...) if err != nil { return errors.New("Error creating container: " + err.Error()) } diff --git a/sysinit.go b/sysinit.go index 4feef57639..99bf43d25f 100644 --- a/sysinit.go +++ b/sysinit.go @@ -1,13 +1,58 @@ package docker import ( + "flag" "fmt" "log" "os" "os/exec" + "os/user" + "strconv" "syscall" ) +// Takes care of dropping privileges to the desired user +func changeUser(u string) { + if u == "" { + return + } + userent, err := user.LookupId(u) + if err != nil { + userent, err = user.Lookup(u) + } + if err != nil { + log.Fatalf("Unable to find user %v: %v", u, err) + } + + uid, err := strconv.Atoi(userent.Uid) + if err != nil { + log.Fatalf("Invalid uid: %v", userent.Uid) + } + gid, err := strconv.Atoi(userent.Gid) + if err != nil { + log.Fatalf("Invalid gid: %v", userent.Gid) + } + + if err := syscall.Setgid(gid); err != nil { + log.Fatalf("setgid failed: %v", err) + } + if err := syscall.Setuid(uid); err != nil { + log.Fatalf("setuid failed: %v", err) + } +} + +func executeProgram(name string, args []string) { + path, err := exec.LookPath(name) + if err != nil { + log.Printf("Unable to locate %v", name) + os.Exit(127) + } + + if err := syscall.Exec(path, args, os.Environ()); err != nil { + panic(err) + } +} + // Sys Init code // This code is run INSIDE the container and is responsible for setting // up the environment before running the actual process @@ -16,14 +61,9 @@ func SysInit() { fmt.Println("You should not invoke docker-init manually") os.Exit(1) } + var u = flag.String("u", "", "username or uid") - 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) - } + flag.Parse() + changeUser(*u) + executeProgram(flag.Arg(0), flag.Args()) }