From e41b0f42a88cbc5733f994cd3de408f33d9ada89 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 13 Sep 2017 19:27:51 +0200 Subject: [PATCH 01/12] seccomp: update "@default" seccomp group a bit Let's add more of the most basic operations to "@default" as absolute baseline needed by glibc and such to operate. Specifically: futex, get_robust_list, get_thread_area, membarrier, set_robust_list, set_thread_area, set_tid_address are all required to properly implement mutexes and other thread synchronization logic. Given that a ton of datastructures are protected by mutexes (such as stdio and such), let's just whitelist this by default, so that things can just work. restart_syscall is used to implement EAGAIN SA_RESTART stuff in some archs, and synthesized by the kernel without any explicit user logic, hence let's make this work out of the box. --- src/shared/seccomp-util.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index 643dde6c4a..88e21f67d8 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -278,11 +278,19 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "execve\0" "exit\0" "exit_group\0" + "futex\0" + "get_robust_list\0" + "get_thread_area\0" "getrlimit\0" /* make sure processes can query stack size and such */ "gettimeofday\0" + "membarrier\0" "nanosleep\0" "pause\0" + "restart_syscall\0" "rt_sigreturn\0" + "set_robust_list\0" + "set_thread_area\0" + "set_tid_address\0" "sigreturn\0" "time\0" }, From 648a0ed0d7df52a485bd20085f9427714e5e176c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 13 Sep 2017 19:31:43 +0200 Subject: [PATCH 02/12] seccomp: let's update base-io a bit Let's add _llseek which is the syscall name on some archs that on others is simply lseek (due to 64bit vs 32bit off_t confusion). Also, let's sort things alphabetically. --- src/shared/seccomp-util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index 88e21f67d8..b4fbb03932 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -298,10 +298,11 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { .name = "@basic-io", .help = "Basic IO", .value = + "_llseek\0" "close\0" + "dup\0" "dup2\0" "dup3\0" - "dup\0" "lseek\0" "pread64\0" "preadv\0" From ceaa6aa76beb4b97c435710b65ba02a951db5347 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 13 Sep 2017 19:33:54 +0200 Subject: [PATCH 03/12] seccomp: let's update @file-system a bit Let's add fremovexattr which was the only xattr syscall so far missing from the group, even though lremovexattr and friends where included. Add inotify_init, which is an older (but still supported) version of inotify_init1. Add oldfstat, oldlstat, oldstat which are old versions of the stat syscalls on some archs. Add utime, which is an older more limited version of utimes and utimensat. Enclose the "statx" entry in some ifdeffery to ensure libseccomp actually knows the syscall. If libseccomp doesn't know it, then we'd get EINVAL rather than EDOM (which is what is returned if a syscall is known but not available on the local system) when resolving the syscall name and we really don't want that, as we use the EDOM vs. EINVAL check for determining whether a syscall makes sense at all. Also, order things alphabetically. --- src/shared/seccomp-util.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index b4fbb03932..44dc027c96 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -362,24 +362,26 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "fchdir\0" "fchmod\0" "fchmodat\0" - "fcntl64\0" "fcntl\0" + "fcntl64\0" "fgetxattr\0" "flistxattr\0" + "fremovexattr\0" "fsetxattr\0" - "fstat64\0" "fstat\0" + "fstat64\0" "fstatat64\0" - "fstatfs64\0" "fstatfs\0" - "ftruncate64\0" + "fstatfs64\0" "ftruncate\0" + "ftruncate64\0" "futimesat\0" "getcwd\0" - "getdents64\0" "getdents\0" + "getdents64\0" "getxattr\0" "inotify_add_watch\0" + "inotify_init\0" "inotify_init1\0" "inotify_rm_watch\0" "lgetxattr\0" @@ -389,36 +391,43 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "llistxattr\0" "lremovexattr\0" "lsetxattr\0" - "lstat64\0" "lstat\0" + "lstat64\0" "mkdir\0" "mkdirat\0" "mknod\0" "mknodat\0" - "mmap2\0" "mmap\0" + "mmap2\0" "munmap\0" "newfstatat\0" + "oldfstat\0" + "oldlstat\0" + "oldstat\0" "open\0" "openat\0" "readlink\0" "readlinkat\0" "removexattr\0" "rename\0" - "renameat2\0" "renameat\0" + "renameat2\0" "rmdir\0" "setxattr\0" - "stat64\0" "stat\0" + "stat64\0" "statfs\0" + "statfs64\0" +#ifdef __PNR_statx "statx\0" +#endif "symlink\0" "symlinkat\0" - "truncate64\0" "truncate\0" + "truncate64\0" "unlink\0" "unlinkat\0" + "utime\0" "utimensat\0" "utimes\0" }, From 215728ff3999bae2010844d6b25105ef97f3e391 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 13 Sep 2017 19:39:02 +0200 Subject: [PATCH 04/12] seccomp: order the syscalls in more groups alphabetically No changes besides reordering. --- src/shared/seccomp-util.c | 42 +++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index 44dc027c96..fef6160731 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -436,15 +436,15 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { .help = "Event loop system calls", .value = "_newselect\0" - "epoll_create1\0" "epoll_create\0" + "epoll_create1\0" "epoll_ctl\0" "epoll_ctl_old\0" "epoll_pwait\0" "epoll_wait\0" "epoll_wait_old\0" - "eventfd2\0" "eventfd\0" + "eventfd2\0" "poll\0" "ppoll\0" "pselect6\0" @@ -466,8 +466,8 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "msgget\0" "msgrcv\0" "msgsnd\0" - "pipe2\0" "pipe\0" + "pipe2\0" "process_vm_readv\0" "process_vm_writev\0" "semctl\0" @@ -502,15 +502,15 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "chroot\0" "mount\0" "pivot_root\0" - "umount2\0" "umount\0" + "umount2\0" }, [SYSCALL_FILTER_SET_NETWORK_IO] = { .name = "@network-io", .help = "Network or Unix socket IO, should not be needed if not network facing", .value = - "accept4\0" "accept\0" + "accept4\0" "bind\0" "connect\0" "getpeername\0" @@ -569,38 +569,38 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "@clock\0" "@module\0" "@raw-io\0" + "_sysctl\0" "acct\0" "bpf\0" "capset\0" - "chown32\0" "chown\0" + "chown32\0" "chroot\0" - "fchown32\0" "fchown\0" + "fchown32\0" "fchownat\0" "kexec_file_load\0" "kexec_load\0" - "lchown32\0" "lchown\0" + "lchown32\0" "nfsservctl\0" "pivot_root\0" "quotactl\0" "reboot\0" "setdomainname\0" - "setfsuid32\0" "setfsuid\0" - "setgroups32\0" + "setfsuid32\0" "setgroups\0" + "setgroups32\0" "sethostname\0" - "setresuid32\0" "setresuid\0" - "setreuid32\0" + "setresuid32\0" "setreuid\0" - "setuid32\0" + "setreuid32\0" "setuid\0" + "setuid32\0" "swapoff\0" "swapon\0" - "_sysctl\0" "vhangup\0" }, [SYSCALL_FILTER_SET_PROCESS] = { @@ -663,20 +663,20 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { .name = "@setuid", .help = "Operations for changing user/group credentials", .value = - "setgid32\0" "setgid\0" - "setgroups32\0" + "setgid32\0" "setgroups\0" - "setregid32\0" + "setgroups32\0" "setregid\0" - "setresgid32\0" + "setregid32\0" "setresgid\0" - "setresuid32\0" + "setresgid32\0" "setresuid\0" - "setreuid32\0" + "setresuid32\0" "setreuid\0" - "setuid32\0" + "setreuid32\0" "setuid\0" + "setuid32\0" }, [SYSCALL_FILTER_SET_SWAP] = { .name = "@swap", From 7e0c3b8fda78157550d89cb55668604f43a4a2f2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 13 Sep 2017 19:39:54 +0200 Subject: [PATCH 05/12] seccomp: "idle" is another obsolete syscall --- src/shared/seccomp-util.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index fef6160731..eee7f04571 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -545,6 +545,7 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "get_kernel_syms\0" "getpmsg\0" "gtty\0" + "idle\0" "lock\0" "mpx\0" "prof\0" From b887d2ebfe19b09d498f71589d245ad2060f1afd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 13 Sep 2017 19:40:23 +0200 Subject: [PATCH 06/12] seccomp: beef up @process group a bit Include the waid syscalls. If we permit forking then we should also permit waiting for a process. Similar to that: also permit determining the usage counters for processes. Include calls to determine process/thread identity. They have little impact security-wise, but are very likely used when process management of any form is done. Also, add rt_sigqueueinfo + rt_tgsigqueueinfo as they are similar to kill() and friends, but permit passing along a userdata ptr. --- src/shared/seccomp-util.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index eee7f04571..d96b449082 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -612,13 +612,23 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "clone\0" "execveat\0" "fork\0" + "getpid\0" + "getppid\0" + "getrusage\0" + "gettid\0" "kill\0" "prctl\0" + "rt_sigqueueinfo\0" + "rt_tgsigqueueinfo\0" "setns\0" "tgkill\0" + "times\0" "tkill\0" "unshare\0" "vfork\0" + "wait4\0" + "waitid\0" + "waitpid\0" }, [SYSCALL_FILTER_SET_RAW_IO] = { .name = "@raw-io", From 0963c053fa59b6fe3a40da1e05c0ca9c4c54eb46 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 13 Sep 2017 19:44:11 +0200 Subject: [PATCH 07/12] seccomp: augment the @resources group a bit Given that sched_setattr/sched_setparam/sched_setscheduler are already in the group the closely related nice + ioprio_set should also be included. Also, order things alphabetically. --- src/shared/seccomp-util.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index d96b449082..f4f4eb36d3 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -658,17 +658,19 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { .name = "@resources", .help = "Alter resource settings", .value = - "sched_setparam\0" - "sched_setscheduler\0" - "sched_setaffinity\0" - "setpriority\0" - "setrlimit\0" - "set_mempolicy\0" + "ioprio_set\0" + "mbind\0" "migrate_pages\0" "move_pages\0" - "mbind\0" - "sched_setattr\0" + "nice\0" "prlimit64\0" + "sched_setaffinity\0" + "sched_setattr\0" + "sched_setparam\0" + "sched_setscheduler\0" + "set_mempolicy\0" + "setpriority\0" + "setrlimit\0" }, [SYSCALL_FILTER_SET_SETUID] = { .name = "@setuid", From cd0ddf6f754ec364c0ec69bd4bb51f566065290e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 13 Sep 2017 19:55:16 +0200 Subject: [PATCH 08/12] seccomp: add four new syscall groups These groups should be useful shortcuts for sets of closely related syscalls where it usually makes more sense to allow them altogether or not at all. --- man/systemd.exec.xml | 16 +++++++++ src/shared/seccomp-util.c | 71 +++++++++++++++++++++++++++++++++++++++ src/shared/seccomp-util.h | 4 +++ 3 files changed, 91 insertions(+) diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 625063f1c0..edc070f6ea 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -1461,6 +1461,10 @@ CapabilityBoundingSet=~CAP_B CAP_C @cpu-emulation System calls for CPU emulation functionality (vm862 and related calls) + + @credentials + System calls for querying process credentials (getuid2, capget2, and related calls) + @debug Debugging, performance monitoring and tracing functionality (ptrace2, perf_event_open2 and related calls) @@ -1481,6 +1485,10 @@ CapabilityBoundingSet=~CAP_B CAP_C @keyring Kernel keyring access (keyctl2 and related calls) + + @memlock + Locking of memory into RAM (mlock2, mlockall2 and related calls) + @module Loading and unloading of kernel modules (init_module2, delete_module2 and related calls) @@ -1521,10 +1529,18 @@ CapabilityBoundingSet=~CAP_B CAP_C @setuid System calls for changing user ID and group ID credentials, (setuid2, setgid2, setresuid2, …) + + @signal + System calls for manipulating and handling process signals (signal2, sigprocmask2, …) + @swap System calls for enabling/disabling swap devices (swapon2, swapoff2) + + @timer + System calls for scheduling operations by time (alarm2, timer_create2, …) + diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index f4f4eb36d3..e282469155 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -333,6 +333,32 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "vm86\0" "vm86old\0" }, + [SYSCALL_FILTER_SET_CREDENTIALS] = { + .name = "@credentials", + .help = "Query own process credentials", + .value = + "capget\0" + "getegid\0" + "getegid32\0" + "geteuid\0" + "geteuid32\0" + "getgid\0" + "getgid32\0" + "getgroups\0" + "getgroups32\0" + "getpgid\0" + "getpgrp\0" + "getpid\0" + "getppid\0" + "getresgid\0" + "getresgid32\0" + "getresuid\0" + "getresuid32\0" + "getsid\0" + "gettid\0" + "getuid\0" + "getuid32\0" + }, [SYSCALL_FILTER_SET_DEBUG] = { .name = "@debug", .help = "Debugging, performance monitoring and tracing functionality", @@ -487,6 +513,16 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "keyctl\0" "request_key\0" }, + [SYSCALL_FILTER_SET_MEMLOCK] = { + .name = "@memlock", + .help = "Memory locking control", + .value = + "mlock\0" + "mlock2\0" + "mlockall\0" + "munlock\0" + "munlockall\0" + }, [SYSCALL_FILTER_SET_MODULE] = { .name = "@module", .help = "Loading and unloading of kernel modules", @@ -691,6 +727,24 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "setuid\0" "setuid32\0" }, + [SYSCALL_FILTER_SET_SIGNAL] = { + .name = "@signal", + .help = "Process signal handling", + .value = + "rt_sigaction\0" + "rt_sigpending\0" + "rt_sigprocmask\0" + "rt_sigsuspend\0" + "rt_sigtimedwait\0" + "sigaction\0" + "sigaltstack\0" + "signal\0" + "signalfd\0" + "signalfd4\0" + "sigpending\0" + "sigprocmask\0" + "sigsuspend\0" + }, [SYSCALL_FILTER_SET_SWAP] = { .name = "@swap", .help = "Enable/disable swap devices", @@ -698,6 +752,23 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "swapoff\0" "swapon\0" }, + [SYSCALL_FILTER_SET_TIMER] = { + .name = "@timer", + .help = "Schedule operations by time", + .value = + "alarm\0" + "getitimer\0" + "setitimer\0" + "timer_create\0" + "timer_delete\0" + "timer_getoverrun\0" + "timer_gettime\0" + "timer_settime\0" + "timerfd_create\0" + "timerfd_gettime\0" + "timerfd_settime\0" + "times\0" + }, }; const SyscallFilterSet *syscall_filter_set_find(const char *name) { diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h index c1612f5894..d4ff027df8 100644 --- a/src/shared/seccomp-util.h +++ b/src/shared/seccomp-util.h @@ -44,11 +44,13 @@ enum { SYSCALL_FILTER_SET_BASIC_IO, SYSCALL_FILTER_SET_CLOCK, SYSCALL_FILTER_SET_CPU_EMULATION, + SYSCALL_FILTER_SET_CREDENTIALS, SYSCALL_FILTER_SET_DEBUG, SYSCALL_FILTER_SET_FILE_SYSTEM, SYSCALL_FILTER_SET_IO_EVENT, SYSCALL_FILTER_SET_IPC, SYSCALL_FILTER_SET_KEYRING, + SYSCALL_FILTER_SET_MEMLOCK, SYSCALL_FILTER_SET_MODULE, SYSCALL_FILTER_SET_MOUNT, SYSCALL_FILTER_SET_NETWORK_IO, @@ -59,7 +61,9 @@ enum { SYSCALL_FILTER_SET_REBOOT, SYSCALL_FILTER_SET_RESOURCES, SYSCALL_FILTER_SET_SETUID, + SYSCALL_FILTER_SET_SIGNAL, SYSCALL_FILTER_SET_SWAP, + SYSCALL_FILTER_SET_TIMER, _SYSCALL_FILTER_SET_MAX }; From 25e94f8c75ffdb1ff173de70b579609d8d5717f5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 13 Sep 2017 19:56:35 +0200 Subject: [PATCH 09/12] tests: let's make sure the seccomp filter lists remain properly ordered It's too easy to corrupt the order, hence let's check for the right order automatically as part of testing. --- src/test/test-seccomp.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/test/test-seccomp.c b/src/test/test-seccomp.c index 5056a08117..4d63b68809 100644 --- a/src/test/test-seccomp.c +++ b/src/test/test-seccomp.c @@ -612,6 +612,36 @@ static void test_lock_personality(void) { assert_se(wait_for_terminate_and_warn("lockpersonalityseccomp", pid, true) == EXIT_SUCCESS); } +static void test_filter_sets_ordered(void) { + size_t i; + + /* Ensure "@default" always remains at the beginning of the list */ + assert_se(SYSCALL_FILTER_SET_DEFAULT == 0); + assert_se(streq(syscall_filter_sets[0].name, "@default")); + + for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) { + const char *k, *p = NULL; + + /* Make sure each group has a description */ + assert_se(!isempty(syscall_filter_sets[0].help)); + + /* Make sure the groups are ordered alphabetically, except for the first entry */ + assert_se(i < 2 || strcmp(syscall_filter_sets[i-1].name, syscall_filter_sets[i].name) < 0); + + NULSTR_FOREACH(k, syscall_filter_sets[i].value) { + + /* Ensure each syscall list is in itself ordered, but groups before names */ + assert_se(!p || + (*p == '@' && *k != '@') || + (((*p == '@' && *k == '@') || + (*p != '@' && *k != '@')) && + strcmp(p, k) < 0)); + + p = k; + } + } +} + int main(int argc, char *argv[]) { log_set_max_level(LOG_DEBUG); @@ -629,6 +659,7 @@ int main(int argc, char *argv[]) { test_restrict_archs(); test_load_syscall_filter_set_raw(); test_lock_personality(); + test_filter_sets_ordered(); return 0; } From cff7bff880b62faa8ee17edb0503449e639a2630 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 13 Sep 2017 19:57:32 +0200 Subject: [PATCH 10/12] seccomp: improve debug logging Let's log explicitly at debug level if we encounter a syscall or group that doesn#t exist at all. --- src/shared/seccomp-util.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index e282469155..5e78a088e9 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -799,8 +799,10 @@ int seccomp_add_syscall_filter_item(scmp_filter_ctx *seccomp, const char *name, const SyscallFilterSet *other; other = syscall_filter_set_find(name); - if (!other) + if (!other) { + log_debug("Filter set %s is not known!", name); return -EINVAL; + } r = seccomp_add_syscall_filter_set(seccomp, other, action, exclude); if (r < 0) @@ -809,8 +811,10 @@ int seccomp_add_syscall_filter_item(scmp_filter_ctx *seccomp, const char *name, int id; id = seccomp_syscall_resolve_name(name); - if (id == __NR_SCMP_ERROR) + if (id == __NR_SCMP_ERROR) { + log_debug("System call %s is not known!", name); return -EINVAL; /* Not known at all? Then that's a real error */ + } r = seccomp_rule_add_exact(seccomp, action, id, 0); if (r < 0) From 96bedbe2e5301fe0e93993de0e9a31baf2679168 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 14 Sep 2017 10:18:57 +0200 Subject: [PATCH 11/12] nspawn: replace syscall blacklist by a whitelist Let's lock things down a bit, and maintain a list of what's permitted rather than a list of what's prohibited in nspawn (also to make things a bit more like Docker and friends). Note that this slightly alters the effect of --system-call-filter=, as now the negative list now takes precedence over the positive list. However, given that the option is just a few days old and not included in any released version it should be fine to change it at this point in time. Note that the whitelist is good chunk more restrictive thatn the previous blacklist. Specifically: - fanotify is not permitted (given the buffer size issues it's problematic in containers) - nfsservctl is not permitted (NFS server support is not virtualized) - pkey_xyz stuff is not permitted (really new stuff I don't grok) - @cpu-emulation is prohibited (untested legacy stuff mostly, and if people really want to run dosemu in nspawn, they should use --system-call-filter=@cpu-emulation and all should be good) --- man/systemd-nspawn.xml | 6 +- src/nspawn/nspawn-seccomp.c | 193 +++++++++++++++++++++++++++++------- 2 files changed, 159 insertions(+), 40 deletions(-) diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index c4db6a3ada..3951e32e8f 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -723,9 +723,9 @@ system calls will be permitted. The list may optionally be prefixed by ~, in which case all listed system calls are prohibited. If this command line option is used multiple times the configured lists are combined. If both a positive and a negative list (that is one system call list without and one with the - ~ prefix) are configured, the positive list takes precedence over the negative list. Note - that systemd-nspawn always implements a system call blacklist (as opposed to a whitelist), - and this command line option hence adds or removes entries from the default blacklist, depending on the + ~ prefix) are configured, the negative list takes precedence over the positive list. Note + that systemd-nspawn always implements a system call whitelist (as opposed to a blacklist), + and this command line option hence adds or removes entries from the default whitelist, depending on the ~ prefix. Note that the applied system call filter is also altered implicitly if additional capabilities are passed using the --capabilities=. diff --git a/src/nspawn/nspawn-seccomp.c b/src/nspawn/nspawn-seccomp.c index a6f7a7dabc..db3d098e7f 100644 --- a/src/nspawn/nspawn-seccomp.c +++ b/src/nspawn/nspawn-seccomp.c @@ -47,47 +47,154 @@ static int seccomp_add_default_syscall_filter( static const struct { uint64_t capability; const char* name; - } blacklist[] = { - { 0, "@obsolete" }, - { 0, "@keyring" }, /* keyring is not namespaced */ - { 0, "bpf" }, - { 0, "kexec_file_load" }, - { 0, "kexec_load" }, - { 0, "lookup_dcookie" }, - { 0, "open_by_handle_at" }, - { 0, "perf_event_open" }, - { 0, "quotactl" }, - { 0, "@swap" }, - { CAP_SYSLOG, "syslog" }, - { CAP_SYS_MODULE, "@module" }, - { CAP_SYS_PACCT, "acct" }, - { CAP_SYS_PTRACE, "process_vm_readv" }, - { CAP_SYS_PTRACE, "process_vm_writev" }, - { CAP_SYS_PTRACE, "ptrace" }, - { CAP_SYS_RAWIO, "@raw-io" }, - { CAP_SYS_TIME, "@clock" }, + } whitelist[] = { + /* Let's use set names where we can */ + { 0, "@basic-io" }, + { 0, "@credentials" }, + { 0, "@default" }, + { 0, "@file-system" }, + { 0, "@io-event" }, + { 0, "@ipc" }, + { 0, "@mount" }, + { 0, "@network-io" }, + { 0, "@process" }, + { 0, "@resources" }, + { 0, "@setuid" }, + { 0, "@signal" }, + { 0, "@timer" }, + + /* The following four are sets we optionally enable, in case the caps have been configured for it */ + { CAP_SYS_TIME, "@clock" }, + { CAP_SYS_MODULE, "@module" }, + { CAP_SYS_RAWIO, "@raw-io" }, + { CAP_IPC_LOCK, "@memlock" }, + + /* Plus a good set of additional syscalls which are not part of any of the groups above */ + { 0, "brk" }, + { 0, "capset" }, + { 0, "chown" }, + { 0, "chown32" }, + { 0, "copy_file_range" }, + { 0, "fadvise64" }, + { 0, "fadvise64_64" }, + { 0, "fchown" }, + { 0, "fchown32" }, + { 0, "fchownat" }, + { 0, "fdatasync" }, + { 0, "flock" }, + { 0, "fsync" }, + { 0, "get_mempolicy" }, + { 0, "getcpu" }, + { 0, "getpriority" }, + { 0, "getrandom" }, + { 0, "io_cancel" }, + { 0, "io_destroy" }, + { 0, "io_getevents" }, + { 0, "io_setup" }, + { 0, "io_submit" }, + { 0, "ioctl" }, + { 0, "ioprio_get" }, + { 0, "kcmp" }, + { 0, "lchown" }, + { 0, "lchown32" }, + { 0, "madvise" }, + { 0, "mincore" }, + { 0, "mprotect" }, + { 0, "mremap" }, + { 0, "msync" }, + { 0, "name_to_handle_at" }, + { 0, "oldolduname" }, + { 0, "olduname" }, + { 0, "personality" }, + { 0, "preadv2" }, + { 0, "pwritev2" }, + { 0, "readahead" }, + { 0, "readdir" }, + { 0, "remap_file_pages" }, + { 0, "sched_get_priority_max" }, + { 0, "sched_get_priority_min" }, + { 0, "sched_getaffinity" }, + { 0, "sched_getattr" }, + { 0, "sched_getparam" }, + { 0, "sched_getscheduler" }, + { 0, "sched_rr_get_interval" }, + { 0, "sched_yield" }, + { 0, "seccomp" }, + { 0, "sendfile" }, + { 0, "sendfile64" }, + { 0, "setdomainname" }, + { 0, "setfsgid" }, + { 0, "setfsgid32" }, + { 0, "setfsuid" }, + { 0, "setfsuid32" }, + { 0, "sethostname" }, + { 0, "setpgid" }, + { 0, "setsid" }, + { 0, "splice" }, + { 0, "sync" }, + { 0, "sync_file_range" }, + { 0, "syncfs" }, + { 0, "sysinfo" }, + { 0, "tee" }, + { 0, "ugetrlimit" }, + { 0, "umask" }, + { 0, "uname" }, + { 0, "userfaultfd" }, + { 0, "vmsplice" }, + + /* The following individual syscalls are added depending on specified caps */ + { CAP_SYS_PACCT, "acct" }, + { CAP_SYS_PTRACE, "process_vm_readv" }, + { CAP_SYS_PTRACE, "process_vm_writev" }, + { CAP_SYS_PTRACE, "ptrace" }, + { CAP_SYS_BOOT, "reboot" }, + { CAP_SYSLOG, "syslog" }, + { CAP_SYS_TTY_CONFIG, "vhangup" }, + + /* + * The following syscalls and groups are knowingly excluded: + * + * @cpu-emulation + * @keyring (NB: keyring is not namespaced!) + * @obsolete + * @swap + * + * bpf (NB: bpffs is not namespaced!) + * fanotify_init + * fanotify_mark + * kexec_file_load + * kexec_load + * lookup_dcookie + * nfsservctl + * open_by_handle_at + * perf_event_open + * pkey_alloc + * pkey_free + * pkey_mprotect + * quotactl + */ }; int r, c = 0; size_t i; char **p; - for (i = 0; i < ELEMENTSOF(blacklist); i++) { - if (blacklist[i].capability != 0 && (cap_list_retain & (1ULL << blacklist[i].capability))) + for (i = 0; i < ELEMENTSOF(whitelist); i++) { + if (whitelist[i].capability != 0 && (cap_list_retain & (1ULL << whitelist[i].capability)) == 0) continue; - r = seccomp_add_syscall_filter_item(ctx, blacklist[i].name, SCMP_ACT_ERRNO(EPERM), syscall_whitelist); + r = seccomp_add_syscall_filter_item(ctx, whitelist[i].name, SCMP_ACT_ALLOW, syscall_blacklist); if (r < 0) /* If the system call is not known on this architecture, then that's fine, let's ignore it */ - log_debug_errno(r, "Failed to add rule for system call %s, ignoring: %m", blacklist[i].name); + log_debug_errno(r, "Failed to add rule for system call %s on %s, ignoring: %m", whitelist[i].name, seccomp_arch_to_string(arch)); else c++; } - STRV_FOREACH(p, syscall_blacklist) { - r = seccomp_add_syscall_filter_item(ctx, *p, SCMP_ACT_ERRNO(EPERM), syscall_whitelist); + STRV_FOREACH(p, syscall_whitelist) { + r = seccomp_add_syscall_filter_item(ctx, *p, SCMP_ACT_ALLOW, syscall_blacklist); if (r < 0) - log_debug_errno(r, "Failed to add rule for system call %s, ignoring: %m", *p); + log_debug_errno(r, "Failed to add rule for system call %s on %s, ignoring: %m", *p, seccomp_arch_to_string(arch)); else c++; } @@ -106,18 +213,33 @@ int setup_seccomp(uint64_t cap_list_retain, char **syscall_whitelist, char **sys SECCOMP_FOREACH_LOCAL_ARCH(arch) { _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; - int n; - log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch)); + log_debug("Applying whitelist on architecture: %s", seccomp_arch_to_string(arch)); + + r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ERRNO(EPERM)); + if (r < 0) + return log_error_errno(r, "Failed to allocate seccomp object: %m"); + + r = seccomp_add_default_syscall_filter(seccomp, arch, cap_list_retain, syscall_whitelist, syscall_blacklist); + if (r < 0) + return r; + + r = seccomp_load(seccomp); + if (IN_SET(r, -EPERM, -EACCES)) + return log_error_errno(r, "Failed to install seccomp filter: %m"); + if (r < 0) + log_debug_errno(r, "Failed to install filter set for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); + } + + SECCOMP_FOREACH_LOCAL_ARCH(arch) { + _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; + + log_debug("Applying NETLINK_AUDIT mask on architecture: %s", seccomp_arch_to_string(arch)); r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW); if (r < 0) return log_error_errno(r, "Failed to allocate seccomp object: %m"); - n = seccomp_add_default_syscall_filter(seccomp, arch, cap_list_retain, syscall_whitelist, syscall_blacklist); - if (n < 0) - return n; - /* Audit is broken in containers, much of the userspace audit hookup will fail if running inside a container. We don't care and just turn off creation of audit sockets. @@ -133,13 +255,10 @@ int setup_seccomp(uint64_t cap_list_retain, char **syscall_whitelist, char **sys 2, SCMP_A0(SCMP_CMP_EQ, AF_NETLINK), SCMP_A2(SCMP_CMP_EQ, NETLINK_AUDIT)); - if (r < 0) + if (r < 0) { log_debug_errno(r, "Failed to add audit seccomp rule, ignoring: %m"); - else - n++; - - if (n <= 0) /* no rule added? then skip this architecture */ continue; + } r = seccomp_load(seccomp); if (IN_SET(r, -EPERM, -EACCES)) From 960d20e1a934b304493663a3d26e138e4adac08a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 14 Sep 2017 10:23:36 +0200 Subject: [PATCH 12/12] man: minor correction for systemd-run The meaning was acidentally inverted in 156d6036be8c4d64747b5919adf372c289d3423a, let's correct this. --- man/systemd-run.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/systemd-run.xml b/man/systemd-run.xml index 9a00c321d9..7477195dab 100644 --- a/man/systemd-run.xml +++ b/man/systemd-run.xml @@ -239,7 +239,7 @@ systemd-run command itself. This allows systemd-run to be used within shell pipelines. Note that this mode is not suitable for interactive command shells and similar, as the - service process will become a TTY controller when invoked on a terminal. Use instead + service process will not become a TTY controller when invoked on a terminal. Use instead in that case. When both and are used in combination the more appropriate