Skip to content

Commit

Permalink
Add --security-opts options to allow user to customize security confi…
Browse files Browse the repository at this point in the history
…guration

security-opts will allow you to customise the security subsystem.

For example the labeling system like SELinux will run on a container.

    --security-opt="label:user:USER"   : Set the label user for the container
    --security-opt="label:role:ROLE"   : Set the label role for the container
    --security-opt="label:type:TYPE"   : Set the label type for the container
    --security-opt="label:level:LEVEL" : Set the label level for the container
    --security-opt="label:disabled"    : Turn off label confinement for the container

Since we are passing a list of string options instead of a space separated
string of options, I will change function calls to use InitLabels instead of
GenLabels.  Genlabels interface is Depracated.

Docker-DCO-1.1-Signed-off-by: Dan Walsh <dwalsh@redhat.com> (github: rhatdan)
  • Loading branch information
rhatdan committed Sep 29, 2014
1 parent d142b18 commit ead5d45
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 10 deletions.
5 changes: 3 additions & 2 deletions contrib/completion/bash/docker
Original file line number Diff line number Diff line change
Expand Up @@ -620,10 +620,11 @@ _docker_run()

case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--rm -d --detach -n --networking --privileged -P --publish-all -i --interactive -t --tty --cidfile --entrypoint -h --hostname -m --memory -u --user -w --workdir --cpuset -c --cpu-shares --sig-proxy --name -a --attach -v --volume --link -e --env -p --publish --expose --dns --volumes-from --lxc-conf" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--rm -d --detach -n --networking --privileged -P --publish-all -i --interactive -t --tty --cidfile --entrypoint -h --hostname -m --memory -u --user -w --workdir --cpuset -c --cpu-shares --sig-proxy --name -a --attach -v --volume --link -e --env -p --publish --expose --dns --volumes-from --lxc-conf --security-opt" -- "$cur" ) )
;;
*)
local counter=$(__docker_pos_first_nonflag '--cidfile|--volumes-from|-v|--volume|-e|--env|--entrypoint|-h|--hostname|-m|--memory|-u|--user|-w|--workdir|--cpuset|-c|--cpu-shares|-n|--name|-a|--attach|--link|-p|--publish|--expose|--dns|--lxc-conf')

local counter=$(__docker_pos_first_nonflag '--cidfile|--volumes-from|-v|--volume|-e|--env|--entrypoint|-h|--hostname|-m|--memory|-u|--user|-w|--workdir|--cpuset|-c|--cpu-shares|-n|--name|-a|--attach|--link|-p|--publish|--expose|--dns|--lxc-conf|--security-opt')

if [ $cword -eq $counter ]; then
__docker_image_repos_and_tags_and_ids
Expand Down
14 changes: 11 additions & 3 deletions daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -528,8 +528,9 @@ func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint, configCmd []string)

