Signed-off-by: Jessica Frazelle <acidburn@docker.com>
| ... | ... |
@@ -197,6 +197,7 @@ RUN ln -sv $PWD/contrib/completion/bash/docker /etc/bash_completion.d/docker |
| 197 | 197 |
COPY contrib/download-frozen-image-v2.sh /go/src/github.com/docker/docker/contrib/ |
| 198 | 198 |
RUN ./contrib/download-frozen-image-v2.sh /docker-frozen-images \ |
| 199 | 199 |
busybox:latest@sha256:eb3c0d4680f9213ee5f348ea6d39489a1f85a318a2ae09e012c426f78252a6d2 \ |
| 200 |
+ debian:jessie@sha256:24a900d1671b269d6640b4224e7b63801880d8e3cb2bcbfaa10a5dddcf4469ed \ |
|
| 200 | 201 |
hello-world:latest@sha256:8be990ef2aeb16dbcb9271ddfe2610fa6658d13f6dfb8bc72074cc1ca36966a7 \ |
| 201 | 202 |
jess/unshare:latest@sha256:2e3a8c0591c4690b82d4eba7e5ef8f49f2ddfe9f867f3e865198db9bd1436c5b |
| 202 | 203 |
# see also "hack/make/.ensure-frozen-images" (which needs to be updated any time this list is) |
| 0 | 3 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,54 @@ |
| 0 |
+#define _GNU_SOURCE |
|
| 1 |
+#include <sched.h> |
|
| 2 |
+#include <unistd.h> |
|
| 3 |
+#include <stdlib.h> |
|
| 4 |
+#include <sys/wait.h> |
|
| 5 |
+#include <signal.h> |
|
| 6 |
+#include <fcntl.h> |
|
| 7 |
+#include <stdio.h> |
|
| 8 |
+#include <string.h> |
|
| 9 |
+#include <limits.h> |
|
| 10 |
+#include <errno.h> |
|
| 11 |
+ |
|
| 12 |
+#define STACKSIZE (1024*1024) |
|
| 13 |
+static char child_stack[STACKSIZE]; |
|
| 14 |
+ |
|
| 15 |
+struct clone_args {
|
|
| 16 |
+ char **argv; |
|
| 17 |
+}; |
|
| 18 |
+ |
|
| 19 |
+// child_exec is the func that will be executed as the result of clone |
|
| 20 |
+static int child_exec(void *stuff) |
|
| 21 |
+{
|
|
| 22 |
+ struct clone_args *args = (struct clone_args *)stuff; |
|
| 23 |
+ if (execvp(args->argv[0], args->argv) != 0) {
|
|
| 24 |
+ fprintf(stderr, "failed to execvp argments %s\n", |
|
| 25 |
+ strerror(errno)); |
|
| 26 |
+ exit(-1); |
|
| 27 |
+ } |
|
| 28 |
+ // we should never reach here! |
|
| 29 |
+ exit(EXIT_FAILURE); |
|
| 30 |
+} |
|
| 31 |
+ |
|
| 32 |
+int main(int argc, char **argv) |
|
| 33 |
+{
|
|
| 34 |
+ struct clone_args args; |
|
| 35 |
+ args.argv = &argv[1]; |
|
| 36 |
+ |
|
| 37 |
+ int clone_flags = CLONE_NEWUSER | SIGCHLD; |
|
| 38 |
+ |
|
| 39 |
+ // the result of this call is that our child_exec will be run in another |
|
| 40 |
+ // process returning it's pid |
|
| 41 |
+ pid_t pid = |
|
| 42 |
+ clone(child_exec, child_stack + STACKSIZE, clone_flags, &args); |
|
| 43 |
+ if (pid < 0) {
|
|
| 44 |
+ fprintf(stderr, "clone failed: %s\n", strerror(errno)); |
|
| 45 |
+ exit(EXIT_FAILURE); |
|
| 46 |
+ } |
|
| 47 |
+ // lets wait on our child process here before we, the parent, exits |
|
| 48 |
+ if (waitpid(pid, NULL, 0) == -1) {
|
|
| 49 |
+ fprintf(stderr, "failed to wait pid %d\n", pid); |
|
| 50 |
+ exit(EXIT_FAILURE); |
|
| 51 |
+ } |
|
| 52 |
+ exit(EXIT_SUCCESS); |
|
| 53 |
+} |
| 33 | 34 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,18 @@ |
| 0 |
+#!/bin/bash |
|
| 1 |
+set -e |
|
| 2 |
+ |
|
| 3 |
+# Build a C binary for cloning a userns for seccomp tests |
|
| 4 |
+# and compile it for target daemon |
|
| 5 |
+ |
|
| 6 |
+dir="$DEST/userns-test" |
|
| 7 |
+mkdir -p "$dir" |
|
| 8 |
+( |
|
| 9 |
+ GOOS=${DOCKER_ENGINE_GOOS:="linux"}
|
|
| 10 |
+ if [ "$GOOS" = "linux" ]; then |
|
| 11 |
+ cd "$dir" |
|
| 12 |
+ gcc -g -Wall -static ../../../../contrib/userns-test/main.c -o ./userns-test |
|
| 13 |
+ cp ../../../../contrib/userns-test/Dockerfile . |
|
| 14 |
+ docker build -qt userns-test . > /dev/null |
|
| 15 |
+ fi |
|
| 16 |
+) |
|
| 17 |
+rm -rf "$dir" |
| ... | ... |
@@ -2858,7 +2858,7 @@ func (s *DockerSuite) TestRunUnshareProc(c *check.C) {
|
| 2858 | 2858 |
testRequires(c, Apparmor, DaemonIsLinux, NotUserNamespace) |
| 2859 | 2859 |
|
| 2860 | 2860 |
name := "acidburn" |
| 2861 |
- out, _, err := dockerCmdWithError("run", "--name", name, "jess/unshare", "unshare", "-p", "-m", "-f", "-r", "--mount-proc=/proc", "mount")
|
|
| 2861 |
+ out, _, err := dockerCmdWithError("run", "--name", name, "--security-opt", "seccomp:unconfined", "jess/unshare", "unshare", "-p", "-m", "-f", "-r", "--mount-proc=/proc", "mount")
|
|
| 2862 | 2862 |
if err == nil || |
| 2863 | 2863 |
!(strings.Contains(strings.ToLower(out), "permission denied") || |
| 2864 | 2864 |
strings.Contains(strings.ToLower(out), "operation not permitted")) {
|
| ... | ... |
@@ -2866,7 +2866,7 @@ func (s *DockerSuite) TestRunUnshareProc(c *check.C) {
|
| 2866 | 2866 |
} |
| 2867 | 2867 |
|
| 2868 | 2868 |
name = "cereal" |
| 2869 |
- out, _, err = dockerCmdWithError("run", "--name", name, "jess/unshare", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc")
|
|
| 2869 |
+ out, _, err = dockerCmdWithError("run", "--name", name, "--security-opt", "seccomp:unconfined", "jess/unshare", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc")
|
|
| 2870 | 2870 |
if err == nil || |
| 2871 | 2871 |
!(strings.Contains(strings.ToLower(out), "permission denied") || |
| 2872 | 2872 |
strings.Contains(strings.ToLower(out), "operation not permitted")) {
|
| ... | ... |
@@ -2875,7 +2875,7 @@ func (s *DockerSuite) TestRunUnshareProc(c *check.C) {
|
| 2875 | 2875 |
|
| 2876 | 2876 |
/* Ensure still fails if running privileged with the default policy */ |
| 2877 | 2877 |
name = "crashoverride" |
| 2878 |
- out, _, err = dockerCmdWithError("run", "--privileged", "--security-opt", "apparmor:docker-default", "--name", name, "jess/unshare", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc")
|
|
| 2878 |
+ out, _, err = dockerCmdWithError("run", "--privileged", "--security-opt", "seccomp:unconfined", "--security-opt", "apparmor:docker-default", "--name", name, "jess/unshare", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc")
|
|
| 2879 | 2879 |
if err == nil || !(strings.Contains(strings.ToLower(out), "permission denied") || strings.Contains(strings.ToLower(out), "operation not permitted")) {
|
| 2880 | 2880 |
c.Fatalf("privileged unshare with apparmor should have failed with permission denied, got: %s, %v", out, err)
|
| 2881 | 2881 |
} |
| ... | ... |
@@ -514,7 +514,7 @@ func (s *DockerSuite) TestRunSeccompProfileDenyUnshare(c *check.C) {
|
| 514 | 514 |
if _, err := tmpFile.Write([]byte(jsonData)); err != nil {
|
| 515 | 515 |
c.Fatal(err) |
| 516 | 516 |
} |
| 517 |
- runCmd := exec.Command(dockerBinary, "run", "--security-opt", "seccomp:"+tmpFile.Name(), "jess/unshare", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc") |
|
| 517 |
+ runCmd := exec.Command(dockerBinary, "run", "--security-opt", "apparmor:unconfined", "--security-opt", "seccomp:"+tmpFile.Name(), "debian:jessie", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc") |
|
| 518 | 518 |
out, _, _ := runCommandWithOutput(runCmd) |
| 519 | 519 |
if !strings.Contains(out, "Operation not permitted") {
|
| 520 | 520 |
c.Fatalf("expected unshare with seccomp profile denied to fail, got %s", out)
|
| ... | ... |
@@ -549,8 +549,9 @@ func (s *DockerSuite) TestRunSeccompProfileDenyChmod(c *check.C) {
|
| 549 | 549 |
} |
| 550 | 550 |
} |
| 551 | 551 |
|
| 552 |
-// TestRunSeccompProfileDenyUserns checks that 'docker run jess/unshare unshare --map-root-user --user sh -c whoami' exits with operation not permitted. |
|
| 553 |
-func (s *DockerSuite) TestRunSeccompProfileDenyUserns(c *check.C) {
|
|
| 552 |
+// TestRunSeccompProfileDenyUnshareUserns checks that 'docker run jess/unshare unshare --map-root-user --user sh -c whoami' with a specific profile to |
|
| 553 |
+// deny unhare of a userns exits with operation not permitted. |
|
| 554 |
+func (s *DockerSuite) TestRunSeccompProfileDenyUnshareUserns(c *check.C) {
|
|
| 554 | 555 |
testRequires(c, SameHostDaemon, seccompEnabled) |
| 555 | 556 |
// from sched.h |
| 556 | 557 |
jsonData := fmt.Sprintf(`{
|
| ... | ... |
@@ -578,9 +579,44 @@ func (s *DockerSuite) TestRunSeccompProfileDenyUserns(c *check.C) {
|
| 578 | 578 |
if _, err := tmpFile.Write([]byte(jsonData)); err != nil {
|
| 579 | 579 |
c.Fatal(err) |
| 580 | 580 |
} |
| 581 |
- runCmd := exec.Command(dockerBinary, "run", "--security-opt", "seccomp:"+tmpFile.Name(), "jess/unshare", "unshare", "--map-root-user", "--user", "sh", "-c", "whoami") |
|
| 581 |
+ runCmd := exec.Command(dockerBinary, "run", "--security-opt", "apparmor:unconfined", "--security-opt", "seccomp:"+tmpFile.Name(), "debian:jessie", "unshare", "--map-root-user", "--user", "sh", "-c", "whoami") |
|
| 582 | 582 |
out, _, _ := runCommandWithOutput(runCmd) |
| 583 | 583 |
if !strings.Contains(out, "Operation not permitted") {
|
| 584 | 584 |
c.Fatalf("expected unshare userns with seccomp profile denied to fail, got %s", out)
|
| 585 | 585 |
} |
| 586 | 586 |
} |
| 587 |
+ |
|
| 588 |
+// TestRunSeccompProfileDenyCloneUserns checks that 'docker run userns-test' |
|
| 589 |
+// with a the default seccomp profile exits with operation not permitted. |
|
| 590 |
+func (s *DockerSuite) TestRunSeccompProfileDenyCloneUserns(c *check.C) {
|
|
| 591 |
+ testRequires(c, SameHostDaemon, seccompEnabled) |
|
| 592 |
+ |
|
| 593 |
+ runCmd := exec.Command(dockerBinary, "run", "userns-test", "id") |
|
| 594 |
+ out, _, err := runCommandWithOutput(runCmd) |
|
| 595 |
+ if err == nil || !strings.Contains(out, "clone failed: Operation not permitted") {
|
|
| 596 |
+ c.Fatalf("expected clone userns with default seccomp profile denied to fail, got %s: %v", out, err)
|
|
| 597 |
+ } |
|
| 598 |
+} |
|
| 599 |
+ |
|
| 600 |
+// TestRunSeccompAllowPrivCloneUserns checks that 'docker run userns-test' |
|
| 601 |
+// with a the default seccomp profile exits with operation not permitted. |
|
| 602 |
+func (s *DockerSuite) TestRunSeccompAllowPrivCloneUserns(c *check.C) {
|
|
| 603 |
+ testRequires(c, SameHostDaemon, seccompEnabled, NotUserNamespace) |
|
| 604 |
+ |
|
| 605 |
+ // make sure running w privileged is ok |
|
| 606 |
+ runCmd := exec.Command(dockerBinary, "run", "--privileged", "userns-test", "id") |
|
| 607 |
+ if out, _, err := runCommandWithOutput(runCmd); err != nil || !strings.Contains(out, "nobody") {
|
|
| 608 |
+ c.Fatalf("expected clone userns with --privileged to succeed, got %s: %v", out, err)
|
|
| 609 |
+ } |
|
| 610 |
+} |
|
| 611 |
+ |
|
| 612 |
+// TestRunSeccompAllowAptKey checks that 'docker run debian:jessie apt-key' succeeds. |
|
| 613 |
+func (s *DockerSuite) TestRunSeccompAllowAptKey(c *check.C) {
|
|
| 614 |
+ testRequires(c, SameHostDaemon, seccompEnabled) |
|
| 615 |
+ |
|
| 616 |
+ // apt-key uses setrlimit & getrlimit, so we want to make sure we don't break it |
|
| 617 |
+ runCmd := exec.Command(dockerBinary, "run", "debian:jessie", "apt-key", "adv", "--keyserver", "hkp://p80.pool.sks-keyservers.net:80", "--recv-keys", "E871F18B51E0147C77796AC81196BA81F6B0FC61") |
|
| 618 |
+ if out, _, err := runCommandWithOutput(runCmd); err != nil {
|
|
| 619 |
+ c.Fatalf("expected apt-key with seccomp to succeed, got %s: %v", out, err)
|
|
| 620 |
+ } |
|
| 621 |
+} |