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:
commit
d40ab6f123
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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{
|
||||||
|
|
Loading…
Reference in New Issue