Explorar o código

Merge pull request #12025 from coolljt0725/add_exec_with_user

Add docker exec run command as a different user and in privileged mode
Michael Crosby %!s(int64=10) %!d(string=hai) anos
pai
achega
12f7db1830

+ 1 - 1
contrib/completion/bash/docker

@@ -407,7 +407,7 @@ _docker_events() {
 _docker_exec() {
 	case "$cur" in
 		-*)
-			COMPREPLY=( $( compgen -W "--detach -d --help --interactive -i -t --tty" -- "$cur" ) )
+			COMPREPLY=( $( compgen -W "--detach -d --help --interactive -i --privileged -t --tty -u --user" -- "$cur" ) )
 			;;
 		*)
 			__docker_containers_running

+ 2 - 0
daemon/exec.go

@@ -138,6 +138,8 @@ func (d *Daemon) ContainerExecCreate(job *engine.Job) error {
 		Tty:        config.Tty,
 		Entrypoint: entrypoint,
 		Arguments:  args,
+		User:       config.User,
+		Privileged: config.Privileged,
 	}
 
 	execConfig := &execConfig{

+ 5 - 2
daemon/execdriver/native/exec.go

@@ -14,7 +14,6 @@ import (
 	"github.com/docker/libcontainer/utils"
 )
 
-// TODO(vishh): Add support for running in privileged mode and running as a different user.
 func (d *driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
 	active := d.activeContainers[c.ID]
 	if active == nil {
@@ -28,7 +27,11 @@ func (d *driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessCo
 		Args: append([]string{processConfig.Entrypoint}, processConfig.Arguments...),
 		Env:  c.ProcessConfig.Env,
 		Cwd:  c.WorkingDir,
-		User: c.ProcessConfig.User,
+		User: processConfig.User,
+	}
+
+	if processConfig.Privileged {
+		p.Capabilities = execdriver.GetAllCapabilities()
 	}
 
 	if processConfig.Tty {

+ 17 - 0
docs/man/docker-exec.1.md

@@ -9,7 +9,9 @@ docker-exec - Run a command in a running container
 [**-d**|**--detach**[=*false*]]
 [**--help**]
 [**-i**|**--interactive**[=*false*]]
+[**--privileged**[=*false*]]
 [**-t**|**--tty**[=*false*]]
+[**-u**|**--user**[=*USER*]]
 CONTAINER COMMAND [ARG...]
 
 # DESCRIPTION
@@ -32,9 +34,24 @@ container is unpaused, and then run
 **-i**, **--interactive**=*true*|*false*
    Keep STDIN open even if not attached. The default is *false*.
 
+**--privileged**=*true*|*false*
+   Give extended privileges to the process to run in a running container. The default is *false*.
+
+   By default, the process run by docker exec in a running container
+have the same capabilities of the container. By setting --privileged will give
+all the capabilities to the process.
+
 **-t**, **--tty**=*true*|*false*
    Allocate a pseudo-TTY. The default is *false*.
 
+**-u**, **--user**=""
+   Sets the username or UID used and optionally the groupname or GID for the specified command.
+
+   The followings examples are all valid:
+   --user [user | user:group | uid | uid:gid | user:gid | uid:group ]
+
+   Without this argument the command will be run as root in the container.
+
 The **-t** option is incompatible with a redirection of the docker client
 standard input.
 

+ 2 - 0
docs/sources/reference/commandline/cli.md

@@ -1120,7 +1120,9 @@ You'll need two shells for this example.
 
       -d, --detach=false         Detached mode: run command in the background
       -i, --interactive=false    Keep STDIN open even if not attached
+      --privileged=false         Give extended privileges to the command
       -t, --tty=false            Allocate a pseudo-TTY
+      -u, --user=                Username or UID (format: <name|uid>[:<group|gid>])
 
 The `docker exec` command runs a new command in a running container.
 

+ 57 - 0
integration-cli/docker_cli_exec_test.go

@@ -665,3 +665,60 @@ func TestRunMutableNetworkFiles(t *testing.T) {
 	}
 	logDone("run - mutable network files")
 }
+
+func TestExecWithUser(t *testing.T) {
+	defer deleteAllContainers()
+
+	runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "parent", "busybox", "top")
+	if out, _, err := runCommandWithOutput(runCmd); err != nil {
+		t.Fatal(out, err)
+	}
+
+	cmd := exec.Command(dockerBinary, "exec", "-u", "1", "parent", "id")
+	out, _, err := runCommandWithOutput(cmd)
+	if err != nil {
+		t.Fatal(err, out)
+	}
+	if !strings.Contains(out, "uid=1(daemon) gid=1(daemon)") {
+		t.Fatalf("exec with user by id expected daemon user got %s", out)
+	}
+
+	cmd = exec.Command(dockerBinary, "exec", "-u", "root", "parent", "id")
+	out, _, err = runCommandWithOutput(cmd)
+	if err != nil {
+		t.Fatal(err, out)
+	}
+	if !strings.Contains(out, "uid=0(root) gid=0(root)") {
+		t.Fatalf("exec with user by root expected root user got %s", out)
+	}
+
+	logDone("exec - with user")
+}
+
+func TestExecWithPrivileged(t *testing.T) {
+	defer deleteAllContainers()
+
+	runCmd := exec.Command(dockerBinary, "run", "-d", "--name", "parent", "--cap-drop=ALL", "busybox", "top")
+	if out, _, err := runCommandWithOutput(runCmd); err != nil {
+		t.Fatal(out, err)
+	}
+
+	cmd := exec.Command(dockerBinary, "exec", "parent", "sh", "-c", "mknod /tmp/sda b 8 0")
+	out, _, err := runCommandWithOutput(cmd)
+	fmt.Printf("%s", out)
+	if err == nil || !strings.Contains(out, "Operation not permitted") {
+		t.Fatalf("exec mknod in --cap-drop=ALL container without --privileged should failed")
+	}
+
+	cmd = exec.Command(dockerBinary, "exec", "--privileged", "parent", "sh", "-c", "mknod /tmp/sda b 8 0 && echo ok")
+	out, _, err = runCommandWithOutput(cmd)
+	if err != nil {
+		t.Fatal(err, out)
+	}
+
+	if actual := strings.TrimSpace(out); actual != "ok" {
+		t.Fatalf("exec mknod in --cap-drop=ALL container with --privileged failed: %v, output: %q", err, out)
+	}
+
+	logDone("exec - exec command in a container with privileged")
+}

+ 11 - 13
runconfig/exec.go

@@ -21,10 +21,8 @@ type ExecConfig struct {
 
 func ExecConfigFromJob(job *engine.Job) (*ExecConfig, error) {
 	execConfig := &ExecConfig{
-		// TODO(vishh): Expose 'User' once it is supported.
-		//User:         job.Getenv("User"),
-		// TODO(vishh): Expose 'Privileged' once it is supported.
-		//Privileged:   job.GetenvBool("Privileged"),
+		User:         job.Getenv("User"),
+		Privileged:   job.GetenvBool("Privileged"),
 		Tty:          job.GetenvBool("Tty"),
 		AttachStdin:  job.GetenvBool("AttachStdin"),
 		AttachStderr: job.GetenvBool("AttachStderr"),
@@ -42,11 +40,13 @@ func ExecConfigFromJob(job *engine.Job) (*ExecConfig, error) {
 
 func ParseExec(cmd *flag.FlagSet, args []string) (*ExecConfig, error) {
 	var (
-		flStdin   = cmd.Bool([]string{"i", "-interactive"}, false, "Keep STDIN open even if not attached")
-		flTty     = cmd.Bool([]string{"t", "-tty"}, false, "Allocate a pseudo-TTY")
-		flDetach  = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: run command in the background")
-		execCmd   []string
-		container string
+		flStdin      = cmd.Bool([]string{"i", "-interactive"}, false, "Keep STDIN open even if not attached")
+		flTty        = cmd.Bool([]string{"t", "-tty"}, false, "Allocate a pseudo-TTY")
+		flDetach     = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: run command in the background")
+		flUser       = cmd.String([]string{"u", "-user"}, "", "Username or UID (format: <name|uid>[:<group|gid>])")
+		flPrivileged = cmd.Bool([]string{"-privileged"}, false, "Give extended privileges to the command")
+		execCmd      []string
+		container    string
 	)
 	cmd.Require(flag.Min, 2)
 	if err := cmd.ParseFlags(args, true); err != nil {
@@ -57,10 +57,8 @@ func ParseExec(cmd *flag.FlagSet, args []string) (*ExecConfig, error) {
 	execCmd = parsedArgs[1:]
 
 	execConfig := &ExecConfig{
-		// TODO(vishh): Expose '-u' flag once it is supported.
-		User: "",
-		// TODO(vishh): Expose '-p' flag once it is supported.
-		Privileged: false,
+		User:       *flUser,
+		Privileged: *flPrivileged,
 		Tty:        *flTty,
 		Cmd:        execCmd,
 		Container:  container,