Ver código fonte

Add --security-opts options to allow user to customize security configuration

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)
Dan Walsh 10 anos atrás
pai
commit
87e732a0f3

+ 3 - 2
contrib/completion/bash/docker

@@ -620,10 +620,11 @@ _docker_run()
 
 
 	case "$cur" in
 	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
 			if [ $cword -eq $counter ]; then
 				__docker_image_repos_and_tags_and_ids
 				__docker_image_repos_and_tags_and_ids

+ 11 - 3
daemon/daemon.go

@@ -529,8 +529,9 @@ func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint, configCmd []string)
 
 
 func (daemon *Daemon) newContainer(name string, config *runconfig.Config, img *image.Image) (*Container, error) {
 func (daemon *Daemon) newContainer(name string, config *runconfig.Config, img *image.Image) (*Container, error) {
 	var (
 	var (
-		id  string
-		err error
+		id         string
+		err        error
+		label_opts []string
 	)
 	)
 	id, name, err = daemon.generateIdAndName(name)
 	id, name, err = daemon.generateIdAndName(name)
 	if err != nil {
 	if err != nil {
@@ -558,7 +559,14 @@ func (daemon *Daemon) newContainer(name string, config *runconfig.Config, img *i
 	}
 	}
 	container.root = daemon.containerRoot(container.ID)
 	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 nil, err
 	}
 	}
 	return container, nil
 	return container, nil

+ 4 - 1
daemon/execdriver/lxc/driver.go

@@ -409,7 +409,10 @@ func rootIsShared() bool {
 }
 }
 
 
 func (d *driver) generateLXCConfig(c *execdriver.Command) (string, error) {
 func (d *driver) generateLXCConfig(c *execdriver.Command) (string, error) {
-	root := path.Join(d.root, "containers", c.ID, "config.lxc")
+	var (
+		root       = path.Join(d.root, "containers", c.ID, "config.lxc")
+		label_opts []string
+	)
 
 
 	fo, err := os.Create(root)
 	fo, err := os.Create(root)
 	if err != nil {
 	if err != nil {

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

@@ -23,6 +23,7 @@ docker-run - Run a command in a new container
 [**--expose**[=*[]*]]
 [**--expose**[=*[]*]]
 [**-h**|**--hostname**[=*HOSTNAME*]]
 [**-h**|**--hostname**[=*HOSTNAME*]]
 [**-i**|**--interactive**[=*false*]]
 [**-i**|**--interactive**[=*false*]]
+[**--security-opt**[=*[]*]]
 [**--link**[=*[]*]]
 [**--link**[=*[]*]]
 [**--lxc-conf**[=*[]*]]
 [**--lxc-conf**[=*[]*]]
 [**-m**|**--memory**[=*MEMORY*]]
 [**-m**|**--memory**[=*MEMORY*]]
@@ -143,6 +144,13 @@ container can be started with the **--link**.
 **-i**, **--interactive**=*true*|*false*
 **-i**, **--interactive**=*true*|*false*
    When set to true, keep stdin open even if not attached. The default is 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*
 **--link**=*name*:*alias*
    Add link to another container. The format is name:alias. If the operator
    Add link to another container. The format is name:alias. If the operator
 uses **--link** when starting the new client container, then the client
 uses **--link** when starting the new client container, then the client
@@ -383,6 +391,29 @@ to the host directory:
 Now, writing to the /data1 volume in the container will be allowed and the
 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.
 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
 # HISTORY
 April 2014, Originally compiled by William Henry (whenry at redhat dot com)
 April 2014, Originally compiled by William Henry (whenry at redhat dot com)
 based on docker.com source material and internal work.
 based on docker.com source material and internal work.

+ 26 - 0
docs/sources/reference/run.md

@@ -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)
     --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
 ## Runtime Constraints on CPU and Memory
 
 
 The operator can also adjust the performance parameters of the
 The operator can also adjust the performance parameters of the

+ 37 - 0
integration-cli/docker_cli_run_test.go

@@ -19,6 +19,7 @@ import (
 
 
 	"github.com/docker/docker/pkg/mount"
 	"github.com/docker/docker/pkg/mount"
 	"github.com/docker/docker/pkg/networkfs/resolvconf"
 	"github.com/docker/docker/pkg/networkfs/resolvconf"
+	"github.com/docker/libcontainer/label"
 	"github.com/kr/pty"
 	"github.com/kr/pty"
 )
 )
 
 
@@ -1719,6 +1720,42 @@ func TestRunWriteResolvFileAndNotCommit(t *testing.T) {
 	logDone("run - write to /etc/resolv.conf and not commited")
 	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) {
 func TestRunWithBadDevice(t *testing.T) {
 	name := "baddevice"
 	name := "baddevice"
 	cmd := exec.Command(dockerBinary, "run", "--name", name, "--device", "/etc", "busybox", "true")
 	cmd := exec.Command(dockerBinary, "run", "--name", name, "--device", "/etc", "busybox", "true")

+ 2 - 0
runconfig/config.go

@@ -32,6 +32,7 @@ type Config struct {
 	Entrypoint      []string
 	Entrypoint      []string
 	NetworkDisabled bool
 	NetworkDisabled bool
 	OnBuild         []string
 	OnBuild         []string
+	SecurityOpt     []string
 }
 }
 
 
 func ContainerConfigFromJob(job *engine.Job) *Config {
 func ContainerConfigFromJob(job *engine.Job) *Config {
@@ -55,6 +56,7 @@ func ContainerConfigFromJob(job *engine.Job) *Config {
 	}
 	}
 	job.GetenvJson("ExposedPorts", &config.ExposedPorts)
 	job.GetenvJson("ExposedPorts", &config.ExposedPorts)
 	job.GetenvJson("Volumes", &config.Volumes)
 	job.GetenvJson("Volumes", &config.Volumes)
+	config.SecurityOpt = job.GetenvList("SecurityOpt")
 	if PortSpecs := job.GetenvList("PortSpecs"); PortSpecs != nil {
 	if PortSpecs := job.GetenvList("PortSpecs"); PortSpecs != nil {
 		config.PortSpecs = PortSpecs
 		config.PortSpecs = PortSpecs
 	}
 	}

+ 3 - 0
runconfig/parse.go

@@ -43,6 +43,7 @@ func Parse(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config,
 		flEnvFile     = opts.NewListOpts(nil)
 		flEnvFile     = opts.NewListOpts(nil)
 		flCapAdd      = opts.NewListOpts(nil)
 		flCapAdd      = opts.NewListOpts(nil)
 		flCapDrop     = opts.NewListOpts(nil)
 		flCapDrop     = opts.NewListOpts(nil)
+		flSecurityOpt = opts.NewListOpts(nil)
 
 
 		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")
@@ -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(&flCapAdd, []string{"-cap-add"}, "Add Linux capabilities")
 	cmd.Var(&flCapDrop, []string{"-cap-drop"}, "Drop 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 {
 	if err := cmd.Parse(args); err != nil {
 		return nil, nil, cmd, err
 		return nil, nil, cmd, err
@@ -254,6 +256,7 @@ func Parse(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config,
 		Volumes:         flVolumes.GetMap(),
 		Volumes:         flVolumes.GetMap(),
 		Entrypoint:      entrypoint,
 		Entrypoint:      entrypoint,
 		WorkingDir:      *flWorkingDir,
 		WorkingDir:      *flWorkingDir,
+		SecurityOpt:     flSecurityOpt.GetAll(),
 	}
 	}
 
 
 	hostConfig := &HostConfig{
 	hostConfig := &HostConfig{