You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by gi...@apache.org on 2019/01/28 07:52:41 UTC
[mesos] 10/11: Added Seccomp isolator tests.
This is an automated email from the ASF dual-hosted git repository.
gilbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mesos.git
commit 528cf11c2a39876f5356830d3a4c52e24c1677e3
Author: Andrei Budnik <ab...@mesosphere.com>
AuthorDate: Sun Jan 27 23:52:06 2019 -0800
Added Seccomp isolator tests.
See summary.
Review: https://reviews.apache.org/r/69420/
---
src/Makefile.am | 3 +-
src/tests/CMakeLists.txt | 3 +-
.../containerizer/linux_seccomp_isolator_tests.cpp | 1404 ++++++++++++++++++++
3 files changed, 1408 insertions(+), 2 deletions(-)
diff --git a/src/Makefile.am b/src/Makefile.am
index 78f3e85..b8105c9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -2653,7 +2653,8 @@ endif
if ENABLE_SECCOMP_ISOLATOR
mesos_tests_SOURCES += \
- tests/containerizer/linux_seccomp_parser_tests.cpp
+ tests/containerizer/linux_seccomp_parser_tests.cpp \
+ tests/containerizer/linux_seccomp_isolator_tests.cpp
endif
mesos_tests_CPPFLAGS = $(MESOS_CPPFLAGS)
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index ceaa119..42f8207 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -246,7 +246,8 @@ if (LINUX)
if (ENABLE_SECCOMP_ISOLATOR)
list(APPEND MESOS_TESTS_SRC
- containerizer/linux_seccomp_parser_tests.cpp)
+ containerizer/linux_seccomp_parser_tests.cpp
+ containerizer/linux_seccomp_isolator_tests.cpp)
endif()
if (ENABLE_LINUX_ROUTING)
diff --git a/src/tests/containerizer/linux_seccomp_isolator_tests.cpp b/src/tests/containerizer/linux_seccomp_isolator_tests.cpp
new file mode 100644
index 0000000..1c4c3b1
--- /dev/null
+++ b/src/tests/containerizer/linux_seccomp_isolator_tests.cpp
@@ -0,0 +1,1404 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gtest/gtest.h>
+
+#include <stout/gtest.hpp>
+#include <stout/path.hpp>
+#include <stout/stringify.hpp>
+#include <stout/try.hpp>
+
+#include "linux/capabilities.hpp"
+
+#include "tests/cluster.hpp"
+#include "tests/environment.hpp"
+#include "tests/mesos.hpp"
+
+using mesos::internal::capabilities::Capability;
+using mesos::internal::capabilities::CHOWN;
+using mesos::internal::capabilities::SYS_ADMIN;
+
+using mesos::internal::slave::Containerizer;
+using mesos::internal::slave::Fetcher;
+using mesos::internal::slave::MesosContainerizer;
+
+using mesos::internal::slave::state::SlaveState;
+
+using mesos::slave::ContainerTermination;
+
+using process::Future;
+using process::Owned;
+
+using std::list;
+using std::map;
+using std::ostream;
+using std::set;
+using std::string;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+// This is a modified version of the Docker's default Seccomp profile:
+// https://github.com/moby/moby/blob/master/profiles/seccomp/default.json
+// This profile allows `pivot_root` system call which is used by the
+// Mesos containerizer.
+constexpr char TEST_SECCOMP_PROFILE[] = R"~(
+{
+ "defaultAction": "SCMP_ACT_ERRNO",
+ "archMap": [
+ {
+ "architecture": "SCMP_ARCH_X86_64",
+ "subArchitectures": [
+ "SCMP_ARCH_X86",
+ "SCMP_ARCH_X32"
+ ]
+ },
+ {
+ "architecture": "SCMP_ARCH_AARCH64",
+ "subArchitectures": [
+ "SCMP_ARCH_ARM"
+ ]
+ },
+ {
+ "architecture": "SCMP_ARCH_MIPS64",
+ "subArchitectures": [
+ "SCMP_ARCH_MIPS",
+ "SCMP_ARCH_MIPS64N32"
+ ]
+ },
+ {
+ "architecture": "SCMP_ARCH_MIPS64N32",
+ "subArchitectures": [
+ "SCMP_ARCH_MIPS",
+ "SCMP_ARCH_MIPS64"
+ ]
+ },
+ {
+ "architecture": "SCMP_ARCH_MIPSEL64",
+ "subArchitectures": [
+ "SCMP_ARCH_MIPSEL",
+ "SCMP_ARCH_MIPSEL64N32"
+ ]
+ },
+ {
+ "architecture": "SCMP_ARCH_MIPSEL64N32",
+ "subArchitectures": [
+ "SCMP_ARCH_MIPSEL",
+ "SCMP_ARCH_MIPSEL64"
+ ]
+ },
+ {
+ "architecture": "SCMP_ARCH_S390X",
+ "subArchitectures": [
+ "SCMP_ARCH_S390"
+ ]
+ }
+ ],
+ "syscalls": [
+ {
+ "names": [
+ "accept",
+ "accept4",
+ "access",
+ "adjtimex",
+ "alarm",
+ "bind",
+ "brk",
+ "capget",
+ "capset",
+ "chdir",
+ "chmod",
+ "chown",
+ "chown32",
+ "clock_getres",
+ "clock_gettime",
+ "clock_nanosleep",
+ "close",
+ "connect",
+ "copy_file_range",
+ "creat",
+ "dup",
+ "dup2",
+ "dup3",
+ "epoll_create",
+ "epoll_create1",
+ "epoll_ctl",
+ "epoll_ctl_old",
+ "epoll_pwait",
+ "epoll_wait",
+ "epoll_wait_old",
+ "eventfd",
+ "eventfd2",
+ "execve",
+ "execveat",
+ "exit",
+ "exit_group",
+ "faccessat",
+ "fadvise64",
+ "fadvise64_64",
+ "fallocate",
+ "fanotify_mark",
+ "fchdir",
+ "fchmod",
+ "fchmodat",
+ "fchown",
+ "fchown32",
+ "fchownat",
+ "fcntl",
+ "fcntl64",
+ "fdatasync",
+ "fgetxattr",
+ "flistxattr",
+ "flock",
+ "fork",
+ "fremovexattr",
+ "fsetxattr",
+ "fstat",
+ "fstat64",
+ "fstatat64",
+ "fstatfs",
+ "fstatfs64",
+ "fsync",
+ "ftruncate",
+ "ftruncate64",
+ "futex",
+ "futimesat",
+ "getcpu",
+ "getcwd",
+ "getdents",
+ "getdents64",
+ "getegid",
+ "getegid32",
+ "geteuid",
+ "geteuid32",
+ "getgid",
+ "getgid32",
+ "getgroups",
+ "getgroups32",
+ "getitimer",
+ "getpeername",
+ "getpgid",
+ "getpgrp",
+ "getpid",
+ "getppid",
+ "getpriority",
+ "getrandom",
+ "getresgid",
+ "getresgid32",
+ "getresuid",
+ "getresuid32",
+ "getrlimit",
+ "get_robust_list",
+ "getrusage",
+ "getsid",
+ "getsockname",
+ "getsockopt",
+ "get_thread_area",
+ "gettid",
+ "gettimeofday",
+ "getuid",
+ "getuid32",
+ "getxattr",
+ "inotify_add_watch",
+ "inotify_init",
+ "inotify_init1",
+ "inotify_rm_watch",
+ "io_cancel",
+ "ioctl",
+ "io_destroy",
+ "io_getevents",
+ "ioprio_get",
+ "ioprio_set",
+ "io_setup",
+ "io_submit",
+ "ipc",
+ "kill",
+ "lchown",
+ "lchown32",
+ "lgetxattr",
+ "link",
+ "linkat",
+ "listen",
+ "listxattr",
+ "llistxattr",
+ "_llseek",
+ "lremovexattr",
+ "lseek",
+ "lsetxattr",
+ "lstat",
+ "lstat64",
+ "madvise",
+ "memfd_create",
+ "mincore",
+ "mkdir",
+ "mkdirat",
+ "mknod",
+ "mknodat",
+ "mlock",
+ "mlock2",
+ "mlockall",
+ "mmap",
+ "mmap2",
+ "mprotect",
+ "mq_getsetattr",
+ "mq_notify",
+ "mq_open",
+ "mq_timedreceive",
+ "mq_timedsend",
+ "mq_unlink",
+ "mremap",
+ "msgctl",
+ "msgget",
+ "msgrcv",
+ "msgsnd",
+ "msync",
+ "munlock",
+ "munlockall",
+ "munmap",
+ "nanosleep",
+ "newfstatat",
+ "_newselect",
+ "open",
+ "openat",
+ "pause",
+ "pipe",
+ "pipe2",
+ "poll",
+ "ppoll",
+ "prctl",
+ "pread64",
+ "preadv",
+ "preadv2",
+ "prlimit64",
+ "pselect6",
+ "pwrite64",
+ "pwritev",
+ "pwritev2",
+ "read",
+ "readahead",
+ "readlink",
+ "readlinkat",
+ "readv",
+ "recv",
+ "recvfrom",
+ "recvmmsg",
+ "recvmsg",
+ "remap_file_pages",
+ "removexattr",
+ "rename",
+ "renameat",
+ "renameat2",
+ "restart_syscall",
+ "rmdir",
+ "rt_sigaction",
+ "rt_sigpending",
+ "rt_sigprocmask",
+ "rt_sigqueueinfo",
+ "rt_sigreturn",
+ "rt_sigsuspend",
+ "rt_sigtimedwait",
+ "rt_tgsigqueueinfo",
+ "sched_getaffinity",
+ "sched_getattr",
+ "sched_getparam",
+ "sched_get_priority_max",
+ "sched_get_priority_min",
+ "sched_getscheduler",
+ "sched_rr_get_interval",
+ "sched_setaffinity",
+ "sched_setattr",
+ "sched_setparam",
+ "sched_setscheduler",
+ "sched_yield",
+ "seccomp",
+ "select",
+ "semctl",
+ "semget",
+ "semop",
+ "semtimedop",
+ "send",
+ "sendfile",
+ "sendfile64",
+ "sendmmsg",
+ "sendmsg",
+ "sendto",
+ "setfsgid",
+ "setfsgid32",
+ "setfsuid",
+ "setfsuid32",
+ "setgid",
+ "setgid32",
+ "setgroups",
+ "setgroups32",
+ "setitimer",
+ "setpgid",
+ "setpriority",
+ "setregid",
+ "setregid32",
+ "setresgid",
+ "setresgid32",
+ "setresuid",
+ "setresuid32",
+ "setreuid",
+ "setreuid32",
+ "setrlimit",
+ "set_robust_list",
+ "setsid",
+ "setsockopt",
+ "set_thread_area",
+ "set_tid_address",
+ "setuid",
+ "setuid32",
+ "setxattr",
+ "shmat",
+ "shmctl",
+ "shmdt",
+ "shmget",
+ "shutdown",
+ "sigaltstack",
+ "signalfd",
+ "signalfd4",
+ "sigreturn",
+ "socket",
+ "socketcall",
+ "socketpair",
+ "splice",
+ "stat",
+ "stat64",
+ "statfs",
+ "statfs64",
+ "statx",
+ "symlink",
+ "symlinkat",
+ "sync",
+ "sync_file_range",
+ "syncfs",
+ "sysinfo",
+ "tee",
+ "tgkill",
+ "time",
+ "timer_create",
+ "timer_delete",
+ "timerfd_create",
+ "timerfd_gettime",
+ "timerfd_settime",
+ "timer_getoverrun",
+ "timer_gettime",
+ "timer_settime",
+ "times",
+ "tkill",
+ "truncate",
+ "truncate64",
+ "ugetrlimit",
+ "umask",
+ "uname",
+ "unlink",
+ "unlinkat",
+ "utime",
+ "utimensat",
+ "utimes",
+ "vfork",
+ "vmsplice",
+ "wait4",
+ "waitid",
+ "waitpid",
+ "write",
+ "writev"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "comment": "",
+ "includes": {},
+ "excludes": {}
+ },
+ {
+ "names": [
+ "personality"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [
+ {
+ "index": 0,
+ "value": 0,
+ "valueTwo": 0,
+ "op": "SCMP_CMP_EQ"
+ }
+ ],
+ "comment": "",
+ "includes": {},
+ "excludes": {}
+ },
+ {
+ "names": [
+ "personality"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [
+ {
+ "index": 0,
+ "value": 8,
+ "valueTwo": 0,
+ "op": "SCMP_CMP_EQ"
+ }
+ ],
+ "comment": "",
+ "includes": {},
+ "excludes": {}
+ },
+ {
+ "names": [
+ "personality"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [
+ {
+ "index": 0,
+ "value": 131072,
+ "valueTwo": 0,
+ "op": "SCMP_CMP_EQ"
+ }
+ ],
+ "comment": "",
+ "includes": {},
+ "excludes": {}
+ },
+ {
+ "names": [
+ "personality"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [
+ {
+ "index": 0,
+ "value": 131080,
+ "valueTwo": 0,
+ "op": "SCMP_CMP_EQ"
+ }
+ ],
+ "comment": "",
+ "includes": {},
+ "excludes": {}
+ },
+ {
+ "names": [
+ "personality"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [
+ {
+ "index": 0,
+ "value": 4294967295,
+ "valueTwo": 0,
+ "op": "SCMP_CMP_EQ"
+ }
+ ],
+ "comment": "",
+ "includes": {},
+ "excludes": {}
+ },
+ {
+ "names": [
+ "sync_file_range2"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "comment": "",
+ "includes": {
+ "arches": [
+ "ppc64le"
+ ]
+ },
+ "excludes": {}
+ },
+ {
+ "names": [
+ "arm_fadvise64_64",
+ "arm_sync_file_range",
+ "sync_file_range2",
+ "breakpoint",
+ "cacheflush",
+ "set_tls"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "comment": "",
+ "includes": {
+ "arches": [
+ "arm",
+ "arm64"
+ ]
+ },
+ "excludes": {}
+ },
+ {
+ "names": [
+ "arch_prctl"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "comment": "",
+ "includes": {
+ "arches": [
+ "amd64",
+ "x32"
+ ]
+ },
+ "excludes": {}
+ },
+ {
+ "names": [
+ "modify_ldt"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "comment": "",
+ "includes": {
+ "arches": [
+ "amd64",
+ "x32",
+ "x86"
+ ]
+ },
+ "excludes": {}
+ },
+ {
+ "names": [
+ "s390_pci_mmio_read",
+ "s390_pci_mmio_write",
+ "s390_runtime_instr"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "comment": "",
+ "includes": {
+ "arches": [
+ "s390",
+ "s390x"
+ ]
+ },
+ "excludes": {}
+ },
+ {
+ "names": [
+ "open_by_handle_at"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "comment": "",
+ "includes": {
+ "caps": [
+ "CAP_DAC_READ_SEARCH"
+ ]
+ },
+ "excludes": {}
+ },
+ {
+ "names": [
+ "bpf",
+ "clone",
+ "fanotify_init",
+ "lookup_dcookie",
+ "mount",
+ "name_to_handle_at",
+ "perf_event_open",
+ "quotactl",
+ "setdomainname",
+ "sethostname",
+ "setns",
+ "syslog",
+ "umount",
+ "umount2",
+ "unshare"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "comment": "",
+ "includes": {
+ "caps": [
+ "CAP_SYS_ADMIN"
+ ]
+ },
+ "excludes": {}
+ },
+ {
+ "names": [
+ "clone"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [
+ {
+ "index": 0,
+ "value": 2080505856,
+ "valueTwo": 0,
+ "op": "SCMP_CMP_MASKED_EQ"
+ }
+ ],
+ "comment": "",
+ "includes": {},
+ "excludes": {
+ "caps": [
+ "CAP_SYS_ADMIN"
+ ],
+ "arches": [
+ "s390",
+ "s390x"
+ ]
+ }
+ },
+ {
+ "names": [
+ "clone"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [
+ {
+ "index": 1,
+ "value": 2080505856,
+ "valueTwo": 0,
+ "op": "SCMP_CMP_MASKED_EQ"
+ }
+ ],
+ "comment": "s390 parameter ordering for clone is different",
+ "includes": {
+ "arches": [
+ "s390",
+ "s390x"
+ ]
+ },
+ "excludes": {
+ "caps": [
+ "CAP_SYS_ADMIN"
+ ]
+ }
+ },
+ {
+ "names": [
+ "reboot"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "comment": "",
+ "includes": {
+ "caps": [
+ "CAP_SYS_BOOT"
+ ]
+ },
+ "excludes": {}
+ },
+ {
+ "names": [
+ "chroot",
+ "pivot_root"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "comment": "",
+ "includes": {
+ "caps": [
+ "CAP_SYS_CHROOT"
+ ]
+ },
+ "excludes": {}
+ },
+ {
+ "names": [
+ "delete_module",
+ "init_module",
+ "finit_module",
+ "query_module"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "comment": "",
+ "includes": {
+ "caps": [
+ "CAP_SYS_MODULE"
+ ]
+ },
+ "excludes": {}
+ },
+ {
+ "names": [
+ "acct"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "comment": "",
+ "includes": {
+ "caps": [
+ "CAP_SYS_PACCT"
+ ]
+ },
+ "excludes": {}
+ },
+ {
+ "names": [
+ "kcmp",
+ "process_vm_readv",
+ "process_vm_writev",
+ "ptrace"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "comment": "",
+ "includes": {
+ "caps": [
+ "CAP_SYS_PTRACE"
+ ]
+ },
+ "excludes": {}
+ },
+ {
+ "names": [
+ "iopl",
+ "ioperm"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "comment": "",
+ "includes": {
+ "caps": [
+ "CAP_SYS_RAWIO"
+ ]
+ },
+ "excludes": {}
+ },
+ {
+ "names": [
+ "settimeofday",
+ "stime",
+ "clock_settime"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "comment": "",
+ "includes": {
+ "caps": [
+ "CAP_SYS_TIME"
+ ]
+ },
+ "excludes": {}
+ },
+ {
+ "names": [
+ "vhangup"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "comment": "",
+ "includes": {
+ "caps": [
+ "CAP_SYS_TTY_CONFIG"
+ ]
+ },
+ "excludes": {}
+ },
+ {
+ "names": [
+ "get_mempolicy",
+ "mbind",
+ "set_mempolicy"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "comment": "",
+ "includes": {
+ "caps": [
+ "CAP_SYS_NICE"
+ ]
+ },
+ "excludes": {}
+ },
+ {
+ "names": [
+ "syslog"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "comment": "",
+ "includes": {
+ "caps": [
+ "CAP_SYSLOG"
+ ]
+ },
+ "excludes": {}
+ }
+ ]
+})~";
+
+
+class LinuxSeccompIsolatorTest
+ : public ContainerizerTest<slave::MesosContainerizer>
+{
+protected:
+ slave::Flags CreateSlaveFlags() override
+ {
+ slave::Flags flags = MesosTest::CreateSlaveFlags();
+
+ flags.isolation = "linux/seccomp";
+ flags.seccomp_config_dir = os::getcwd();
+
+ return flags;
+ }
+
+ string createProfile(const string& config) const
+ {
+ CHECK(!config.empty());
+
+ Try<string> profilePath = os::mktemp(path::join(os::getcwd(), "XXXXXX"));
+ EXPECT_SOME(profilePath);
+
+ EXPECT_SOME(os::write(profilePath.get(), config));
+
+ return Path(profilePath.get()).basename();
+ }
+};
+
+
+// This test verifies that the Seccomp isolator fails during initialization
+// when `--seccomp_config_dir` flag is not provided.
+TEST_F(LinuxSeccompIsolatorTest, ROOT_MissingConfigDir)
+{
+ slave::Flags flags = CreateSlaveFlags();
+ flags.seccomp_config_dir = None();
+
+ Fetcher fetcher(flags);
+
+ Try<MesosContainerizer*> create =
+ MesosContainerizer::create(flags, false, &fetcher);
+
+ ASSERT_ERROR(create);
+ EXPECT_TRUE(strings::contains(
+ create.error(), "Missing required `--seccomp_config_dir` flag"));
+}
+
+
+// This test verifies that the Seccomp isolator fails during initialization
+// when default Seccomp profile is invalid.
+TEST_F(LinuxSeccompIsolatorTest, ROOT_InvalidDefaultProfile)
+{
+ slave::Flags flags = CreateSlaveFlags();
+ flags.seccomp_profile_name = createProfile("{}");
+
+ Fetcher fetcher(flags);
+
+ Try<MesosContainerizer*> create =
+ MesosContainerizer::create(flags, false, &fetcher);
+
+ ASSERT_ERROR(create);
+ EXPECT_TRUE(strings::contains(
+ create.error(), "Failed to parse Seccomp profile"));
+}
+
+
+// This test verifies that we can launch shell commands when the default
+// Seccomp profile is enabled.
+TEST_F(LinuxSeccompIsolatorTest, ROOT_SECCOMP_LaunchWithDefaultProfile)
+{
+ slave::Flags flags = CreateSlaveFlags();
+ flags.seccomp_profile_name = createProfile(TEST_SECCOMP_PROFILE);
+
+ Fetcher fetcher(flags);
+
+ Try<MesosContainerizer*> create =
+ MesosContainerizer::create(flags, false, &fetcher);
+
+ ASSERT_SOME(create);
+
+ Owned<MesosContainerizer> containerizer(create.get());
+
+ SlaveState state;
+ state.id = SlaveID();
+
+ AWAIT_READY(containerizer->recover(state));
+
+ ContainerID containerId;
+ containerId.set_value(id::UUID::random().toString());
+
+ Try<string> directory = environment->mkdtemp();
+ ASSERT_SOME(directory);
+
+ const string command = "id && env && uname && hostname";
+
+ Future<Containerizer::LaunchResult> launch = containerizer->launch(
+ containerId,
+ createContainerConfig(
+ None(),
+ createExecutorInfo("executor", command, "cpus:1"),
+ directory.get()),
+ map<string, string>(),
+ None());
+
+ AWAIT_ASSERT_EQ(Containerizer::LaunchResult::SUCCESS, launch);
+
+ Future<Option<ContainerTermination>> wait = containerizer->wait(containerId);
+
+ AWAIT_READY(wait);
+ ASSERT_SOME(wait.get());
+ ASSERT_TRUE(wait.get()->has_status());
+ EXPECT_WEXITSTATUS_EQ(0, wait.get()->status());
+}
+
+
+// This test verifies that OS kernel kills the task process on invocation of
+// syscall that is disabled by Seccomp profile.
+TEST_F(LinuxSeccompIsolatorTest, ROOT_SECCOMP_LaunchWithUnameDisabled)
+{
+ const string config =
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ALLOW",
+ "archMap": [
+ {
+ "architecture": "SCMP_ARCH_X86_64",
+ "subArchitectures": [
+ "SCMP_ARCH_X86",
+ "SCMP_ARCH_X32"
+ ]
+ }
+ ],
+ "syscalls": [
+ {
+ "names": ["uname"],
+ "action": "SCMP_ACT_ERRNO",
+ "args": [],
+ "includes": {},
+ "excludes": {}
+ }
+ ]
+ })~";
+
+ slave::Flags flags = CreateSlaveFlags();
+ flags.seccomp_profile_name = createProfile(config);
+
+ Fetcher fetcher(flags);
+
+ Try<MesosContainerizer*> create =
+ MesosContainerizer::create(flags, false, &fetcher);
+
+ ASSERT_SOME(create);
+
+ Owned<MesosContainerizer> containerizer(create.get());
+
+ SlaveState state;
+ state.id = SlaveID();
+
+ AWAIT_READY(containerizer->recover(state));
+
+ ContainerID containerId;
+ containerId.set_value(id::UUID::random().toString());
+
+ Try<string> directory = environment->mkdtemp();
+ ASSERT_SOME(directory);
+
+ Future<Containerizer::LaunchResult> launch = containerizer->launch(
+ containerId,
+ createContainerConfig(
+ None(),
+ createExecutorInfo("executor", "uname", "cpus:1"),
+ directory.get()),
+ map<string, string>(),
+ None());
+
+ AWAIT_ASSERT_EQ(Containerizer::LaunchResult::SUCCESS, launch);
+
+ Future<Option<ContainerTermination>> wait = containerizer->wait(containerId);
+
+ AWAIT_READY(wait);
+ ASSERT_SOME(wait.get());
+ ASSERT_TRUE(wait.get()->has_status());
+ EXPECT_WEXITSTATUS_NE(0, wait.get()->status());
+}
+
+
+// This test verifies that we can launch a task container with overridden
+// Seccomp profile.
+TEST_F(LinuxSeccompIsolatorTest, ROOT_SECCOMP_LaunchWithOverriddenProfile)
+{
+ const string config =
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ALLOW",
+ "archMap": [
+ {
+ "architecture": "SCMP_ARCH_X86_64",
+ "subArchitectures": [
+ "SCMP_ARCH_X86",
+ "SCMP_ARCH_X32"
+ ]
+ }
+ ],
+ "syscalls": [
+ {
+ "names": ["uname"],
+ "action": "SCMP_ACT_ERRNO",
+ "args": [],
+ "includes": {},
+ "excludes": {}
+ }
+ ]
+ })~";
+
+ slave::Flags flags = CreateSlaveFlags();
+ flags.seccomp_profile_name = createProfile(TEST_SECCOMP_PROFILE);
+
+ Fetcher fetcher(flags);
+
+ Try<MesosContainerizer*> create =
+ MesosContainerizer::create(flags, false, &fetcher);
+
+ ASSERT_SOME(create);
+
+ Owned<MesosContainerizer> containerizer(create.get());
+
+ SlaveState state;
+ state.id = SlaveID();
+
+ AWAIT_READY(containerizer->recover(state));
+
+ ContainerID containerId;
+ containerId.set_value(id::UUID::random().toString());
+
+ Try<string> directory = environment->mkdtemp();
+ ASSERT_SOME(directory);
+
+ auto containerConfig = createContainerConfig(
+ None(),
+ createExecutorInfo("executor", "uname", "cpus:1"),
+ directory.get());
+
+ ContainerInfo* container = containerConfig.mutable_container_info();
+ container->set_type(ContainerInfo::MESOS);
+
+ // Set the Seccomp profile name for this particular task.
+ SeccompInfo* seccomp = container->mutable_linux_info()->mutable_seccomp();
+ seccomp->set_profile_name(createProfile(config));
+
+ Future<Containerizer::LaunchResult> launch = containerizer->launch(
+ containerId,
+ containerConfig,
+ map<string, string>(),
+ None());
+
+ AWAIT_ASSERT_EQ(Containerizer::LaunchResult::SUCCESS, launch);
+
+ Future<Option<ContainerTermination>> wait = containerizer->wait(containerId);
+
+ AWAIT_READY(wait);
+ ASSERT_SOME(wait.get());
+ ASSERT_TRUE(wait.get()->has_status());
+ EXPECT_WEXITSTATUS_NE(0, wait.get()->status());
+}
+
+
+// This test verifies that launching a task with a non-existent Seccomp profile
+// leads to failure.
+TEST_F(
+ LinuxSeccompIsolatorTest,
+ ROOT_SECCOMP_LaunchWithOverriddenNonExistentProfile)
+{
+ slave::Flags flags = CreateSlaveFlags();
+ flags.seccomp_profile_name = createProfile(TEST_SECCOMP_PROFILE);
+
+ Fetcher fetcher(flags);
+
+ Try<MesosContainerizer*> create =
+ MesosContainerizer::create(flags, false, &fetcher);
+
+ ASSERT_SOME(create);
+
+ Owned<MesosContainerizer> containerizer(create.get());
+
+ SlaveState state;
+ state.id = SlaveID();
+
+ AWAIT_READY(containerizer->recover(state));
+
+ ContainerID containerId;
+ containerId.set_value(id::UUID::random().toString());
+
+ Try<string> directory = environment->mkdtemp();
+ ASSERT_SOME(directory);
+
+ auto containerConfig = createContainerConfig(
+ None(),
+ createExecutorInfo("executor", "exit 0", "cpus:1"),
+ directory.get());
+
+ ContainerInfo* container = containerConfig.mutable_container_info();
+ container->set_type(ContainerInfo::MESOS);
+
+ // Set a non-existent Seccomp profile for this particular task.
+ SeccompInfo* seccomp = container->mutable_linux_info()->mutable_seccomp();
+ seccomp->set_profile_name("absent");
+
+ Future<Containerizer::LaunchResult> launch = containerizer->launch(
+ containerId,
+ containerConfig,
+ map<string, string>(),
+ None());
+
+ AWAIT_FAILED(launch);
+}
+
+
+// Param for the tests:
+// 'includes_capabilities'
+// List of capabilities that are set in the `includes` section within
+// Seccomp profile for a syscall filtering rule.
+// 'excludes_capabilities'
+// List of capabilities that are set in the `excludes` section within
+// Seccomp profile for a syscall filtering rule.
+// 'container_capabilities'
+// Container specified effective and bounding capabilities for the
+// container.
+// 'result'
+// True if the task should finish normally.
+struct SeccompTestParam
+{
+ enum Result
+ {
+ FAILURE = 0,
+ SUCCESS = 1
+ };
+
+ SeccompTestParam(
+ const Option<set<Capability>>& _includes_capabilities,
+ const Option<set<Capability>>& _excludes_capabilities,
+ const Option<set<Capability>>& _container_capabilities,
+ Result _result)
+ : includes_capabilities(convertToList(_includes_capabilities)),
+ excludes_capabilities(convertToList(_excludes_capabilities)),
+ container_capabilities(convertToInfo(_container_capabilities)),
+ result(_result) {}
+
+ static const Option<CapabilityInfo> convertToInfo(
+ const Option<set<Capability>>& caps)
+ {
+ return caps.isSome()
+ ? capabilities::convert(caps.get())
+ : Option<CapabilityInfo>::none();
+ }
+
+ static const Option<list<string>> convertToList(
+ const Option<set<Capability>>& caps)
+ {
+ if (caps.isSome()) {
+ list<string> capabilities;
+
+ foreach (const Capability& capability, caps.get()) {
+ capabilities.emplace_back(
+ strings::format("\"CAP_%s\"", stringify(capability)).get());
+ }
+
+ return capabilities;
+ }
+
+ return None();
+ }
+
+ const Option<list<string>> includes_capabilities;
+ const Option<list<string>> excludes_capabilities;
+
+ const Option<CapabilityInfo> container_capabilities;
+
+ const Result result;
+};
+
+
+ostream& operator<<(ostream& stream, const SeccompTestParam& param)
+{
+ if (param.includes_capabilities.isSome()) {
+ stream << "includes_capabilities='"
+ << stringify(param.includes_capabilities.get()) << "', ";
+ } else {
+ stream << "includes_capabilities='none', ";
+ }
+
+ if (param.excludes_capabilities.isSome()) {
+ stream << "excludes_capabilities='"
+ << stringify(param.excludes_capabilities.get()) << "', ";
+ } else {
+ stream << "excludes_capabilities='none', ";
+ }
+
+ if (param.container_capabilities.isSome()) {
+ stream << "container_capabilities='"
+ << JSON::protobuf(param.container_capabilities.get()) << "', ";
+ } else {
+ stream << "container_capabilities='none', ";
+ }
+
+ switch (param.result) {
+ case SeccompTestParam::FAILURE:
+ stream << "result=failure'";
+ break;
+ case SeccompTestParam::SUCCESS:
+ stream << "result=success'";
+ }
+
+ return stream;
+}
+
+
+class LinuxSeccompIsolatorWithCapabilitiesTest
+ : public LinuxSeccompIsolatorTest,
+ public ::testing::WithParamInterface<SeccompTestParam>
+{
+public:
+ LinuxSeccompIsolatorWithCapabilitiesTest()
+ : param(GetParam()) {}
+
+protected:
+ SeccompTestParam param;
+};
+
+
+// Parameterized test confirming the filtering of Seccomp rules by capabilities.
+// This test launches a container with enabled filtering of a Seccomp rule which
+// disables `uname` system call depending on container's capabilities. E.g., if
+// `includes` section contains `CAP_SYS_ADMIN` and the container is launched
+// with `CAP_SYS_ADMIN`, then the rule is applied, so the `uname` command fails
+// as expected.
+TEST_P(LinuxSeccompIsolatorWithCapabilitiesTest, ROOT_SECCOMP_LaunchWithFilter)
+{
+ string includesCaps = "{}";
+ string excludesCaps = "{}";
+
+ if (param.includes_capabilities.isSome()) {
+ includesCaps = strings::format(
+ "{\"caps\": %s}",
+ stringify(param.includes_capabilities.get())).get();
+ }
+
+ if (param.excludes_capabilities.isSome()) {
+ excludesCaps = strings::format(
+ "{\"caps\": %s}",
+ stringify(param.excludes_capabilities.get())).get();
+ }
+
+ const string config = strings::format(
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ALLOW",
+ "archMap": [
+ {
+ "architecture": "SCMP_ARCH_X86_64",
+ "subArchitectures": [
+ "SCMP_ARCH_X86",
+ "SCMP_ARCH_X32"
+ ]
+ }
+ ],
+ "syscalls": [
+ {
+ "names": ["uname"],
+ "action": "SCMP_ACT_ERRNO",
+ "args": [],
+ "includes": %s,
+ "excludes": %s
+ }
+ ]
+ })~",
+ includesCaps,
+ excludesCaps).get();
+
+ slave::Flags flags = CreateSlaveFlags();
+ flags.isolation = "linux/capabilities,linux/seccomp";
+ flags.effective_capabilities = param.container_capabilities;
+ flags.bounding_capabilities = param.container_capabilities;
+ flags.seccomp_profile_name = createProfile(config);
+
+ Fetcher fetcher(flags);
+
+ Try<MesosContainerizer*> create =
+ MesosContainerizer::create(flags, false, &fetcher);
+
+ ASSERT_SOME(create);
+
+ Owned<MesosContainerizer> containerizer(create.get());
+
+ SlaveState state;
+ state.id = SlaveID();
+
+ AWAIT_READY(containerizer->recover(state));
+
+ ContainerID containerId;
+ containerId.set_value(id::UUID::random().toString());
+
+ Try<string> directory = environment->mkdtemp();
+ ASSERT_SOME(directory);
+
+ Future<Containerizer::LaunchResult> launch = containerizer->launch(
+ containerId,
+ createContainerConfig(
+ None(),
+ createExecutorInfo("executor", "uname", "cpus:1"),
+ directory.get()),
+ map<string, string>(),
+ None());
+
+ AWAIT_ASSERT_EQ(Containerizer::LaunchResult::SUCCESS, launch);
+
+ Future<Option<ContainerTermination>> wait = containerizer->wait(containerId);
+
+ AWAIT_READY(wait);
+ ASSERT_SOME(wait.get());
+ ASSERT_TRUE(wait.get()->has_status());
+
+ switch (param.result) {
+ case SeccompTestParam::SUCCESS:
+ EXPECT_WEXITSTATUS_EQ(0, wait.get()->status());
+ break;
+ case SeccompTestParam::FAILURE:
+ EXPECT_WEXITSTATUS_NE(0, wait.get()->status());
+ break;
+ }
+}
+
+
+INSTANTIATE_TEST_CASE_P(
+ SeccompTestParam,
+ LinuxSeccompIsolatorWithCapabilitiesTest,
+ ::testing::Values(
+ // Seccomp filter capabilities match container capabilities.
+ SeccompTestParam(
+ set<Capability>({SYS_ADMIN}),
+ None(),
+ set<Capability>({SYS_ADMIN}),
+ SeccompTestParam::FAILURE),
+ SeccompTestParam(
+ None(),
+ set<Capability>({SYS_ADMIN}),
+ set<Capability>({SYS_ADMIN}),
+ SeccompTestParam::SUCCESS),
+
+ // Seccomp filter capabilities does not match container capabilities.
+ SeccompTestParam(
+ set<Capability>({SYS_ADMIN}),
+ None(),
+ set<Capability>({CHOWN}),
+ SeccompTestParam::SUCCESS),
+ SeccompTestParam(
+ None(),
+ set<Capability>({SYS_ADMIN}),
+ set<Capability>({CHOWN}),
+ SeccompTestParam::FAILURE)));
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {