Browse code

Add support for --pid=container:<id>

Signed-off-by: Mrunal Patel <mrunalp@gmail.com>

Mrunal Patel authored on 2016/05/07 03:56:03
Showing 9 changed files
... ...
@@ -1832,6 +1832,21 @@ _docker_run() {
1832 1832
 			esac
1833 1833
 			return
1834 1834
 			;;
1835
+		--pid)
1836
+			case "$cur" in
1837
+				*:*)
1838
+					cur="${cur#*:}"
1839
+					__docker_complete_containers_running
1840
+					;;
1841
+				*)
1842
+					COMPREPLY=( $( compgen -W 'host container:' -- "$cur" ) )
1843
+					if [ "$COMPREPLY" = "container:" ]; then
1844
+						__docker_nospace
1845
+					fi
1846
+					;;
1847
+			esac
1848
+			return
1849
+			;;
1835 1850
 		--security-opt)
1836 1851
 			COMPREPLY=( $( compgen -W "apparmor= label= no-new-privileges seccomp=" -- "$cur") )
1837 1852
 			if [ "${COMPREPLY[*]}" != "no-new-privileges" ] ; then
... ...
@@ -699,7 +699,7 @@ __docker_subcommand() {
699 699
         "($help)--pids-limit[Tune container pids limit (set -1 for unlimited)]"
700 700
         "($help -P --publish-all)"{-P,--publish-all}"[Publish all exposed ports]"
701 701
         "($help)*"{-p=,--publish=}"[Expose a container's port to the host]:port:_ports"
702
-        "($help)--pid=[PID namespace to use]:PID: "
702
+        "($help)--pid=[PID namespace to use]:PID namespace: "
703 703
         "($help)--privileged[Give extended privileges to this container]"
704 704
         "($help)--read-only[Mount the container's root filesystem as read only]"
705 705
         "($help)*--security-opt=[Security options]:security option: "
... ...
@@ -169,6 +169,21 @@ func (daemon *Daemon) getIpcContainer(container *container.Container) (*containe
169 169
 	return c, nil
170 170
 }
171 171
 
172
+func (daemon *Daemon) getPidContainer(container *container.Container) (*container.Container, error) {
173
+	containerID := container.HostConfig.PidMode.Container()
174
+	c, err := daemon.GetContainer(containerID)
175
+	if err != nil {
176
+		return nil, err
177
+	}
178
+	if !c.IsRunning() {
179
+		return nil, fmt.Errorf("cannot join PID of a non running container: %s", containerID)
180
+	}
181
+	if c.IsRestarting() {
182
+		return nil, errContainerIsRestarting(container.ID)
183
+	}
184
+	return c, nil
185
+}
186
+
172 187
 func (daemon *Daemon) setupIpcDirs(c *container.Container) error {
173 188
 	var err error
174 189
 
... ...
@@ -142,13 +142,40 @@ func (daemon *Daemon) generateSecurityOpt(ipcMode containertypes.IpcMode, pidMod
142 142
 	if ipcMode.IsHost() || pidMode.IsHost() {
143 143
 		return label.DisableSecOpt(), nil
144 144
 	}
145
-	if ipcContainer := ipcMode.Container(); ipcContainer != "" {
145
+
146
+	var ipcLabel []string
147
+	var pidLabel []string
148
+	ipcContainer := ipcMode.Container()
149
+	pidContainer := pidMode.Container()
150
+	if ipcContainer != "" {
146 151
 		c, err := daemon.GetContainer(ipcContainer)
147 152
 		if err != nil {
148 153
 			return nil, err
149 154
 		}
155
+		ipcLabel = label.DupSecOpt(c.ProcessLabel)
156
+		if pidContainer == "" {
157
+			return ipcLabel, err
158
+		}
159
+	}
160
+	if pidContainer != "" {
161
+		c, err := daemon.GetContainer(pidContainer)
162
+		if err != nil {
163
+			return nil, err
164
+		}
150 165
 
151
-		return label.DupSecOpt(c.ProcessLabel), nil
166
+		pidLabel = label.DupSecOpt(c.ProcessLabel)
167
+		if ipcContainer == "" {
168
+			return pidLabel, err
169
+		}
170
+	}
171
+
172
+	if pidLabel != nil && ipcLabel != nil {
173
+		for i := 0; i < len(pidLabel); i++ {
174
+			if pidLabel[i] != ipcLabel[i] {
175
+				return nil, fmt.Errorf("--ipc and --pid containers SELinux labels aren't the same")
176
+			}
177
+		}
178
+		return pidLabel, nil
152 179
 	}
153 180
 	return nil, nil
154 181
 }
... ...
@@ -296,8 +296,25 @@ func setNamespaces(daemon *Daemon, s *specs.Spec, c *container.Container) error
296 296
 		setNamespace(s, ns)
297 297
 	}
298 298
 	// pid
299
-	if c.HostConfig.PidMode.IsHost() {
299
+	if c.HostConfig.PidMode.IsContainer() {
300
+		ns := specs.Namespace{Type: "pid"}
301
+		pc, err := daemon.getPidContainer(c)
302
+		if err != nil {
303
+			return err
304
+		}
305
+		ns.Path = fmt.Sprintf("/proc/%d/ns/pid", pc.State.GetPID())
306
+		setNamespace(s, ns)
307
+		if userNS {
308
+			// to share an PID namespace, they must also share a user namespace
309
+			nsUser := specs.Namespace{Type: "user"}
310
+			nsUser.Path = fmt.Sprintf("/proc/%d/ns/user", pc.State.GetPID())
311
+			setNamespace(s, nsUser)
312
+		}
313
+	} else if c.HostConfig.PidMode.IsHost() {
300 314
 		delNamespace(s, specs.NamespaceType("pid"))
315
+	} else {
316
+		ns := specs.Namespace{Type: "pid"}
317
+		setNamespace(s, ns)
301 318
 	}
302 319
 	// uts
303 320
 	if c.HostConfig.UTSMode.IsHost() {
... ...
@@ -193,7 +193,8 @@ the digest value is predictable and referenceable.
193 193
 ## PID settings (--pid)
194 194
 
195 195
     --pid=""  : Set the PID (Process) Namespace mode for the container,
196
-           'host': use the host's PID namespace inside the container
196
+                 'container:<name|id>': joins another container's PID namespace
197
+                 'host': use the host's PID namespace inside the container
197 198
 
198 199
 By default, all containers have the PID namespace enabled.
199 200
 
... ...
@@ -229,6 +230,23 @@ Use the following command to run `htop` inside a container:
229 229
 $ docker run -it --rm --pid=host myhtop
230 230
 ```
231 231
 
232
+Joining another container's pid namespace can be used for debugging that container.
233
+
234
+### Example
235
+
236
+Start a container running a redis server:
237
+
238
+```bash
239
+$ docker run --name my-redis -d redis
240
+```
241
+
242
+Debug the redis container by running another container that has strace in it:
243
+
244
+```bash
245
+$ docker run --it --pid=container:my-redis bash
246
+$ strace -p 1
247
+```
248
+
232 249
 ## UTS settings (--uts)
233 250
 
234 251
     --uts=""  : Set the UTS namespace mode for the container,
... ...
@@ -2443,6 +2443,53 @@ func (s *DockerSuite) TestRunModeIpcContainerNotRunning(c *check.C) {
2443 2443
 	}
2444 2444
 }
2445 2445
 
2446
+func (s *DockerSuite) TestRunModePidContainer(c *check.C) {
2447
+	// Not applicable on Windows as uses Unix-specific capabilities
2448
+	testRequires(c, SameHostDaemon, DaemonIsLinux)
2449
+
2450
+	out, _ := dockerCmd(c, "run", "-d", "busybox", "sh", "-c", "top")
2451
+
2452
+	id := strings.TrimSpace(out)
2453
+	state := inspectField(c, id, "State.Running")
2454
+	if state != "true" {
2455
+		c.Fatal("Container state is 'not running'")
2456
+	}
2457
+	pid1 := inspectField(c, id, "State.Pid")
2458
+
2459
+	parentContainerPid, err := os.Readlink(fmt.Sprintf("/proc/%s/ns/pid", pid1))
2460
+	if err != nil {
2461
+		c.Fatal(err)
2462
+	}
2463
+
2464
+	out, _ = dockerCmd(c, "run", fmt.Sprintf("--pid=container:%s", id), "busybox", "readlink", "/proc/self/ns/pid")
2465
+	out = strings.Trim(out, "\n")
2466
+	if parentContainerPid != out {
2467
+		c.Fatalf("PID different with --pid=container:%s %s != %s\n", id, parentContainerPid, out)
2468
+	}
2469
+}
2470
+
2471
+func (s *DockerSuite) TestRunModePidContainerNotExists(c *check.C) {
2472
+	// Not applicable on Windows as uses Unix-specific capabilities
2473
+	testRequires(c, DaemonIsLinux)
2474
+	out, _, err := dockerCmdWithError("run", "-d", "--pid", "container:abcd1234", "busybox", "top")
2475
+	if !strings.Contains(out, "abcd1234") || err == nil {
2476
+		c.Fatalf("run PID from a non exists container should with correct error out")
2477
+	}
2478
+}
2479
+
2480
+func (s *DockerSuite) TestRunModePidContainerNotRunning(c *check.C) {
2481
+	// Not applicable on Windows as uses Unix-specific capabilities
2482
+	testRequires(c, SameHostDaemon, DaemonIsLinux)
2483
+
2484
+	out, _ := dockerCmd(c, "create", "busybox")
2485
+
2486
+	id := strings.TrimSpace(out)
2487
+	out, _, err := dockerCmdWithError("run", fmt.Sprintf("--pid=container:%s", id), "busybox")
2488
+	if err == nil {
2489
+		c.Fatalf("Run container with pid mode container should fail with non running container: %s\n%s", out, err)
2490
+	}
2491
+}
2492
+
2446 2493
 func (s *DockerSuite) TestRunMountShmMqueueFromHost(c *check.C) {
2447 2494
 	// Not applicable on Windows as uses Unix-specific capabilities
2448 2495
 	testRequires(c, SameHostDaemon, DaemonIsLinux, NotUserNamespace)
... ...
@@ -57,7 +57,7 @@ docker-create - Create a new container
57 57
 [**--oom-score-adj**[=*0*]]
58 58
 [**-P**|**--publish-all**]
59 59
 [**-p**|**--publish**[=*[]*]]
60
-[**--pid**[=*[]*]]
60
+[**--pid**[=*[PID]*]]
61 61
 [**--userns**[=*[]*]]
62 62
 [**--pids-limit**[=*PIDS_LIMIT*]]
63 63
 [**--privileged**]
... ...
@@ -289,10 +289,11 @@ unit, `b` is used. Set LIMIT to `-1` to enable unlimited swap.
289 289
                                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`)
290 290
                                (use 'docker port' to see the actual mapping)
291 291
 
292
-**--pid**=*host*
292
+**--pid**=""
293 293
    Set the PID mode for the container
294
-     **host**: use the host's PID namespace inside the container.
295
-     Note: the host mode gives the container full access to local PID and is therefore considered insecure.
294
+   Default is to create a private PID namespace for the container
295
+                               'container:<name|id>': join another container's PID namespace
296
+                               '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.
296 297
 
297 298
 **--userns**=""
298 299
    Set the usernamespace mode for the container when `userns-remap` option is enabled.
... ...
@@ -59,7 +59,7 @@ docker-run - Run a command in a new container
59 59
 [**--oom-score-adj**[=*0*]]
60 60
 [**-P**|**--publish-all**]
61 61
 [**-p**|**--publish**[=*[]*]]
62
-[**--pid**[=*[]*]]
62
+[**--pid**[=*[PID]*]]
63 63
 [**--userns**[=*[]*]]
64 64
 [**--pids-limit**[=*PIDS_LIMIT*]]
65 65
 [**--privileged**]
... ...
@@ -420,10 +420,11 @@ but not `docker run -p 1230-1236:1230-1240 --name RangeContainerPortsBiggerThanR
420 420
 With ip: `docker run -p 127.0.0.1:$HOSTPORT:$CONTAINERPORT --name CONTAINER -t someimage`
421 421
 Use `docker port` to see the actual mapping: `docker port CONTAINER $CONTAINERPORT`
422 422
 
423
-**--pid**=*host*
423
+**--pid**=""
424 424
    Set the PID mode for the container
425
-     **host**: use the host's PID namespace inside the container.
426
-     Note: the host mode gives the container full access to local PID and is therefore considered insecure.
425
+   Default is to create a private PID namespace for the container
426
+                               'container:<name|id>': join another container's PID namespace
427
+                               '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.
427 428
 
428 429
 **--userns**=""
429 430
    Set the usernamespace mode for the container when `userns-remap` option is enabled.