Skip to content

Commit

Permalink
Merge pull request #20727 from mrunalp/no_new_priv
Browse files Browse the repository at this point in the history
Add support for NoNewPrivileges in docker
  • Loading branch information
cpuguy83 committed Mar 8, 2016
2 parents 38e1cd1 + 74bb1ce commit dc702b6
Show file tree
Hide file tree
Showing 13 changed files with 89 additions and 13 deletions.
1 change: 1 addition & 0 deletions container/container_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type Container struct {
ShmPath string
ResolvConfPath string
SeccompProfile string
NoNewPrivileges bool
}

// CreateDaemonEnvironment returns the list of all environment variables given the list of
Expand Down
9 changes: 9 additions & 0 deletions contrib/nnp-test/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM buildpack-deps:jessie

COPY . /usr/src/

WORKDIR /usr/src/

RUN gcc -g -Wall -static nnp-test.c -o /usr/bin/nnp-test

RUN chmod +s /usr/bin/nnp-test
10 changes: 10 additions & 0 deletions contrib/nnp-test/nnp-test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main(int argc, char *argv[])
{
printf("EUID=%d\n", geteuid());
return 0;
}

1 change: 1 addition & 0 deletions daemon/container_operations_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ func (daemon *Daemon) populateCommand(c *container.Container, env []string) erro
SeccompProfile: c.SeccompProfile,
UIDMapping: uidMap,
UTS: uts,
NoNewPrivileges: c.NoNewPrivileges,
}
if c.HostConfig.CgroupParent != "" {
c.Command.CgroupParent = c.HostConfig.CgroupParent
Expand Down
28 changes: 17 additions & 11 deletions daemon/daemon_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,23 @@ func parseSecurityOpt(container *container.Container, config *containertypes.Hos
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":
labelOpts = append(labelOpts, con[1])
case "apparmor":
container.AppArmorProfile = con[1]
case "seccomp":
container.SeccompProfile = con[1]
default:
return fmt.Errorf("Invalid --security-opt: %q", opt)
switch con[0] {
case "no-new-privileges":
container.NoNewPrivileges = true
default:
return fmt.Errorf("Invalid --security-opt 1: %q", opt)
}
} else {
switch con[0] {
case "label":
labelOpts = append(labelOpts, con[1])
case "apparmor":
container.AppArmorProfile = con[1]
case "seccomp":
container.SeccompProfile = con[1]
default:
return fmt.Errorf("Invalid --security-opt 2: %q", opt)
}
}
}

Expand Down
1 change: 1 addition & 0 deletions daemon/execdriver/driver_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ type Command struct {
SeccompProfile string `json:"seccomp_profile"`
UIDMapping []idtools.IDMap `json:"uidmapping"`
UTS *UTS `json:"uts"`
NoNewPrivileges bool `json:"no_new_privileges"`
}

// SetRootPropagation sets the root mount propagation mode.
Expand Down
2 changes: 2 additions & 0 deletions daemon/execdriver/native/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ func (d *Driver) createContainer(c *execdriver.Command, hooks execdriver.Hooks)

d.setupLabels(container, c)
d.setupRlimits(container, c)

container.NoNewPrivileges = c.NoNewPrivileges
return container, nil
}

Expand Down
9 changes: 9 additions & 0 deletions docs/reference/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,8 @@ with the same logic -- if the original volume was specified with a name it will
--security-opt="label:disable" : Turn off label confinement for the container
--security-opt="apparmor:PROFILE" : Set the apparmor profile to be applied
to the container
--security-opt="no-new-privileges" : Disable container processes from gaining
new privileges

You can override the default labeling scheme for each container by specifying
the `--security-opt` flag. For example, you can specify the MCS/MLS level, a
Expand All @@ -631,6 +633,13 @@ command:

> **Note**: You would have to write policy defining a `svirt_apache_t` type.
If you want to prevent your container processes from gaining additional
privileges, you can execute the following command:

$ docker run --security-opt no-new-privileges -it centos bash

