Переглянути джерело

Merge pull request #8299 from vieux/pr_7425

Add --security-opts options to allow user to customize container labels and apparmor profile
Andrea Luzzardi 10 роки тому
батько
коміт
d40ab6f123

+ 3 - 2
contrib/completion/bash/docker

@@ -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

+ 2 - 0
daemon/container.go

@@ -77,6 +77,7 @@ type Container struct {
 
 	daemon                   *Daemon
 	MountLabel, ProcessLabel string
+	AppArmorProfile          string
 	RestartCount             int
 
 	// Maps container paths to volume paths.  The key in this is the path to which
@@ -275,6 +276,7 @@ func populateCommand(c *Container, env []string) error {
 		ProcessLabel:       c.GetProcessLabel(),
 		MountLabel:         c.GetMountLabel(),
 		LxcConfig:          lxcConfig,
+		AppArmorProfile:    c.AppArmorProfile,
 	}
 
 	return nil

+ 27 - 5
daemon/daemon.go

@@ -527,6 +527,31 @@ func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint, configCmd []string)
 	return entrypoint, args
 }
 
+func parseSecurityOpt(container *Container, config *runconfig.Config) error {
+	var (
+		label_opts []string
+		err        error
+	)
+
+	for _, opt := range config.SecurityOpt {
+		con := strings.SplitN(opt, ":", 2)
+		if len(con) == 1 {
+			return fmt.Errorf("Invalid --security-opt: %q", opt)
+		}
+		switch con[0] {
+		case "label":
+			label_opts = append(label_opts, con[1])
+		case "apparmor":
+			container.AppArmorProfile = con[1]
+		default:
+			return fmt.Errorf("Invalid --security-opt: %q", opt)
+		}
+	}
+
+	container.ProcessLabel, container.MountLabel, err = label.InitLabels(label_opts)
+	return err
+}
+
 func (daemon *Daemon) newContainer(name string, config *runconfig.Config, img *image.Image) (*Container, error) {
 	var (
 		id  string
@@ -557,11 +582,8 @@ func (daemon *Daemon) newContainer(name string, config *runconfig.Config, img *i
 		execCommands:    newExecStore(),
 	}
 	container.root = daemon.containerRoot(container.ID)
-
-	if container.ProcessLabel, container.MountLabel, err = label.GenLabels(""); err != nil {
-		return nil, err
-	}
-	return container, nil
+	err = parseSecurityOpt(container, config)
+	return container, err
 }
 
 func (daemon *Daemon) createRootfs(container *Container, img *image.Image) error {

+ 39 - 0
daemon/daemon_unit_test.go

@@ -0,0 +1,39 @@
+package daemon
+
+import (
+	"testing"
+
+	"github.com/docker/docker/runconfig"
+)
+
+func TestParseSecurityOpt(t *testing.T) {
+	container := &Container{}
+	config := &runconfig.Config{}
+
+	// test apparmor
+	config.SecurityOpt = []string{"apparmor:test_profile"}
+	if err := parseSecurityOpt(container, config); err != nil {
+		t.Fatalf("Unexpected parseSecurityOpt error: %v", err)
+	}
+	if container.AppArmorProfile != "test_profile" {
+		t.Fatalf("Unexpected AppArmorProfile, expected: \"test_profile\", got %q", container.AppArmorProfile)
+	}
+
+	// test valid label
+	config.SecurityOpt = []string{"label:user:USER"}
+	if err := parseSecurityOpt(container, config); err != nil {
+		t.Fatalf("Unexpected parseSecurityOpt error: %v", err)
+	}
+
+	// test invalid label
+	config.SecurityOpt = []string{"label"}
+	if err := parseSecurityOpt(container, config); err == nil {
+		t.Fatal("Expected parseSecurityOpt error, got nil")
+	}
+
+	// test invalid opt
+	config.SecurityOpt = []string{"test"}
+	if err := parseSecurityOpt(container, config); err == nil {
+		t.Fatal("Expected parseSecurityOpt error, got nil")
+	}
+}

+ 1 - 0
daemon/execdriver/driver.go

@@ -116,4 +116,5 @@ type Command struct {
 	ProcessLabel       string            `json:"process_label"`
 	MountLabel         string            `json:"mount_label"`
 	LxcConfig          []string          `json:"lxc_config"`
+	AppArmorProfile    string            `json:"apparmor_profile"`
 }

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

@@ -50,6 +50,10 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Config, e
 		}
 	}
 
+	if c.AppArmorProfile != "" {
+		container.AppArmorProfile = c.AppArmorProfile
+	}
+
 	if err := d.setupCgroups(container, c); err != nil {
 		return nil, err
 	}

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

@@ -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*]]
@@ -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
@@ -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 practice 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.

+ 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)
 
+## 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
+    --secutity-opt="apparmor:PROFILE"  : Set the apparmor profile to be applied 
+                                         to 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 practice 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 following command if you want to disable the labeling controls for just 
+this container.
+
+    # docker run --security-opt label:disable -i -t fedora bash
+
+Run the following command if you want to run a container that could only listen
+on apache ports.
+
+    # 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

+ 2 - 0
runconfig/config.go

@@ -32,6 +32,7 @@ type Config struct {
 	Entrypoint      []string
 	NetworkDisabled bool
 	OnBuild         []string
+	SecurityOpt     []string
 }
 
 func ContainerConfigFromJob(job *engine.Job) *Config {
@@ -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
 	}

+ 3 - 0
runconfig/parse.go

@@ -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")
@@ -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
@@ -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{