Skip to content

Commit

Permalink
Merge pull request #10080 from crosbymichael/pid-ns
Browse files Browse the repository at this point in the history
Add --pid flag for staying in the host's pid namespace
  • Loading branch information
crosbymichael committed Jan 14, 2015
2 parents 8ac075b + 15e8f3f commit 47e3da8
Show file tree
Hide file tree
Showing 11 changed files with 123 additions and 5 deletions.
4 changes: 4 additions & 0 deletions daemon/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,9 @@ func populateCommand(c *Container, env []string) error {
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.
userSpecifiedDevices := make([]*devices.Device, len(c.hostConfig.Devices))
for i, deviceMapping := range c.hostConfig.Devices {
Expand Down Expand Up @@ -295,6 +298,7 @@ func populateCommand(c *Container, env []string) error {
WorkingDir: c.Config.WorkingDir,
Network: en,
Ipc: ipc,
Pid: pid,
Resources: resources,
AllowedDevices: allowedDevices,
AutoCreatedDevices: autoCreatedDevices,
Expand Down
6 changes: 3 additions & 3 deletions daemon/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
return nil, nil, err
}
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 {
return nil, nil, err
}
Expand Down Expand Up @@ -124,8 +124,8 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
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
}
if ipcContainer := ipcMode.Container(); ipcContainer != "" {
Expand Down
6 changes: 6 additions & 0 deletions daemon/execdriver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ type Ipc struct {
HostIpc bool `json:"host_ipc"`
}

// PID settings of the container
type Pid struct {
HostPid bool `json:"host_pid"`
}

type NetworkInterface struct {
Gateway string `json:"gateway"`
IPAddress string `json:"ip"`
Expand Down Expand Up @@ -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
Network *Network `json:"network"`
Ipc *Ipc `json:"ipc"`
Pid *Pid `json:"pid"`
Resources *Resources `json:"resources"`
Mounts []Mount `json:"mounts"`
AllowedDevices []*devices.Device `json:"allowed_devices"`
Expand Down
13 changes: 13 additions & 0 deletions daemon/execdriver/native/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Config, e
return nil, err
}

if err := d.createPid(container, c); err != nil {
return nil, err
}

if err := d.createNetwork(container, c); err != nil {
return nil, err
}
Expand Down Expand Up @@ -151,6 +155,15 @@ func (d *driver) createIpc(container *libcontainer.Config, c *execdriver.Command
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) {
container.Capabilities = capabilities.GetAllCapabilities()
container.Cgroups.AllowAllDevices = true
Expand Down
6 changes: 6 additions & 0 deletions docs/man/docker-create.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ docker-create - Create a new container
[**--net**[=*"bridge"*]]
[**-P**|**--publish-all**[=*false*]]
[**-p**|**--publish**[=*[]*]]
[**--pid**[=*[]*]]
[**--privileged**[=*false*]]
[**--restart**[=*RESTART*]]
[**--security-opt**[=*[]*]]
Expand Down Expand Up @@ -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`)
(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*
Give extended privileges to this container. The default is *false*.

Expand Down
6 changes: 6 additions & 0 deletions docs/man/docker-run.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ docker-run - Run a command in a new container
[**--net**[=*"bridge"*]]
[**-P**|**--publish-all**[=*false*]]
[**-p**|**--publish**[=*[]*]]
[**--pid**[=*[]*]]
[**--privileged**[=*false*]]
[**--restart**[=*RESTART*]]
[**--rm**[=*false*]]
Expand Down Expand Up @@ -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`)
(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*
Give extended privileges to this container. The default is *false*.

Expand Down
1 change: 1 addition & 0 deletions docs/sources/reference/commandline/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -1604,6 +1604,7 @@ removed before the image is removed.
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`)
(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
--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)
Expand Down
22 changes: 21 additions & 1 deletion docs/sources/reference/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
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="" : Set the IPC mode for the container,
'container:<name|id>': reuses another container's IPC namespace
'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.

Expand Down
32 changes: 32 additions & 0 deletions integration-cli/docker_cli_run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2750,6 +2750,38 @@ func TestContainerNetworkMode(t *testing.T) {
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) {
cmd := exec.Command(dockerBinary, "ps")
out, ec, err := runCommandWithOutput(cmd)
Expand Down
23 changes: 23 additions & 0 deletions runconfig/hostconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,27 @@ func (n IpcMode) Container() string {
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 {
PathOnHost string
PathInContainer string
Expand All @@ -92,6 +113,7 @@ type HostConfig struct {
Devices []DeviceMapping
NetworkMode NetworkMode
IpcMode IpcMode
PidMode PidMode
CapAdd []string
CapDrop []string
RestartPolicy RestartPolicy
Expand Down Expand Up @@ -125,6 +147,7 @@ func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
PublishAllPorts: job.GetenvBool("PublishAllPorts"),
NetworkMode: NetworkMode(job.Getenv("NetworkMode")),
IpcMode: IpcMode(job.Getenv("IpcMode")),
PidMode: PidMode(job.Getenv("PidMode")),
}

job.GetenvJson("LxcConf", &hostConfig.LxcConf)
Expand Down
9 changes: 8 additions & 1 deletion runconfig/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
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")
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")
Expand Down Expand Up @@ -248,7 +249,12 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe

ipcMode := IpcMode(*flIpcMode)
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)
Expand Down Expand Up @@ -300,6 +306,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
VolumesFrom: flVolumesFrom.GetAll(),
NetworkMode: netMode,
IpcMode: ipcMode,
PidMode: pidMode,
Devices: deviceMappings,
CapAdd: flCapAdd.GetAll(),
CapDrop: flCapDrop.GetAll(),
Expand Down

0 comments on commit 47e3da8

Please sign in to comment.