add more seccomp profile tests
Signed-off-by: Jessica Frazelle <acidburn@docker.com>
This commit is contained in:
parent
626c933730
commit
327421d1df
9 changed files with 156 additions and 41 deletions
15
contrib/syscall-test/Dockerfile
Normal file
15
contrib/syscall-test/Dockerfile
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
FROM debian:jessie
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
gcc \
|
||||||
|
libc6-dev \
|
||||||
|
--no-install-recommends \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY . /usr/src/
|
||||||
|
|
||||||
|
WORKDIR /usr/src/
|
||||||
|
|
||||||
|
RUN gcc -g -Wall -static userns.c -o /usr/bin/userns-test \
|
||||||
|
&& gcc -g -Wall -static ns.c -o /usr/bin/ns-test \
|
||||||
|
&& gcc -g -Wall -static acct.c -o /usr/bin/acct-test
|
16
contrib/syscall-test/acct.c
Normal file
16
contrib/syscall-test/acct.c
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int err = acct("/tmp/t");
|
||||||
|
if (err == -1) {
|
||||||
|
fprintf(stderr, "acct failed: %s\n", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
63
contrib/syscall-test/ns.c
Normal file
63
contrib/syscall-test/ns.c
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define STACK_SIZE (1024 * 1024) /* Stack size for cloned child */
|
||||||
|
|
||||||
|
struct clone_args {
|
||||||
|
char **argv;
|
||||||
|
};
|
||||||
|
|
||||||
|
// child_exec is the func that will be executed as the result of clone
|
||||||
|
static int child_exec(void *stuff)
|
||||||
|
{
|
||||||
|
struct clone_args *args = (struct clone_args *)stuff;
|
||||||
|
if (execvp(args->argv[0], args->argv) != 0) {
|
||||||
|
fprintf(stderr, "failed to execvp argments %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
// we should never reach here!
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct clone_args args;
|
||||||
|
args.argv = &argv[1];
|
||||||
|
|
||||||
|
int clone_flags = CLONE_NEWNS | CLONE_NEWPID | SIGCHLD;
|
||||||
|
|
||||||
|
// allocate stack for child
|
||||||
|
char *stack; /* Start of stack buffer */
|
||||||
|
char *child_stack; /* End of stack buffer */
|
||||||
|
stack =
|
||||||
|
mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE,
|
||||||
|
MAP_SHARED | MAP_ANON | MAP_STACK, -1, 0);
|
||||||
|
if (stack == MAP_FAILED) {
|
||||||
|
fprintf(stderr, "mmap failed: %s\n", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
child_stack = stack + STACK_SIZE; /* Assume stack grows downward */
|
||||||
|
|
||||||
|
// the result of this call is that our child_exec will be run in another
|
||||||
|
// process returning it's pid
|
||||||
|
pid_t pid = clone(child_exec, child_stack, clone_flags, &args);
|
||||||
|
if (pid < 0) {
|
||||||
|
fprintf(stderr, "clone failed: %s\n", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
// lets wait on our child process here before we, the parent, exits
|
||||||
|
if (waitpid(pid, NULL, 0) == -1) {
|
||||||
|
fprintf(stderr, "failed to wait pid %d\n", pid);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
|
@ -1,17 +1,15 @@
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
#include <sched.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#define STACKSIZE (1024*1024)
|
#define STACK_SIZE (1024 * 1024) /* Stack size for cloned child */
|
||||||
static char child_stack[STACKSIZE];
|
|
||||||
|
|
||||||
struct clone_args {
|
struct clone_args {
|
||||||
char **argv;
|
char **argv;
|
||||||
|
@ -37,10 +35,21 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
int clone_flags = CLONE_NEWUSER | SIGCHLD;
|
int clone_flags = CLONE_NEWUSER | SIGCHLD;
|
||||||
|
|
||||||
|
// allocate stack for child
|
||||||
|
char *stack; /* Start of stack buffer */
|
||||||
|
char *child_stack; /* End of stack buffer */
|
||||||
|
stack =
|
||||||
|
mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE,
|
||||||
|
MAP_SHARED | MAP_ANON | MAP_STACK, -1, 0);
|
||||||
|
if (stack == MAP_FAILED) {
|
||||||
|
fprintf(stderr, "mmap failed: %s\n", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
child_stack = stack + STACK_SIZE; /* Assume stack grows downward */
|
||||||
|
|
||||||
// the result of this call is that our child_exec will be run in another
|
// the result of this call is that our child_exec will be run in another
|
||||||
// process returning it's pid
|
// process returning it's pid
|
||||||
pid_t pid =
|
pid_t pid = clone(child_exec, child_stack, clone_flags, &args);
|
||||||
clone(child_exec, child_stack + STACKSIZE, clone_flags, &args);
|
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
fprintf(stderr, "clone failed: %s\n", strerror(errno));
|
fprintf(stderr, "clone failed: %s\n", strerror(errno));
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
|
@ -1,3 +0,0 @@
|
||||||
FROM debian:jessie
|
|
||||||
COPY userns-test .
|
|
||||||
ENTRYPOINT ["./userns-test"]
|
|
8
hack/make/.ensure-syscall-test
Normal file
8
hack/make/.ensure-syscall-test
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Build a C binary for cloning a userns for seccomp tests
|
||||||
|
# and compile it for target daemon
|
||||||
|
if [ "$DOCKER_ENGINE_GOOS" = "linux" ]; then
|
||||||
|
docker build -qt syscall-test contrib/syscall-test > /dev/null
|
||||||
|
fi
|
|
@ -1,17 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Build a C binary for cloning a userns for seccomp tests
|
|
||||||
# and compile it for target daemon
|
|
||||||
|
|
||||||
dir="$DEST/userns-test"
|
|
||||||
mkdir -p "$dir"
|
|
||||||
(
|
|
||||||
if [ "$(go env GOOS)" = "linux" ]; then
|
|
||||||
cd "$dir"
|
|
||||||
gcc -g -Wall -static ../../../../contrib/userns-test/main.c -o ./userns-test
|
|
||||||
cp ../../../../contrib/userns-test/Dockerfile .
|
|
||||||
docker build -qt userns-test . > /dev/null
|
|
||||||
fi
|
|
||||||
)
|
|
||||||
rm -rf "$dir"
|
|
|
@ -10,4 +10,4 @@ export DOCKER_ENGINE_GOARCH=$(echo $DOCKER_ENGINE_OSARCH | cut -d'/' -f2)
|
||||||
bundle .ensure-emptyfs
|
bundle .ensure-emptyfs
|
||||||
bundle .ensure-frozen-images
|
bundle .ensure-frozen-images
|
||||||
bundle .ensure-httpserver
|
bundle .ensure-httpserver
|
||||||
bundle .ensure-userns-test
|
bundle .ensure-syscall-test
|
||||||
|
|
|
@ -586,12 +586,12 @@ func (s *DockerSuite) TestRunSeccompProfileDenyUnshareUserns(c *check.C) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestRunSeccompProfileDenyCloneUserns checks that 'docker run userns-test'
|
// TestRunSeccompProfileDenyCloneUserns checks that 'docker run syscall-test'
|
||||||
// with a the default seccomp profile exits with operation not permitted.
|
// with a the default seccomp profile exits with operation not permitted.
|
||||||
func (s *DockerSuite) TestRunSeccompProfileDenyCloneUserns(c *check.C) {
|
func (s *DockerSuite) TestRunSeccompProfileDenyCloneUserns(c *check.C) {
|
||||||
testRequires(c, SameHostDaemon, seccompEnabled)
|
testRequires(c, SameHostDaemon, seccompEnabled)
|
||||||
|
|
||||||
runCmd := exec.Command(dockerBinary, "run", "userns-test", "id")
|
runCmd := exec.Command(dockerBinary, "run", "syscall-test", "userns-test", "id")
|
||||||
out, _, err := runCommandWithOutput(runCmd)
|
out, _, err := runCommandWithOutput(runCmd)
|
||||||
if err == nil || !strings.Contains(out, "clone failed: Operation not permitted") {
|
if err == nil || !strings.Contains(out, "clone failed: Operation not permitted") {
|
||||||
c.Fatalf("expected clone userns with default seccomp profile denied to fail, got %s: %v", out, err)
|
c.Fatalf("expected clone userns with default seccomp profile denied to fail, got %s: %v", out, err)
|
||||||
|
@ -599,24 +599,24 @@ func (s *DockerSuite) TestRunSeccompProfileDenyCloneUserns(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestRunSeccompUnconfinedCloneUserns checks that
|
// TestRunSeccompUnconfinedCloneUserns checks that
|
||||||
// 'docker run --security-opt seccomp:unconfined userns-test' allows creating a userns.
|
// 'docker run --security-opt seccomp:unconfined syscall-test' allows creating a userns.
|
||||||
func (s *DockerSuite) TestRunSeccompUnconfinedCloneUserns(c *check.C) {
|
func (s *DockerSuite) TestRunSeccompUnconfinedCloneUserns(c *check.C) {
|
||||||
testRequires(c, SameHostDaemon, seccompEnabled, NotUserNamespace)
|
testRequires(c, SameHostDaemon, seccompEnabled, NotUserNamespace)
|
||||||
|
|
||||||
// make sure running w privileged is ok
|
// make sure running w privileged is ok
|
||||||
runCmd := exec.Command(dockerBinary, "run", "--security-opt", "seccomp:unconfined", "userns-test", "id")
|
runCmd := exec.Command(dockerBinary, "run", "--security-opt", "seccomp:unconfined", "syscall-test", "userns-test", "id")
|
||||||
if out, _, err := runCommandWithOutput(runCmd); err != nil || !strings.Contains(out, "nobody") {
|
if out, _, err := runCommandWithOutput(runCmd); err != nil || !strings.Contains(out, "nobody") {
|
||||||
c.Fatalf("expected clone userns with --security-opt seccomp:unconfined to succeed, got %s: %v", out, err)
|
c.Fatalf("expected clone userns with --security-opt seccomp:unconfined to succeed, got %s: %v", out, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestRunSeccompAllowPrivCloneUserns checks that 'docker run --privileged userns-test'
|
// TestRunSeccompAllowPrivCloneUserns checks that 'docker run --privileged syscall-test'
|
||||||
// allows creating a userns.
|
// allows creating a userns.
|
||||||
func (s *DockerSuite) TestRunSeccompAllowPrivCloneUserns(c *check.C) {
|
func (s *DockerSuite) TestRunSeccompAllowPrivCloneUserns(c *check.C) {
|
||||||
testRequires(c, SameHostDaemon, seccompEnabled, NotUserNamespace)
|
testRequires(c, SameHostDaemon, seccompEnabled, NotUserNamespace)
|
||||||
|
|
||||||
// make sure running w privileged is ok
|
// make sure running w privileged is ok
|
||||||
runCmd := exec.Command(dockerBinary, "run", "--privileged", "userns-test", "id")
|
runCmd := exec.Command(dockerBinary, "run", "--privileged", "syscall-test", "userns-test", "id")
|
||||||
if out, _, err := runCommandWithOutput(runCmd); err != nil || !strings.Contains(out, "nobody") {
|
if out, _, err := runCommandWithOutput(runCmd); err != nil || !strings.Contains(out, "nobody") {
|
||||||
c.Fatalf("expected clone userns with --privileged to succeed, got %s: %v", out, err)
|
c.Fatalf("expected clone userns with --privileged to succeed, got %s: %v", out, err)
|
||||||
}
|
}
|
||||||
|
@ -624,7 +624,7 @@ func (s *DockerSuite) TestRunSeccompAllowPrivCloneUserns(c *check.C) {
|
||||||
|
|
||||||
// TestRunSeccompAllowAptKey checks that 'docker run debian:jessie apt-key' succeeds.
|
// TestRunSeccompAllowAptKey checks that 'docker run debian:jessie apt-key' succeeds.
|
||||||
func (s *DockerSuite) TestRunSeccompAllowAptKey(c *check.C) {
|
func (s *DockerSuite) TestRunSeccompAllowAptKey(c *check.C) {
|
||||||
testRequires(c, SameHostDaemon, seccompEnabled)
|
testRequires(c, SameHostDaemon, seccompEnabled, Network)
|
||||||
|
|
||||||
// apt-key uses setrlimit & getrlimit, so we want to make sure we don't break it
|
// apt-key uses setrlimit & getrlimit, so we want to make sure we don't break it
|
||||||
runCmd := exec.Command(dockerBinary, "run", "debian:jessie", "apt-key", "adv", "--keyserver", "hkp://p80.pool.sks-keyservers.net:80", "--recv-keys", "E871F18B51E0147C77796AC81196BA81F6B0FC61")
|
runCmd := exec.Command(dockerBinary, "run", "debian:jessie", "apt-key", "adv", "--keyserver", "hkp://p80.pool.sks-keyservers.net:80", "--recv-keys", "E871F18B51E0147C77796AC81196BA81F6B0FC61")
|
||||||
|
@ -632,3 +632,27 @@ func (s *DockerSuite) TestRunSeccompAllowAptKey(c *check.C) {
|
||||||
c.Fatalf("expected apt-key with seccomp to succeed, got %s: %v", out, err)
|
c.Fatalf("expected apt-key with seccomp to succeed, got %s: %v", out, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DockerSuite) TestRunSeccompDefaultProfile(c *check.C) {
|
||||||
|
testRequires(c, SameHostDaemon, seccompEnabled, NotUserNamespace)
|
||||||
|
|
||||||
|
out, _, err := dockerCmdWithError("run", "--cap-add", "ALL", "syscall-test", "acct-test")
|
||||||
|
if err == nil || !strings.Contains(out, "Operation not permitted") {
|
||||||
|
c.Fatalf("expected Operation not permitted, got: %s", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, _, err = dockerCmdWithError("run", "--cap-add", "ALL", "syscall-test", "ns-test", "echo", "hello")
|
||||||
|
if err == nil || !strings.Contains(out, "Operation not permitted") {
|
||||||
|
c.Fatalf("expected Operation not permitted, got: %s", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, _, err = dockerCmdWithError("run", "--cap-add", "ALL", "--security-opt", "seccomp:unconfined", "syscall-test", "acct-test")
|
||||||
|
if err == nil || !strings.Contains(out, "No such file or directory") {
|
||||||
|
c.Fatalf("expected No such file or directory, got: %s", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, _, err = dockerCmdWithError("run", "--cap-add", "ALL", "--security-opt", "seccomp:unconfined", "syscall-test", "ns-test", "echo", "hello")
|
||||||
|
if err != nil || !strings.Contains(out, "hello") {
|
||||||
|
c.Fatalf("expected hello, got: %s, %v", out, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue