Browse Source

Merge pull request #10080 from crosbymichael/pid-ns

Add --pid flag for staying in the host's pid namespace
Michael Crosby 10 years ago
parent
commit
47e3da848f

+ 4 - 0
daemon/container.go

@@ -250,6 +250,9 @@ func populateCommand(c *Container, env []string) error {
 		ipc.HostIpc = c.hostConfig.IpcMode.IsHost()
 		ipc.HostIpc = c.hostConfig.IpcMode.IsHost()
 	}
 	}
 
 
+	pid := &execdriver.Pid{}
+	pid.HostPid = c.hostConfig.PidMode.IsHost()
+
 	// Build lists of devices allowed and created within the container.
 	// Build lists of devices allowed and created within the container.
 	userSpecifiedDevices := make([]*devices.Device, len(c.hostConfig.Devices))
 	userSpecifiedDevices := make([]*devices.Device, len(c.hostConfig.Devices))
 	for i, deviceMapping := range c.hostConfig.Devices {
 	for i, deviceMapping := range c.hostConfig.Devices {
@@ -295,6 +298,7 @@ func populateCommand(c *Container, env []string) error {
 		WorkingDir:         c.Config.WorkingDir,
 		WorkingDir:         c.Config.WorkingDir,
 		Network:            en,
 		Network:            en,
 		Ipc:                ipc,
 		Ipc:                ipc,
+		Pid:                pid,
 		Resources:          resources,
 		Resources:          resources,
 		AllowedDevices:     allowedDevices,
 		AllowedDevices:     allowedDevices,
 		AutoCreatedDevices: autoCreatedDevices,
 		AutoCreatedDevices: autoCreatedDevices,

+ 3 - 3
daemon/create.go

@@ -92,7 +92,7 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
 		return nil, nil, err
 		return nil, nil, err
 	}
 	}
 	if hostConfig != nil && hostConfig.SecurityOpt == nil {
 	if hostConfig != nil && hostConfig.SecurityOpt == nil {
-		hostConfig.SecurityOpt, err = daemon.GenerateSecurityOpt(hostConfig.IpcMode)
+		hostConfig.SecurityOpt, err = daemon.GenerateSecurityOpt(hostConfig.IpcMode, hostConfig.PidMode)
 		if err != nil {
 		if err != nil {
 			return nil, nil, err
 			return nil, nil, err
 		}
 		}
@@ -124,8 +124,8 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
 	return container, warnings, nil
 	return container, warnings, nil
 }
 }
 
 
-func (daemon *Daemon) GenerateSecurityOpt(ipcMode runconfig.IpcMode) ([]string, error) {
-	if ipcMode.IsHost() {
+func (daemon *Daemon) GenerateSecurityOpt(ipcMode runconfig.IpcMode, pidMode runconfig.PidMode) ([]string, error) {
+	if ipcMode.IsHost() || pidMode.IsHost() {
 		return label.DisableSecOpt(), nil
 		return label.DisableSecOpt(), nil
 	}
 	}
 	if ipcContainer := ipcMode.Container(); ipcContainer != "" {
 	if ipcContainer := ipcMode.Container(); ipcContainer != "" {

+ 6 - 0
daemon/execdriver/driver.go

@@ -77,6 +77,11 @@ type Ipc struct {
 	HostIpc     bool   `json:"host_ipc"`
 	HostIpc     bool   `json:"host_ipc"`
 }
 }
 
 
+// PID settings of the container
+type Pid struct {
+	HostPid bool `json:"host_pid"`
+}
+
 type NetworkInterface struct {
 type NetworkInterface struct {
 	Gateway              string `json:"gateway"`
 	Gateway              string `json:"gateway"`
 	IPAddress            string `json:"ip"`
 	IPAddress            string `json:"ip"`
@@ -126,6 +131,7 @@ type Command struct {
 	ConfigPath         string            `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver
 	ConfigPath         string            `json:"config_path"` // this should be able to be removed when the lxc template is moved into the driver
 	Network            *Network          `json:"network"`
 	Network            *Network          `json:"network"`
 	Ipc                *Ipc              `json:"ipc"`
 	Ipc                *Ipc              `json:"ipc"`
+	Pid                *Pid              `json:"pid"`
 	Resources          *Resources        `json:"resources"`
 	Resources          *Resources        `json:"resources"`
 	Mounts             []Mount           `json:"mounts"`
 	Mounts             []Mount           `json:"mounts"`
 	AllowedDevices     []*devices.Device `json:"allowed_devices"`
 	AllowedDevices     []*devices.Device `json:"allowed_devices"`

+ 13 - 0
daemon/execdriver/native/create.go

@@ -40,6 +40,10 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Config, e
 		return nil, err
 		return nil, err
 	}
 	}
 
 
+	if err := d.createPid(container, c); err != nil {
+		return nil, err
+	}
+
 	if err := d.createNetwork(container, c); err != nil {
 	if err := d.createNetwork(container, c); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -151,6 +155,15 @@ func (d *driver) createIpc(container *libcontainer.Config, c *execdriver.Command
 	return nil
 	return nil
 }
 }
 
 
+func (d *driver) createPid(container *libcontainer.Config, c *execdriver.Command) error {
+	if c.Pid.HostPid {
+		container.Namespaces.Remove(libcontainer.NEWPID)
+		return nil
+	}
+
+	return nil
+}
+
 func (d *driver) setPrivileged(container *libcontainer.Config) (err error) {
 func (d *driver) setPrivileged(container *libcontainer.Config) (err error) {
 	container.Capabilities = capabilities.GetAllCapabilities()
 	container.Capabilities = capabilities.GetAllCapabilities()
 	container.Cgroups.AllowAllDevices = true
 	container.Cgroups.AllowAllDevices = true

+ 6 - 0
docs/man/docker-create.1.md

@@ -32,6 +32,7 @@ docker-create - Create a new container
 [**--net**[=*"bridge"*]]
 [**--net**[=*"bridge"*]]
 [**-P**|**--publish-all**[=*false*]]
 [**-P**|**--publish-all**[=*false*]]
 [**-p**|**--publish**[=*[]*]]
 [**-p**|**--publish**[=*[]*]]
+[**--pid**[=*[]*]]
 [**--privileged**[=*false*]]
 [**--privileged**[=*false*]]
 [**--restart**[=*RESTART*]]
 [**--restart**[=*RESTART*]]
 [**--security-opt**[=*[]*]]
 [**--security-opt**[=*[]*]]
@@ -131,6 +132,11 @@ IMAGE [COMMAND] [ARG...]
                                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`)
                                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)
                                (use 'docker port' to see the actual mapping)
 
 
+**--pid**=host
+   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.
+
 **--privileged**=*true*|*false*
 **--privileged**=*true*|*false*
    Give extended privileges to this container. The default is *false*.
    Give extended privileges to this container. The default is *false*.
 
 

+ 6 - 0
docs/man/docker-run.1.md

@@ -33,6 +33,7 @@ docker-run - Run a command in a new container
 [**--net**[=*"bridge"*]]
 [**--net**[=*"bridge"*]]
 [**-P**|**--publish-all**[=*false*]]
 [**-P**|**--publish-all**[=*false*]]
 [**-p**|**--publish**[=*[]*]]
 [**-p**|**--publish**[=*[]*]]
+[**--pid**[=*[]*]]
 [**--privileged**[=*false*]]
 [**--privileged**[=*false*]]
 [**--restart**[=*RESTART*]]
 [**--restart**[=*RESTART*]]
 [**--rm**[=*false*]]
 [**--rm**[=*false*]]
@@ -234,6 +235,11 @@ mapping between the host ports and the exposed ports, use **docker port**.
                                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`)
                                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)
                                (use 'docker port' to see the actual mapping)
 
 
+**--pid**=host
+   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.
+
 **--privileged**=*true*|*false*
 **--privileged**=*true*|*false*
    Give extended privileges to this container. The default is *false*.
    Give extended privileges to this container. The default is *false*.
 
 

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

@@ -1604,6 +1604,7 @@ removed before the image is removed.
                                    Both hostPort and containerPort can be specified as a range of ports. 
                                    Both hostPort and containerPort can be specified as a range of ports. 
                                    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`)
                                    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)
                                    (use 'docker port' to see the actual mapping)
+      --pid=host		 'host': use the host PID namespace inside the container.  Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
       --privileged=false         Give extended privileges to this container
       --privileged=false         Give extended privileges to this container
       --restart=""               Restart policy to apply when a container exits (no, on-failure[:max-retry], always)
       --restart=""               Restart policy to apply when a container exits (no, on-failure[:max-retry], always)
       --rm=false                 Automatically remove the container when it exits (incompatible with -d)
       --rm=false                 Automatically remove the container when it exits (incompatible with -d)

+ 21 - 1
docs/sources/reference/run.md

@@ -133,11 +133,31 @@ While not strictly a means of identifying a container, you can specify a version
 image you'd like to run the container with by adding `image[:tag]` to the command. For
 image you'd like to run the container with by adding `image[:tag]` to the command. For
 example, `docker run ubuntu:14.04`.
 example, `docker run ubuntu:14.04`.
 
 
+## PID Settings
+    --pid=""  : Set the PID (Process) Namespace mode for the container,
+           'host': use the host's PID namespace inside the container
+By default, all containers have the PID namespace enabled.
+
+PID namespace provides separation of processes. The PID Namespace removes the
+view of the system processes, and allows process ids to be reused including
+pid 1.
+
+In certain cases you want your container to share the host's process namespace,
+basically allowing processes within the container to see all of the processes
+on the system.  For example, you could build a container with debugging tools
+like `strace` or `gdb`, but want to use these tools when debugging processes
+within the container.
+
+    $ sudo docker run --pid=host rhel7 strace -p 1234
+
+This command would allow you to use `strace` inside the container on pid 1234 on
+the host.
+
 ## IPC Settings
 ## IPC Settings
     --ipc=""  : Set the IPC mode for the container,
     --ipc=""  : Set the IPC mode for the container,
                                  'container:<name|id>': reuses another container's IPC namespace
                                  'container:<name|id>': reuses another container's IPC namespace
                                  'host': use the host's IPC namespace inside the container
                                  'host': use the host's IPC namespace inside the container
-By default, all containers have the IPC namespace enabled 
+By default, all containers have the IPC namespace enabled.
 
 
 IPC (POSIX/SysV IPC) namespace provides separation of named shared memory segments, semaphores and message queues.  
 IPC (POSIX/SysV IPC) namespace provides separation of named shared memory segments, semaphores and message queues.  
 
 

+ 32 - 0
integration-cli/docker_cli_run_test.go

@@ -2750,6 +2750,38 @@ func TestContainerNetworkMode(t *testing.T) {
 	logDone("run - container shared network namespace")
 	logDone("run - container shared network namespace")
 }
 }
 
 
+func TestRunModePidHost(t *testing.T) {
+	hostPid, err := os.Readlink("/proc/1/ns/pid")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	cmd := exec.Command(dockerBinary, "run", "--pid=host", "busybox", "readlink", "/proc/self/ns/pid")
+	out2, _, err := runCommandWithOutput(cmd)
+	if err != nil {
+		t.Fatal(err, out2)
+	}
+
+	out2 = strings.Trim(out2, "\n")
+	if hostPid != out2 {
+		t.Fatalf("PID different with --pid=host %s != %s\n", hostPid, out2)
+	}
+
+	cmd = exec.Command(dockerBinary, "run", "busybox", "readlink", "/proc/self/ns/pid")
+	out2, _, err = runCommandWithOutput(cmd)
+	if err != nil {
+		t.Fatal(err, out2)
+	}
+
+	out2 = strings.Trim(out2, "\n")
+	if hostPid == out2 {
+		t.Fatalf("PID should be different without --pid=host %s == %s\n", hostPid, out2)
+	}
+	deleteAllContainers()
+
+	logDone("run - pid host mode")
+}
+
 func TestRunTLSverify(t *testing.T) {
 func TestRunTLSverify(t *testing.T) {
 	cmd := exec.Command(dockerBinary, "ps")
 	cmd := exec.Command(dockerBinary, "ps")
 	out, ec, err := runCommandWithOutput(cmd)
 	out, ec, err := runCommandWithOutput(cmd)

+ 23 - 0
runconfig/hostconfig.go

@@ -66,6 +66,27 @@ func (n IpcMode) Container() string {
 	return ""
 	return ""
 }
 }
 
 
+type PidMode string
+
+// IsPrivate indicates whether container use it's private pid stack
+func (n PidMode) IsPrivate() bool {
+	return !(n.IsHost())
+}
+
+func (n PidMode) IsHost() bool {
+	return n == "host"
+}
+
+func (n PidMode) Valid() bool {
+	parts := strings.Split(string(n), ":")
+	switch mode := parts[0]; mode {
+	case "", "host":
+	default:
+		return false
+	}
+	return true
+}
+
 type DeviceMapping struct {
 type DeviceMapping struct {
 	PathOnHost        string
 	PathOnHost        string
 	PathInContainer   string
 	PathInContainer   string
@@ -92,6 +113,7 @@ type HostConfig struct {
 	Devices         []DeviceMapping
 	Devices         []DeviceMapping
 	NetworkMode     NetworkMode
 	NetworkMode     NetworkMode
 	IpcMode         IpcMode
 	IpcMode         IpcMode
+	PidMode         PidMode
 	CapAdd          []string
 	CapAdd          []string
 	CapDrop         []string
 	CapDrop         []string
 	RestartPolicy   RestartPolicy
 	RestartPolicy   RestartPolicy
@@ -125,6 +147,7 @@ func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
 		PublishAllPorts: job.GetenvBool("PublishAllPorts"),
 		PublishAllPorts: job.GetenvBool("PublishAllPorts"),
 		NetworkMode:     NetworkMode(job.Getenv("NetworkMode")),
 		NetworkMode:     NetworkMode(job.Getenv("NetworkMode")),
 		IpcMode:         IpcMode(job.Getenv("IpcMode")),
 		IpcMode:         IpcMode(job.Getenv("IpcMode")),
+		PidMode:         PidMode(job.Getenv("PidMode")),
 	}
 	}
 
 
 	job.GetenvJson("LxcConf", &hostConfig.LxcConf)
 	job.GetenvJson("LxcConf", &hostConfig.LxcConf)

+ 8 - 1
runconfig/parse.go

@@ -46,6 +46,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 
 
 		flNetwork         = cmd.Bool([]string{"#n", "#-networking"}, true, "Enable networking for this container")
 		flNetwork         = cmd.Bool([]string{"#n", "#-networking"}, true, "Enable networking for this container")
 		flPrivileged      = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container")
 		flPrivileged      = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container")
+		flPidMode         = cmd.String([]string{"-pid"}, "", "Default is to create a private PID namespace for the container\n'host': use the host PID namespace inside the container.  Note: the host mode gives the container full access to processes on the system and is therefore considered insecure.")
 		flPublishAll      = cmd.Bool([]string{"P", "-publish-all"}, false, "Publish all exposed ports to random ports on the host interfaces")
 		flPublishAll      = cmd.Bool([]string{"P", "-publish-all"}, false, "Publish all exposed ports to random ports on the host interfaces")
 		flStdin           = cmd.Bool([]string{"i", "-interactive"}, false, "Keep STDIN open even if not attached")
 		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")
 		flTty             = cmd.Bool([]string{"t", "-tty"}, false, "Allocate a pseudo-TTY")
@@ -248,7 +249,12 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 
 
 	ipcMode := IpcMode(*flIpcMode)
 	ipcMode := IpcMode(*flIpcMode)
 	if !ipcMode.Valid() {
 	if !ipcMode.Valid() {
-		return nil, nil, cmd, fmt.Errorf("--ipc: invalid IPC mode: %v", err)
+		return nil, nil, cmd, fmt.Errorf("--ipc: invalid IPC mode")
+	}
+
+	pidMode := PidMode(*flPidMode)
+	if !pidMode.Valid() {
+		return nil, nil, cmd, fmt.Errorf("--pid: invalid PID mode")
 	}
 	}
 
 
 	netMode, err := parseNetMode(*flNetMode)
 	netMode, err := parseNetMode(*flNetMode)
@@ -300,6 +306,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 		VolumesFrom:     flVolumesFrom.GetAll(),
 		VolumesFrom:     flVolumesFrom.GetAll(),
 		NetworkMode:     netMode,
 		NetworkMode:     netMode,
 		IpcMode:         ipcMode,
 		IpcMode:         ipcMode,
+		PidMode:         pidMode,
 		Devices:         deviceMappings,
 		Devices:         deviceMappings,
 		CapAdd:          flCapAdd.GetAll(),
 		CapAdd:          flCapAdd.GetAll(),
 		CapDrop:         flCapDrop.GetAll(),
 		CapDrop:         flCapDrop.GetAll(),