Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --security-opts options to allow user to customize container labels. #7425

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see where this one is filled from config.SecurityOpt

}
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need a comma after "containers" or maybe after "override". I can't quite tell where that dependent clause is supposed to end. Maybe there's a missing word or something too, the independent clause doesn't quite make sense.

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a complete sentence.

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a complete sentence; need a comma after "example".

run the container execute the following command.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be "by executing 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")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

busybox compiled ps doesn't support -Z

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")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

busybox compiled ps doesn't support -Z

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