Browse Source

Add support for ambient capabilities

Linux kernel 4.3 and later supports "ambient capabilities" which are the
only way to pass capabilities to containers running as a non root uid.

Previously there was no way to allow containers not running as root
capabilities in a useful way.

Fix #8460

Signed-off-by: Justin Cormack <justin.cormack@docker.com>
Justin Cormack 8 years ago
parent
commit
199e19548e

+ 4 - 0
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

+ 8 - 0
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

+ 18 - 0
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)
 

+ 10 - 0
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() {