func (daemon *Daemon) newContainer(name string, config *runconfig.Config, img *image.Image) (*Container, error) {
var (
id string
err error
id string
err error
label_opts []string
)
id, name, err = daemon.generateIdAndName(name)
if err != nil {
Expand Down Expand Up @@ -557,7 +558,14 @@ func (daemon *Daemon) newContainer(name string, config *runconfig.Config, img *i
}
container.root = daemon.containerRoot(container.ID)

if container.ProcessLabel, container.MountLabel, err = label.GenLabels(""); err != nil {
for _, opt := range config.SecurityOpt {
con := strings.SplitN(opt, ":", 2)
if con[0] == "label" {
label_opts = append(label_opts, con[1])
}
}

if container.ProcessLabel, container.MountLabel, err = label.InitLabels(label_opts); err != nil {
return nil, err
}
return container, nil
Expand Down
1 change: 1 addition & 0 deletions daemon/execdriver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,5 @@ type Command struct {
CapDrop []string `json:"cap_drop"`
ContainerPid int `json:"container_pid"` // the pid for the process inside a container
ProcessConfig ProcessConfig `json:"process_config"` // Describes the init process of the container.
SecurityOpt []string `json:"security_opt"`
}
15 changes: 10 additions & 5 deletions daemon/execdriver/lxc/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -413,21 +413,26 @@ func (d *driver) generateLXCConfig(c *execdriver.Command) (string, error) {
var (
process, mount string
root = path.Join(d.root, "containers", c.ID, "config.lxc")
labels = c.Config["label"]
label_opts []string
)
fo, err := os.Create(root)
if err != nil {
return "", err
}
defer fo.Close()

if len(labels) > 0 {
process, mount, err = label.GenLabels(labels[0])
if err != nil {
return "", err
for _, opt := range c.SecurityOpt {
con := strings.SplitN(opt, ":", 2)
if con[0] == "label" {
label_opts = append(label_opts, con[1])
}
}

process, mount, err = label.InitLabels(label_opts)
if err != nil {
return "", err
}

if err := LxcTemplateCompiled.Execute(fo, struct {
*execdriver.Command
AppArmor bool
Expand Down
31 changes: 31 additions & 0 deletions docs/man/docker-run.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ docker-run - Run a command in a new container
[**--expose**[=*[]*]]
[**-h**|**--hostname**[=*HOSTNAME*]]
[**-i**|**--interactive**[=*false*]]
[**--security-opt**[=*[]*]]
[**--link**[=*[]*]]
[**--lxc-conf**[=*[]*]]
[**-m**|**--memory**[=*MEMORY*]]
Expand Down Expand Up @@ -143,6 +144,13 @@ container can be started with the **--link**.
**-i**, **--interactive**=*true*|*false*
When set to true, keep stdin open even if not attached. The default is false.

**--security-opt**=*secdriver*:*name*:*value*
"label:user:USER" : Set the label user for the container
"label:role:ROLE" : Set the label role for the container
"label:type:TYPE" : Set the label type for the container
"label:level:LEVEL" : Set the label level for the container
"label:disable" : Turn off label confinement for the container

**--link**=*name*:*alias*
Add link to another container. The format is name:alias. If the operator
uses **--link** when starting the new client container, then the client
Expand Down Expand Up @@ -383,6 +391,29 @@ to the host directory:
Now, writing to the /data1 volume in the container will be allowed and the
changes will also be reflected on the host in /var/db.

## Using alternative security labeling

If you want to use the same label for multiple containers you can override use
the security-opt flag to select an MCS level. This is a common practive for MLS
systems. But it also might help in cases where you want to share the same
content between containers. Run the following command.

# docker run --security-opt label:level:s0:c100,c200 -i -t fedora bash

Run the follwing command if you want to disable the labeling controls for just
this container.

# docker run --security-opt label:disable -i -t fedora bash

If you decide you would like to work with a tighter policy on your container.
For example if you want to run a container that could only listen on apache
ports, and not connect to the network. You could select an alternate type to
run the container execute the following command.

# docker run --security-opt label:type:svirt_apache_t -i -t fedora bash

Note: You would have to write policy defining a svirt_apache_t type.

# HISTORY
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
based on docker.com source material and internal work.
Expand Down
26 changes: 26 additions & 0 deletions docs/sources/reference/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,32 @@ the container exits**, you can add the `--rm` flag:

--rm=false: Automatically remove the container when it exits (incompatible with -d)

## Security Configuration
--security-opt="label:user:USER" : Set the label user for the container
--security-opt="label:role:ROLE" : Set the label role for the container
--security-opt="label:type:TYPE" : Set the label type for the container
--security-opt="label:level:LEVEL" : Set the label level for the container
--security-opt="label:disable" : Turn off label confinement for the container

If you want to use the same label for multiple containers you can override use
the security-opt flag to select an MCS level. This is a common practive for MLS
systems. But it also might help in cases where you want to share the same
content between containers. Run the following command.

# docker run --security-opt label:level:s0:c100,c200 -i -t fedora bash

Run the follwing command if you want to disable the labeling controls for just
this container.

# docker run --security-opt label:disable -i -t fedora bash

If you decide you would like to work with a tighter policy on your container.
For example if you want to run a container that could only listen on apache
ports, and not connect to the network. You could select an alternate type to
run the container execute the following command.

# docker run --security-opt label:type:svirt_apache_t -i -t fedora bash

## Runtime Constraints on CPU and Memory

The operator can also adjust the performance parameters of the
Expand Down
37 changes: 37 additions & 0 deletions integration-cli/docker_cli_run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (

"github.com/docker/docker/pkg/mount"
"github.com/docker/docker/pkg/networkfs/resolvconf"
"github.com/docker/libcontainer/label"
"github.com/kr/pty"
)

Expand Down Expand Up @@ -1719,6 +1720,42 @@ func TestRunWriteResolvFileAndNotCommit(t *testing.T) {
logDone("run - write to /etc/resolv.conf and not commited")
}

func TestRunSecurityOptLevel(t *testing.T) {
plabel, _, _ := label.InitLabels(nil)
if plabel != "" {
defer deleteAllContainers()
cmd := exec.Command(dockerBinary, "run", "--security-opt", "label:level:s0:c0,c100", "busybox", "ps", "-eZ")
out, _, err := runCommandWithOutput(cmd)
if err != nil {
t.Fatal(err, out)
}
id := strings.TrimSpace(out)
if !strings.ContainsAny(id, "s0:c0,c100") {
t.Fatal("security-opt label:level:s0:c0,c100 failed")
}
}

logDone("run - security-opt label:level")
}

func TestRunSecurityOptDisable(t *testing.T) {
plabel, _, _ := label.InitLabels(nil)
if plabel != "" {
defer deleteAllContainers()
cmd := exec.Command(dockerBinary, "run", "--security-opt", "label:disable", "busybox", "ps", "-eZ")
out, _, err := runCommandWithOutput(cmd)
if err != nil {
t.Fatal(err, out)
}
id := strings.TrimSpace(out)
if !strings.ContainsAny(id, "svirt") {
t.Fatal("security-opt label:level:disable failed")
}
}

logDone("run - security-opt label:disable")
}

func TestRunWithBadDevice(t *testing.T) {
name := "baddevice"
cmd := exec.Command(dockerBinary, "run", "--name", name, "--device", "/etc", "busybox", "true")
Expand Down
2 changes: 2 additions & 0 deletions runconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type Config struct {
Entrypoint []string
NetworkDisabled bool
OnBuild []string
SecurityOpt []string
}

func ContainerConfigFromJob(job *engine.Job) *Config {
Expand All @@ -55,6 +56,7 @@ func ContainerConfigFromJob(job *engine.Job) *Config {
}
job.GetenvJson("ExposedPorts", &config.ExposedPorts)
job.GetenvJson("Volumes", &config.Volumes)
config.SecurityOpt = job.GetenvList("SecurityOpt")
if PortSpecs := job.GetenvList("PortSpecs"); PortSpecs != nil {
config.PortSpecs = PortSpecs
}
Expand Down
3 changes: 3 additions & 0 deletions runconfig/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func Parse(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config,
flEnvFile = opts.NewListOpts(nil)
flCapAdd = opts.NewListOpts(nil)
flCapDrop = opts.NewListOpts(nil)
flSecurityOpt = opts.NewListOpts(nil)

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")
Expand Down Expand Up @@ -79,6 +80,7 @@ func Parse(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config,

cmd.Var(&flCapAdd, []string{"-cap-add"}, "Add Linux capabilities")
cmd.Var(&flCapDrop, []string{"-cap-drop"}, "Drop Linux capabilities")
cmd.Var(&flSecurityOpt, []string{"-security-opt"}, "Security Options")

if err := cmd.Parse(args); err != nil {
return nil, nil, cmd, err
Expand Down Expand Up @@ -254,6 +256,7 @@ func Parse(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config,
Volumes: flVolumes.GetMap(),
Entrypoint: entrypoint,
WorkingDir: *flWorkingDir,
SecurityOpt: flSecurityOpt.GetAll(),
}

hostConfig := &HostConfig{
Expand Down

0 comments on commit ead5d45

Please sign in to comment.