Merge pull request #8299 from vieux/pr_7425

Add --security-opts options to allow user to customize container labels and apparmor profile
This commit is contained in:
Andrea Luzzardi 2014-09-30 17:53:11 -07:00
commit d40ab6f123
10 changed files with 138 additions and 7 deletions

View File

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

View File

@ -77,6 +77,7 @@ type Container struct {
daemon *Daemon daemon *Daemon
MountLabel, ProcessLabel string MountLabel, ProcessLabel string
AppArmorProfile string
RestartCount int RestartCount int
// Maps container paths to volume paths. The key in this is the path to which // 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(), ProcessLabel: c.GetProcessLabel(),
MountLabel: c.GetMountLabel(), MountLabel: c.GetMountLabel(),
LxcConfig: lxcConfig, LxcConfig: lxcConfig,
AppArmorProfile: c.AppArmorProfile,
} }
return nil return nil

View File

@ -527,6 +527,31 @@ func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint, configCmd []string)
return entrypoint, args 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) { func (daemon *Daemon) newContainer(name string, config *runconfig.Config, img *image.Image) (*Container, error) {
var ( var (
id string id string
@ -557,11 +582,8 @@ func (daemon *Daemon) newContainer(name string, config *runconfig.Config, img *i
execCommands: newExecStore(), execCommands: newExecStore(),
} }
container.root = daemon.containerRoot(container.ID) container.root = daemon.containerRoot(container.ID)
err = parseSecurityOpt(container, config)
if container.ProcessLabel, container.MountLabel, err = label.GenLabels(""); err != nil { return container, err
return nil, err
}
return container, nil
} }
func (daemon *Daemon) createRootfs(container *Container, img *image.Image) error { func (daemon *Daemon) createRootfs(container *Container, img *image.Image) error {

View File

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

View File

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

View File

@ -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 { if err := d.setupCgroups(container, c); err != nil {
return nil, err return nil, err
} }

View File

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

View File

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

View File

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

View File

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