Browse code

Add `-u|--user` flag to docker exec for running command as a different user

Signed-off-by: Lei Jitang <leijitang@huawei.com>

Lei Jitang authored on 2015/04/11 12:04:24
Showing 7 changed files
... ...
@@ -407,7 +407,7 @@ _docker_events() {
407 407
 _docker_exec() {
408 408
 	case "$cur" in
409 409
 		-*)
410
-			COMPREPLY=( $( compgen -W "--detach -d --help --interactive -i -t --tty" -- "$cur" ) )
410
+			COMPREPLY=( $( compgen -W "--detach -d --help --interactive -i -t --tty -u --user" -- "$cur" ) )
411 411
 			;;
412 412
 		*)
413 413
 			__docker_containers_running
... ...
@@ -138,6 +138,7 @@ func (d *Daemon) ContainerExecCreate(job *engine.Job) error {
138 138
 		Tty:        config.Tty,
139 139
 		Entrypoint: entrypoint,
140 140
 		Arguments:  args,
141
+		User:       config.User,
141 142
 	}
142 143
 
143 144
 	execConfig := &execConfig{
... ...
@@ -14,7 +14,7 @@ import (
14 14
 	"github.com/docker/libcontainer/utils"
15 15
 )
16 16
 
17
-// TODO(vishh): Add support for running in privileged mode and running as a different user.
17
+// TODO(vishh): Add support for running in privileged mode.
18 18
 func (d *driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
19 19
 	active := d.activeContainers[c.ID]
20 20
 	if active == nil {
... ...
@@ -28,7 +28,7 @@ func (d *driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessCo
28 28
 		Args: append([]string{processConfig.Entrypoint}, processConfig.Arguments...),
29 29
 		Env:  c.ProcessConfig.Env,
30 30
 		Cwd:  c.WorkingDir,
31
-		User: c.ProcessConfig.User,
31
+		User: processConfig.User,
32 32
 	}
33 33
 
34 34
 	if processConfig.Tty {
... ...
@@ -10,6 +10,7 @@ docker-exec - Run a command in a running container
10 10
 [**--help**]
11 11
 [**-i**|**--interactive**[=*false*]]
12 12
 [**-t**|**--tty**[=*false*]]
13
+[**-u**|**--user**[=*USER*]]
13 14
 CONTAINER COMMAND [ARG...]
14 15
 
15 16
 # DESCRIPTION
... ...
@@ -35,6 +36,14 @@ container is unpaused, and then run
35 35
 **-t**, **--tty**=*true*|*false*
36 36
    Allocate a pseudo-TTY. The default is *false*.
37 37
 
38
+**-u**, **--user**=""
39
+   Sets the username or UID used and optionally the groupname or GID for the specified command.
40
+
41
+   The followings examples are all valid:
42
+   --user [user | user:group | uid | uid:gid | user:gid | uid:group ]
43
+
44
+   Without this argument the command will be run as root in the container.
45
+
38 46
 The **-t** option is incompatible with a redirection of the docker client
39 47
 standard input.
40 48
 
... ...
@@ -1115,6 +1115,7 @@ You'll need two shells for this example.
1115 1115
       -d, --detach=false         Detached mode: run command in the background
1116 1116
       -i, --interactive=false    Keep STDIN open even if not attached
1117 1117
       -t, --tty=false            Allocate a pseudo-TTY
1118
+      -u, --user=                Username or UID (format: <name|uid>[:<group|gid>])
1118 1119
 
1119 1120
 The `docker exec` command runs a new command in a running container.
1120 1121
 
... ...
@@ -665,3 +665,32 @@ func TestRunMutableNetworkFiles(t *testing.T) {
665 665
 	}
666 666
 	logDone("run - mutable network files")
667 667
 }
668
+
669
+func TestExecWithUser(t *testing.T) {
670
+	defer deleteAllContainers()
671
+
672
+	runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "parent", "busybox", "top")
673
+	if out, _, err := runCommandWithOutput(runCmd); err != nil {
674
+		t.Fatal(out, err)
675
+	}
676
+
677
+	cmd := exec.Command(dockerBinary, "exec", "-u", "1", "parent", "id")
678
+	out, _, err := runCommandWithOutput(cmd)
679
+	if err != nil {
680
+		t.Fatal(err, out)
681
+	}
682
+	if !strings.Contains(out, "uid=1(daemon) gid=1(daemon)") {
683
+		t.Fatalf("exec with user by id expected daemon user got %s", out)
684
+	}
685
+
686
+	cmd = exec.Command(dockerBinary, "exec", "-u", "root", "parent", "id")
687
+	out, _, err = runCommandWithOutput(cmd)
688
+	if err != nil {
689
+		t.Fatal(err, out)
690
+	}
691
+	if !strings.Contains(out, "uid=0(root) gid=0(root)") {
692
+		t.Fatalf("exec with user by root expected root user got %s", out)
693
+	}
694
+
695
+	logDone("exec - with user")
696
+}
... ...
@@ -21,8 +21,7 @@ type ExecConfig struct {
21 21
 
22 22
 func ExecConfigFromJob(job *engine.Job) (*ExecConfig, error) {
23 23
 	execConfig := &ExecConfig{
24
-		// TODO(vishh): Expose 'User' once it is supported.
25
-		//User:         job.Getenv("User"),
24
+		User:         job.Getenv("User"),
26 25
 		// TODO(vishh): Expose 'Privileged' once it is supported.
27 26
 		//Privileged:   job.GetenvBool("Privileged"),
28 27
 		Tty:          job.GetenvBool("Tty"),
... ...
@@ -45,6 +44,7 @@ func ParseExec(cmd *flag.FlagSet, args []string) (*ExecConfig, error) {
45 45
 		flStdin   = cmd.Bool([]string{"i", "-interactive"}, false, "Keep STDIN open even if not attached")
46 46
 		flTty     = cmd.Bool([]string{"t", "-tty"}, false, "Allocate a pseudo-TTY")
47 47
 		flDetach  = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: run command in the background")
48
+		flUser    = cmd.String([]string{"u", "-user"}, "", "Username or UID (format: <name|uid>[:<group|gid>])")
48 49
 		execCmd   []string
49 50
 		container string
50 51
 	)
... ...
@@ -57,8 +57,7 @@ func ParseExec(cmd *flag.FlagSet, args []string) (*ExecConfig, error) {
57 57
 	execCmd = parsedArgs[1:]
58 58
 
59 59
 	execConfig := &ExecConfig{
60
-		// TODO(vishh): Expose '-u' flag once it is supported.
61
-		User: "",
60
+		User: *flUser,
62 61
 		// TODO(vishh): Expose '-p' flag once it is supported.
63 62
 		Privileged: false,
64 63
 		Tty:        *flTty,