Browse code

Implemented support to run as a different user (through the -u flag)

Andrea Luzzardi authored on 2013/02/14 10:24:35
Showing 4 changed files
... ...
@@ -49,6 +49,7 @@ type Container struct {
49 49
 
50 50
 type Config struct {
51 51
 	Hostname  string
52
+	User      string
52 53
 	Ram       int64
53 54
 	Tty       bool // Attach standard streams to a tty, including stdin if it is not closed.
54 55
 	OpenStdin bool // Open stdin
... ...
@@ -270,10 +271,12 @@ func (container *Container) Start() error {
270 270
 		"-f", container.lxcConfigPath,
271 271
 		"--",
272 272
 		"/sbin/init",
273
-		container.Path,
274 273
 	}
274
+	if container.Config.User != "" {
275
+		params = append(params, "-u", container.Config.User)
276
+	}
277
+	params = append(params, "--", container.Path)
275 278
 	params = append(params, container.Args...)
276
-
277 279
 	container.cmd = exec.Command("/usr/bin/lxc-start", params...)
278 280
 
279 281
 	var err error
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"fmt"
5 5
 	"io"
6 6
 	"io/ioutil"
7
+	"strings"
7 8
 	"testing"
8 9
 	"time"
9 10
 )
... ...
@@ -186,6 +187,121 @@ func TestExitCode(t *testing.T) {
186 186
 	}
187 187
 }
188 188
 
