diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index f67ebebd66..f26ff80244 100644 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -1832,6 +1832,21 @@ _docker_run() { esac return ;; + --pid) + case "$cur" in + *:*) + cur="${cur#*:}" + __docker_complete_containers_running + ;; + *) + COMPREPLY=( $( compgen -W 'host container:' -- "$cur" ) ) + if [ "$COMPREPLY" = "container:" ]; then + __docker_nospace + fi + ;; + esac + return + ;; --security-opt) COMPREPLY=( $( compgen -W "apparmor= label= no-new-privileges seccomp=" -- "$cur") ) if [ "${COMPREPLY[*]}" != "no-new-privileges" ] ; then diff --git a/contrib/completion/zsh/_docker b/contrib/completion/zsh/_docker index fa8befe975..5862768ff3 100644 --- a/contrib/completion/zsh/_docker +++ b/contrib/completion/zsh/_docker @@ -699,7 +699,7 @@ __docker_subcommand() { "($help)--pids-limit[Tune container pids limit (set -1 for unlimited)]" "($help -P --publish-all)"{-P,--publish-all}"[Publish all exposed ports]" "($help)*"{-p=,--publish=}"[Expose a container's port to the host]:port:_ports" - "($help)--pid=[PID namespace to use]:PID: " + "($help)--pid=[PID namespace to use]:PID namespace: " "($help)--privileged[Give extended privileges to this container]" "($help)--read-only[Mount the container's root filesystem as read only]" "($help)*--security-opt=[Security options]:security option: " diff --git a/daemon/container_operations_unix.go b/daemon/container_operations_unix.go index 84be5410d6..55bd3fc839 100644 --- a/daemon/container_operations_unix.go +++ b/daemon/container_operations_unix.go @@ -169,6 +169,21 @@ func (daemon *Daemon) getIpcContainer(container *container.Container) (*containe return c, nil } +func (daemon *Daemon) getPidContainer(container *container.Container) (*container.Container, error) { + containerID := container.HostConfig.PidMode.Container() + c, err := daemon.GetContainer(containerID) + if err != nil { + return nil, err + } + if !c.IsRunning() { + return nil, fmt.Errorf("cannot join PID of a non running container: %s", containerID) + } + if c.IsRestarting() { + return nil, errContainerIsRestarting(container.ID) + } + return c, nil +} + func (daemon *Daemon) setupIpcDirs(c *container.Container) error { var err error diff --git a/daemon/create.go b/daemon/create.go index 93576f521f..eea3a1351d 100644 --- a/daemon/create.go +++ b/daemon/create.go @@ -142,13 +142,40 @@ func (daemon *Daemon) generateSecurityOpt(ipcMode containertypes.IpcMode, pidMod if ipcMode.IsHost() || pidMode.IsHost() { return label.DisableSecOpt(), nil } - if ipcContainer := ipcMode.Container(); ipcContainer != "" { + + var ipcLabel []string + var pidLabel []string + ipcContainer := ipcMode.Container() + pidContainer := pidMode.Container() + if ipcContainer != "" { c, err := daemon.GetContainer(ipcContainer) if err != nil { return nil, err } + ipcLabel = label.DupSecOpt(c.ProcessLabel) + if pidContainer == "" { + return ipcLabel, err + } + } + if pidContainer != "" { + c, err := daemon.GetContainer(pidContainer) + if err != nil { + return nil, err + } - return label.DupSecOpt(c.ProcessLabel), nil + pidLabel = label.DupSecOpt(c.ProcessLabel) + if ipcContainer == "" { + return pidLabel, err + } + } + + if pidLabel != nil && ipcLabel != nil { + for i := 0; i < len(pidLabel); i++ { + if pidLabel[i] != ipcLabel[i] { + return nil, fmt.Errorf("--ipc and --pid containers SELinux labels aren't the same") + } + } + return pidLabel, nil } return nil, nil } diff --git a/daemon/oci_linux.go b/daemon/oci_linux.go index d8ab1f8fd6..f20904d5b3 100644 --- a/daemon/oci_linux.go +++ b/daemon/oci_linux.go @@ -296,8 +296,25 @@ func setNamespaces(daemon *Daemon, s *specs.Spec, c *container.Container) error setNamespace(s, ns) } // pid - if c.HostConfig.PidMode.IsHost() { + if c.HostConfig.PidMode.IsContainer() { + ns := specs.Namespace{Type: "pid"} + pc, err := daemon.getPidContainer(c) + if err != nil { + return err + } + ns.Path = fmt.Sprintf("/proc/%d/ns/pid", pc.State.GetPID()) + setNamespace(s, ns) + if userNS { + // to share an PID namespace, they must also share a user namespace + nsUser := specs.Namespace{Type: "user"} + nsUser.Path = fmt.Sprintf("/proc/%d/ns/user", pc.State.GetPID()) + setNamespace(s, nsUser) + } + } else if c.HostConfig.PidMode.IsHost() { delNamespace(s, specs.NamespaceType("pid")) + } else { + ns := specs.Namespace{Type: "pid"} + setNamespace(s, ns) } // uts if c.HostConfig.UTSMode.IsHost() { diff --git a/docs/reference/run.md b/docs/reference/run.md index 942bba948f..190b9b7601 100644 --- a/docs/reference/run.md +++ b/docs/reference/run.md @@ -193,7 +193,8 @@ the digest value is predictable and referenceable. ## PID settings (--pid) --pid="" : Set the PID (Process) Namespace mode for the container, - 'host': use the host's PID namespace inside the container + 'container:': joins another container's PID namespace + 'host': use the host's PID namespace inside the container By default, all containers have the PID namespace enabled. @@ -229,6 +230,23 @@ Use the following command to run `htop` inside a container: $ docker run -it --rm --pid=host myhtop ``` +Joining another container's pid namespace can be used for debugging that container. + +### Example + +Start a container running a redis server: + +```bash +$ docker run --name my-redis -d redis +``` + +Debug the redis container by running another container that has strace in it: + +```bash +$ docker run --it --pid=container:my-redis bash +$ strace -p 1 +``` + ## UTS settings (--uts) --uts="" : Set the UTS namespace mode for the container, diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 7f6fd91964..42f9a7b9f9 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -2443,6 +2443,53 @@ func (s *DockerSuite) TestRunModeIpcContainerNotRunning(c *check.C) { } } +func (s *DockerSuite) TestRunModePidContainer(c *check.C) { + // Not applicable on Windows as uses Unix-specific capabilities + testRequires(c, SameHostDaemon, DaemonIsLinux) + + out, _ := dockerCmd(c, "run", "-d", "busybox", "sh", "-c", "top") + + id := strings.TrimSpace(out) + state := inspectField(c, id, "State.Running") + if state != "true" { + c.Fatal("Container state is 'not running'") + } + pid1 := inspectField(c, id, "State.Pid") + + parentContainerPid, err := os.Readlink(fmt.Sprintf("/proc/%s/ns/pid", pid1)) + if err != nil { + c.Fatal(err) + } + + out, _ = dockerCmd(c, "run", fmt.Sprintf("--pid=container:%s", id), "busybox", "readlink", "/proc/self/ns/pid") + out = strings.Trim(out, "\n") + if parentContainerPid != out { + c.Fatalf("PID different with --pid=container:%s %s != %s\n", id, parentContainerPid, out) + } +} + +func (s *DockerSuite) TestRunModePidContainerNotExists(c *check.C) { + // Not applicable on Windows as uses Unix-specific capabilities + testRequires(c, DaemonIsLinux) + out, _, err := dockerCmdWithError("run", "-d", "--pid", "container:abcd1234", "busybox", "top") + if !strings.Contains(out, "abcd1234") || err == nil { + c.Fatalf("run PID from a non exists container should with correct error out") + } +} + +func (s *DockerSuite) TestRunModePidContainerNotRunning(c *check.C) { + // Not applicable on Windows as uses Unix-specific capabilities + testRequires(c, SameHostDaemon, DaemonIsLinux) + + out, _ := dockerCmd(c, "create", "busybox") + + id := strings.TrimSpace(out) + out, _, err := dockerCmdWithError("run", fmt.Sprintf("--pid=container:%s", id), "busybox") + if err == nil { + c.Fatalf("Run container with pid mode container should fail with non running container: %s\n%s", out, err) + } +} + func (s *DockerSuite) TestRunMountShmMqueueFromHost(c *check.C) { // Not applicable on Windows as uses Unix-specific capabilities testRequires(c, SameHostDaemon, DaemonIsLinux, NotUserNamespace) diff --git a/man/docker-create.1.md b/man/docker-create.1.md index f8a80cd347..d48e1ac3c4 100644 --- a/man/docker-create.1.md +++ b/man/docker-create.1.md @@ -57,7 +57,7 @@ docker-create - Create a new container [**--oom-score-adj**[=*0*]] [**-P**|**--publish-all**] [**-p**|**--publish**[=*[]*]] -[**--pid**[=*[]*]] +[**--pid**[=*[PID]*]] [**--userns**[=*[]*]] [**--pids-limit**[=*PIDS_LIMIT*]] [**--privileged**] @@ -289,10 +289,11 @@ unit, `b` is used. Set LIMIT to `-1` to enable unlimited swap. When specifying ranges for both, the number of container ports in the range must match the number of host ports in the range. (e.g., `-p 1234-1236:1234-1236/tcp`) (use 'docker port' to see the actual mapping) -**--pid**=*host* +**--pid**="" Set the PID mode for the container - **host**: use the host's PID namespace inside the container. - Note: the host mode gives the container full access to local PID and is therefore considered insecure. + Default is to create a private PID namespace for the container + 'container:': join another container's PID namespace + 'host': use the host's PID namespace for the container. Note: the host mode gives the container full access to local PID and is therefore considered insecure. **--userns**="" Set the usernamespace mode for the container when `userns-remap` option is enabled. diff --git a/man/docker-run.1.md b/man/docker-run.1.md index 101acfa606..6de2f2d70e 100644 --- a/man/docker-run.1.md +++ b/man/docker-run.1.md @@ -59,7 +59,7 @@ docker-run - Run a command in a new container [**--oom-score-adj**[=*0*]] [**-P**|**--publish-all**] [**-p**|**--publish**[=*[]*]] -[**--pid**[=*[]*]] +[**--pid**[=*[PID]*]] [**--userns**[=*[]*]] [**--pids-limit**[=*PIDS_LIMIT*]] [**--privileged**] @@ -420,10 +420,11 @@ but not `docker run -p 1230-1236:1230-1240 --name RangeContainerPortsBiggerThanR With ip: `docker run -p 127.0.0.1:$HOSTPORT:$CONTAINERPORT --name CONTAINER -t someimage` Use `docker port` to see the actual mapping: `docker port CONTAINER $CONTAINERPORT` -**--pid**=*host* +**--pid**="" Set the PID mode for the container - **host**: use the host's PID namespace inside the container. - Note: the host mode gives the container full access to local PID and is therefore considered insecure. + Default is to create a private PID namespace for the container + 'container:': join another container's PID namespace + 'host': use the host's PID namespace for the container. Note: the host mode gives the container full access to local PID and is therefore considered insecure. **--userns**="" Set the usernamespace mode for the container when `userns-remap` option is enabled.