For more details, see [kernel documentation](https://www.kernel.org/doc/Documentation/prctl/no_new_privs.txt).

## Specifying custom cgroups

Using the `--cgroup-parent` flag, you can pass a specific cgroup to run a
Expand Down
22 changes: 22 additions & 0 deletions hack/make/.ensure-nnp-test
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash
set -e

# Build a C binary for testing no-new-privileges
# and compile it for target daemon
if [ "$DOCKER_ENGINE_GOOS" = "linux" ]; then
if [ "$DOCKER_ENGINE_OSARCH" = "$DOCKER_CLIENT_OSARCH" ]; then
tmpdir=$(mktemp -d)
gcc -g -Wall -static contrib/nnp-test/nnp-test.c -o "${tmpdir}/nnp-test"

dockerfile="${tmpdir}/Dockerfile"
cat <<-EOF > "$dockerfile"
FROM debian:jessie
COPY . /usr/bin/
RUN chmod +s /usr/bin/nnp-test
EOF
docker build --force-rm ${DOCKER_BUILD_ARGS} -qt nnp-test "${tmpdir}" > /dev/null
rm -rf "${tmpdir}"
else
docker build ${DOCKER_BUILD_ARGS} -qt nnp-test contrib/nnp-test > /dev/null
fi
fi
1 change: 1 addition & 0 deletions hack/make/.integration-daemon-setup
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ if [ $DOCKER_ENGINE_GOOS != "windows" ]; then
bundle .ensure-frozen-images
bundle .ensure-httpserver
bundle .ensure-syscall-test
bundle .ensure-nnp-test
else
# Note this is Windows to Windows CI, not Windows to Linux CI
bundle .ensure-frozen-images-windows
Expand Down
12 changes: 12 additions & 0 deletions integration-cli/docker_cli_run_unix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,18 @@ func (s *DockerSuite) TestRunSeccompDefaultProfile(c *check.C) {
}
}

// TestRunNoNewPrivSetuid checks that --security-opt=no-new-privileges prevents
// effective uid transtions on executing setuid binaries.
func (s *DockerSuite) TestRunNoNewPrivSetuid(c *check.C) {
testRequires(c, DaemonIsLinux, NotUserNamespace, SameHostDaemon)

// test that running a setuid binary results in no effective uid transition
runCmd := exec.Command(dockerBinary, "run", "--security-opt", "no-new-privileges", "--user", "1000", "nnp-test", "/usr/bin/nnp-test")
if out, _, err := runCommandWithOutput(runCmd); err != nil || !strings.Contains(out, "EUID=1000") {
c.Fatalf("expected output to contain EUID=1000, got %s: %v", out, err)
}
}

func (s *DockerSuite) TestRunApparmorProcDirectory(c *check.C) {
testRequires(c, SameHostDaemon, Apparmor)

Expand Down
2 changes: 2 additions & 0 deletions man/docker-run.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,8 @@ its root filesystem mounted as read only prohibiting any writes.
"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
"no-new-privileges" : Disable container processes from gaining additional privileges


**--stop-signal**=*SIGTERM*
Signal to stop a container. Default is SIGTERM.
Expand Down
4 changes: 2 additions & 2 deletions runconfig/opts/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -500,8 +500,8 @@ func parseLoggingOpts(loggingDriver string, loggingOpts []string) (map[string]st
func parseSecurityOpts(securityOpts []string) ([]string, error) {
for key, opt := range securityOpts {
con := strings.SplitN(opt, ":", 2)
if len(con) == 1 {
return securityOpts, fmt.Errorf("invalid --security-opt: %q", opt)
if len(con) == 1 && con[0] != "no-new-privileges" {
return securityOpts, fmt.Errorf("Invalid --security-opt: %q", opt)
}
if con[0] == "seccomp" && con[1] != "unconfined" {
f, err := ioutil.ReadFile(con[1])
Expand Down

0 comments on commit dc702b6

Please sign in to comment.