diff --git a/components/engine/docs/reference/run.md b/components/engine/docs/reference/run.md index 375c0c1538..82bffc1537 100644 --- a/components/engine/docs/reference/run.md +++ b/components/engine/docs/reference/run.md @@ -1220,6 +1220,10 @@ since Docker 1.12. In Docker 1.10 and 1.11 this did not happen and it may be nec to use a custom seccomp profile or use `--security-opt seccomp=unconfined` when adding capabilities. +It is only possible to grant capabilities to a container running as a user other than `root` +on a system with a Linux kernel version of 4.3 or later, as this requires "ambient capabilities" +to be granted. These will be added if the kernel allows it from Docker version 1.13. + ## Logging drivers (--log-driver) The container can have a different logging driver than the Docker daemon. Use diff --git a/components/engine/docs/security/security.md b/components/engine/docs/security/security.md index 7a17f869f8..16c0988aaa 100644 --- a/components/engine/docs/security/security.md +++ b/components/engine/docs/security/security.md @@ -212,6 +212,14 @@ capability removal, or less secure through the addition of capabilities. The best practice for users would be to remove all capabilities except those explicitly required for their processes. +Linux kernel versions since 4.3 allow Docker to grant capabilities to +container processes running as a non root user. This adds an extra +layer of protection as the process can then be denied access to be able +to write files belonging to the root uid, for example. User namespaces +also allow capabilities to be granted to processes that are effectively +non root, but these capabilities are limited to resources created in the +user namespace, so they have limitations. + ## Other kernel security features Capabilities are just one of the many security features provided by diff --git a/components/engine/integration-cli/docker_cli_run_unix_test.go b/components/engine/integration-cli/docker_cli_run_unix_test.go index 165dc54e66..fc690dcbcb 100644 --- a/components/engine/integration-cli/docker_cli_run_unix_test.go +++ b/components/engine/integration-cli/docker_cli_run_unix_test.go @@ -1155,6 +1155,24 @@ func (s *DockerSuite) TestRunNoNewPrivSetuid(c *check.C) { } } +func (s *DockerSuite) TestRunAmbientCapabilities(c *check.C) { + testRequires(c, DaemonIsLinux, ambientCapabilities) + + // test that a non root user can gain capabilities + runCmd := exec.Command(dockerBinary, "run", "--user", "1000", "--cap-add", "chown", "busybox", "chown", "100", "/tmp") + _, _, err := runCommandWithOutput(runCmd) + c.Assert(err, check.IsNil) + // test that non root user has default capabilities + runCmd = exec.Command(dockerBinary, "run", "--user", "1000", "busybox", "chown", "100", "/tmp") + _, _, err = runCommandWithOutput(runCmd) + c.Assert(err, check.IsNil) + // test this fails without cap_chown + runCmd = exec.Command(dockerBinary, "run", "--user", "1000", "--cap-drop", "chown", "busybox", "chown", "100", "/tmp") + out, _, err := runCommandWithOutput(runCmd) + c.Assert(err, checker.NotNil, check.Commentf(out)) + c.Assert(strings.TrimSpace(out), checker.Equals, "chown: /tmp: Operation not permitted") +} + func (s *DockerSuite) TestRunApparmorProcDirectory(c *check.C) { testRequires(c, SameHostDaemon, Apparmor) diff --git a/components/engine/integration-cli/requirements_unix.go b/components/engine/integration-cli/requirements_unix.go index aa5e0cce06..5d17c49364 100644 --- a/components/engine/integration-cli/requirements_unix.go +++ b/components/engine/integration-cli/requirements_unix.go @@ -112,6 +112,16 @@ var ( }, "Test cannot be run with 'sysctl kernel.unprivileged_userns_clone' = 0", } + ambientCapabilities = testRequirement{ + func() bool { + content, err := ioutil.ReadFile("/proc/self/status") + if err == nil && strings.Contains(string(content), "CapAmb:") { + return true + } + return false + }, + "Test cannot be run without a kernel (4.3+) supporting ambient capabilities", + } ) func init() {