Browse Source

Merge pull request #14113 from dit4c/10348-exec-privileged

Remerge of `docker exec --privileged` with better tests
Sebastiaan van Stijn 10 years ago
parent
commit
e1f3a5ad0a

+ 1 - 1
contrib/completion/bash/docker

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

+ 1 - 0
daemon/exec.go

@@ -152,6 +152,7 @@ func (d *Daemon) ContainerExecCreate(config *runconfig.ExecConfig) (string, erro
 		Entrypoint: entrypoint,
 		Arguments:  args,
 		User:       user,
+		Privileged: config.Privileged,
 	}
 
 	execConfig := &execConfig{

+ 4 - 1
daemon/execdriver/native/exec.go

@@ -19,7 +19,6 @@ import (
 
 // Exec implements the exec driver Driver interface,
 // it calls libcontainer APIs to execute a container.
-// TODO(vishh): Add support for running in privileged mode.
 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 {
@@ -33,6 +32,10 @@ func (d *Driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessCo
 		User: processConfig.User,
 	}
 
+	if processConfig.Privileged {
+		p.Capabilities = execdriver.GetAllCapabilities()
+	}
+
 	config := active.Config()
 	if err := setupPipes(&config, processConfig, p, pipes); err != nil {
 		return -1, err

+ 1 - 1
docs/reference/commandline/exec.md

@@ -17,6 +17,7 @@ weight=1
 
       -d, --detach=false         Detached mode: run command in the background
       -i, --interactive=false    Keep STDIN open even if not attached
+      --privileged=false         Give extended Linux capabilities to the command
       -t, --tty=false            Allocate a pseudo-TTY
       -u, --user=                Username or UID (format: <name|uid>[:<group|gid>])
 
@@ -52,4 +53,3 @@ This will create a new file `/tmp/execWorks` inside the running container
     $ docker exec -it ubuntu_bash bash
 
 This will create a new Bash session in the container `ubuntu_bash`.
-

+ 38 - 0
integration-cli/docker_cli_exec_test.go

@@ -532,6 +532,44 @@ func (s *DockerSuite) TestExecWithUser(c *check.C) {
 	}
 }
 
+func (s *DockerSuite) TestExecWithPrivileged(c *check.C) {
+
+	// Start main loop which attempts mknod repeatedly
+	dockerCmd(c, "run", "-d", "--name", "parent", "--cap-drop=ALL", "busybox", "sh", "-c", `while (true); do if [ -e /exec_priv ]; then cat /exec_priv && mknod /tmp/sda b 8 0 && echo "Success"; else echo "Privileged exec has not run yet"; fi; usleep 10000; done`)
+
+	// Check exec mknod doesn't work
+	cmd := exec.Command(dockerBinary, "exec", "parent", "sh", "-c", "mknod /tmp/sdb b 8 16")
+	out, _, err := runCommandWithOutput(cmd)
+	if err == nil || !strings.Contains(out, "Operation not permitted") {
+		c.Fatalf("exec mknod in --cap-drop=ALL container without --privileged should fail")
+	}
+
+	// Check exec mknod does work with --privileged
+	cmd = exec.Command(dockerBinary, "exec", "--privileged", "parent", "sh", "-c", `echo "Running exec --privileged" > /exec_priv && mknod /tmp/sdb b 8 16 && usleep 50000 && echo "Finished exec --privileged" > /exec_priv && echo ok`)
+	out, _, err = runCommandWithOutput(cmd)
+	if err != nil {
+		c.Fatal(err, out)
+	}
+
+	if actual := strings.TrimSpace(out); actual != "ok" {
+		c.Fatalf("exec mknod in --cap-drop=ALL container with --privileged failed: %v, output: %q", err, out)
+	}
+
+	// Check subsequent unprivileged exec cannot mknod
+	cmd = exec.Command(dockerBinary, "exec", "parent", "sh", "-c", "mknod /tmp/sdc b 8 32")
+	out, _, err = runCommandWithOutput(cmd)
+	if err == nil || !strings.Contains(out, "Operation not permitted") {
+		c.Fatalf("repeating exec mknod in --cap-drop=ALL container after --privileged without --privileged should fail")
+	}
+
+	// Confirm at no point was mknod allowed
+	logCmd := exec.Command(dockerBinary, "logs", "parent")
+	if out, _, err := runCommandWithOutput(logCmd); err != nil || strings.Contains(out, "Success") {
+		c.Fatal(out, err)
+	}
+
+}
+
 func (s *DockerSuite) TestExecWithImageUser(c *check.C) {
 	name := "testbuilduser"
 	_, err := buildImage(name,

+ 10 - 1
man/docker-exec.1.md

@@ -9,13 +9,14 @@ 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
 
-Run a process in a running container. 
+Run a process in a running container.
 
 The command started using `docker exec` will only run while the container's primary
 process (`PID 1`) is running, and will not be restarted if the container is restarted.
@@ -33,6 +34,14 @@ 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 the process extended [Linux capabilities](http://man7.org/linux/man-pages/man7/capabilities.7.html)
+when running in a container. The default is *false*.
+
+   Without this flag, the process run by `docker exec` in a running container has
+the same capabilities as the container, which may be limited. Set
+`--privileged` to give all capabilities to the process.
+
 **-t**, **--tty**=*true*|*false*
    Allocate a pseudo-TTY. The default is *false*.
 

+ 13 - 13
runconfig/exec.go

@@ -24,12 +24,13 @@ type ExecConfig struct {
 // not valid, it will return an 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")
-		flUser    = cmd.String([]string{"u", "-user"}, "", "Username or UID (format: <name|uid>[:<group|gid>])")
-		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 {
@@ -40,13 +41,12 @@ func ParseExec(cmd *flag.FlagSet, args []string) (*ExecConfig, error) {
 	execCmd = parsedArgs[1:]
 
 	execConfig := &ExecConfig{
-		User: *flUser,
-		// TODO(vishh): Expose 'Privileged' once it is supported.
-		// +		//Privileged:   job.GetenvBool("Privileged"),
-		Tty:       *flTty,
-		Cmd:       execCmd,
-		Container: container,
-		Detach:    *flDetach,
+		User:       *flUser,
+		Privileged: *flPrivileged,
+		Tty:        *flTty,
+		Cmd:        execCmd,
+		Container:  container,
+		Detach:     *flDetach,
 	}
 
 	// If -d is not set, attach to everything by default