189
+func TestUser(t *testing.T) {
190
+	docker, err := newTestDocker()
191
+	if err != nil {
192
+		t.Fatal(err)
193
+	}
194
+
195
+	// Default user must be root
196
+	container, err := docker.Create(
197
+		"user_default",
198
+		"id",
199
+		[]string{},
200
+		[]string{"/var/lib/docker/images/ubuntu"},
201
+		&Config{},
202
+	)
203
+	if err != nil {
204
+		t.Fatal(err)
205
+	}
206
+	defer docker.Destroy(container)
207
+	output, err := container.Output()
208
+	if err != nil {
209
+		t.Fatal(err)
210
+	}
211
+	if !strings.Contains(string(output), "uid=0(root) gid=0(root)") {
212
+		t.Error(string(output))
213
+	}
214
+
215
+	// Set a username
216
+	container, err = docker.Create(
217
+		"user_root",
218
+		"id",
219
+		[]string{},
220
+		[]string{"/var/lib/docker/images/ubuntu"},
221
+		&Config{
222
+			User: "root",
223
+		},
224
+	)
225
+	if err != nil {
226
+		t.Fatal(err)
227
+	}
228
+	defer docker.Destroy(container)
229
+	output, err = container.Output()
230
+	if err != nil {
231
+		t.Fatal(err)
232
+	}
233
+	if !strings.Contains(string(output), "uid=0(root) gid=0(root)") {
234
+		t.Error(string(output))
235
+	}
236
+
237
+	// Set a UID
238
+	container, err = docker.Create(
239
+		"user_uid0",
240
+		"id",
241
+		[]string{},
242
+		[]string{"/var/lib/docker/images/ubuntu"},
243
+		&Config{
244
+			User: "0",
245
+		},
246
+	)
247
+	if err != nil {
248
+		t.Fatal(err)
249
+	}
250
+	defer docker.Destroy(container)
251
+	output, err = container.Output()
252
+	if err != nil {
253
+		t.Fatal(err)
254
+	}
255
+	if !strings.Contains(string(output), "uid=0(root) gid=0(root)") {
256
+		t.Error(string(output))
257
+	}
258
+
259
+	// Set a different user by uid
260
+	container, err = docker.Create(
261
+		"user_uid1",
262
+		"id",
263
+		[]string{},
264
+		[]string{"/var/lib/docker/images/ubuntu"},
265
+		&Config{
266
+			User: "1",
267
+		},
268
+	)
269
+	if err != nil {
270
+		t.Fatal(err)
271
+	}
272
+	defer docker.Destroy(container)
273
+	output, err = container.Output()
274
+	if err != nil {
275
+		t.Fatal(err)
276
+	}
277
+	if !strings.Contains(string(output), "uid=1(daemon) gid=1(daemon)") {
278
+		t.Error(string(output))
279
+	}
280
+
281
+	// Set a different user by username
282
+	container, err = docker.Create(
283
+		"user_daemon",
284
+		"id",
285
+		[]string{},
286
+		[]string{"/var/lib/docker/images/ubuntu"},
287
+		&Config{
288
+			User: "daemon",
289
+		},
290
+	)
291
+	if err != nil {
292
+		t.Fatal(err)
293
+	}
294
+	defer docker.Destroy(container)
295
+	output, err = container.Output()
296
+	if err != nil {
297
+		t.Fatal(err)
298
+	}
299
+	if !strings.Contains(string(output), "uid=1(daemon) gid=1(daemon)") {
300
+		t.Error(string(output))
301
+	}
302
+}
303
+
189 304
 func TestMultipleContainers(t *testing.T) {
190 305
 	docker, err := newTestDocker()
191 306
 	if err != nil {
... ...
@@ -611,10 +611,10 @@ func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string
611 611
 	return errors.New("No such container: " + cmd.Arg(0))
612 612
 }
613 613
 
614
-func (srv *Server) CreateContainer(img *image.Image, tty bool, openStdin bool, comment string, cmd string, args ...string) (*docker.Container, error) {
614
+func (srv *Server) CreateContainer(img *image.Image, user string, tty bool, openStdin bool, comment string, cmd string, args ...string) (*docker.Container, error) {
615 615
 	id := future.RandomId()[:8]
616 616
 	container, err := srv.containers.Create(id, cmd, args, img.Layers,
617
-		&docker.Config{Hostname: id, Tty: tty, OpenStdin: openStdin})
617
+		&docker.Config{Hostname: id, User: user, Tty: tty, OpenStdin: openStdin})
618 618
 	if err != nil {
619 619
 		return nil, err
620 620
 	}
... ...
@@ -680,6 +680,7 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
680 680
 	fl_attach := cmd.Bool("a", false, "Attach stdin and stdout")
681 681
 	fl_stdin := cmd.Bool("i", false, "Keep stdin open even if not attached")
682 682
 	fl_tty := cmd.Bool("t", false, "Allocate a pseudo-tty")
683
+	fl_user := cmd.String("u", "0", "Username or UID")
683 684
 	fl_comment := cmd.String("c", "", "Comment")
684 685
 	if err := cmd.Parse(args); err != nil {
685 686
 		return nil
... ...
@@ -706,7 +707,7 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
706 706
 		return errors.New("No such image: " + name)
707 707
 	}
708 708
 	// Create new container
709
-	container, err := srv.CreateContainer(img, *fl_tty, *fl_stdin, *fl_comment, cmdline[0], cmdline[1:]...)
709
+	container, err := srv.CreateContainer(img, *fl_user, *fl_tty, *fl_stdin, *fl_comment, cmdline[0], cmdline[1:]...)
710 710
 	if err != nil {
711 711
 		return errors.New("Error creating container: " + err.Error())
712 712
 	}
... ...
@@ -1,13 +1,58 @@
1 1
 package docker
2 2
 
3 3
 import (
4
+	"flag"
4 5
 	"fmt"
5 6
 	"log"
6 7
 	"os"
7 8
 	"os/exec"
9
+	"os/user"
10
+	"strconv"
8 11
 	"syscall"
9 12
 )
10 13
 
14
+// Takes care of dropping privileges to the desired user
15
+func changeUser(u string) {
16
+	if u == "" {
17
+		return
18
+	}
19
+	userent, err := user.LookupId(u)
20
+	if err != nil {
21
+		userent, err = user.Lookup(u)
22
+	}
23
+	if err != nil {
24
+		log.Fatalf("Unable to find user %v: %v", u, err)
25
+	}
26
+
27
+	uid, err := strconv.Atoi(userent.Uid)
28
+	if err != nil {
29
+		log.Fatalf("Invalid uid: %v", userent.Uid)
30
+	}
31
+	gid, err := strconv.Atoi(userent.Gid)
32
+	if err != nil {
33
+		log.Fatalf("Invalid gid: %v", userent.Gid)
34
+	}
35
+
36
+	if err := syscall.Setgid(gid); err != nil {
37
+		log.Fatalf("setgid failed: %v", err)
38
+	}
39
+	if err := syscall.Setuid(uid); err != nil {
40
+		log.Fatalf("setuid failed: %v", err)
41
+	}
42
+}
43
+
44
+func executeProgram(name string, args []string) {
45
+	path, err := exec.LookPath(name)
46
+	if err != nil {
47
+		log.Printf("Unable to locate %v", name)
48
+		os.Exit(127)
49
+	}
50
+
51
+	if err := syscall.Exec(path, args, os.Environ()); err != nil {
52
+		panic(err)
53
+	}
54
+}
55
+
11 56
 // Sys Init code
12 57
 // This code is run INSIDE the container and is responsible for setting
13 58
 // up the environment before running the actual process
... ...
@@ -16,14 +61,9 @@ func SysInit() {
16 16
 		fmt.Println("You should not invoke docker-init manually")
17 17
 		os.Exit(1)
18 18
 	}
19
+	var u = flag.String("u", "", "username or uid")
19 20
 
20
-	path, err := exec.LookPath(os.Args[1])
21
-	if err != nil {
22
-		log.Printf("Unable to locate %v", os.Args[1])
23
-		os.Exit(127)
24
-	}
25
-
26
-	if err := syscall.Exec(path, os.Args[1:], os.Environ()); err != nil {
27
-		panic(err)
28
-	}
21
+	flag.Parse()
22
+	changeUser(*u)
23
+	executeProgram(flag.Arg(0), flag.Args())
29 24
 }