From 4bc51378189aa626f585a7b3db93d5c45bc88cf4 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 3 Jul 2020 13:18:36 +0200 Subject: [PATCH] seccomp: support allowlist/denylist in profiles Signed-off-by: Christian Brauner --- config/templates/common.conf.in | 4 +- config/templates/common.seccomp | 2 +- doc/examples/Makefile.am | 6 +-- doc/examples/seccomp-v1.conf | 2 +- ...lacklist.conf => seccomp-v2-denylist.conf} | 4 +- doc/examples/seccomp-v2.conf | 8 ++-- doc/ja/lxc.container.conf.sgml.in | 18 ++++---- doc/ko/lxc.container.conf.sgml.in | 16 +++---- doc/lxc.container.conf.sgml.in | 16 +++---- src/lxc/cgroups/cgroup2_devices.c | 2 +- src/lxc/seccomp.c | 45 +++++++++++++------ src/lxc/string_utils.h | 10 +++++ 12 files changed, 81 insertions(+), 52 deletions(-) rename doc/examples/{seccomp-v2-blacklist.conf => seccomp-v2-denylist.conf} (84%) diff --git a/config/templates/common.conf.in b/config/templates/common.conf.in index 286c5e4a3e..a66755271f 100644 --- a/config/templates/common.conf.in +++ b/config/templates/common.conf.in @@ -17,7 +17,7 @@ lxc.hook.clone = @LXCHOOKDIR@/clonehostname # Default legacy cgroup configuration # -# CGroup whitelist +# CGroup allowlist lxc.cgroup.devices.deny = a ## Allow any mknod (but not reading/writing the node) lxc.cgroup.devices.allow = c *:* m @@ -46,7 +46,7 @@ lxc.cgroup.devices.allow = c 10:229 rwm # Default unified cgroup configuration # -# CGroup whitelist +# CGroup allowlist lxc.cgroup2.devices.deny = a ## Allow any mknod (but not reading/writing the node) lxc.cgroup2.devices.allow = c *:* m diff --git a/config/templates/common.seccomp b/config/templates/common.seccomp index 6f8eeba383..bb09108a26 100644 --- a/config/templates/common.seccomp +++ b/config/templates/common.seccomp @@ -1,5 +1,5 @@ 2 -blacklist +denylist reject_force_umount # comment this to allow umount -f; not recommended [all] kexec_load errno 1 diff --git a/doc/examples/Makefile.am b/doc/examples/Makefile.am index a420dfab2d..f97ca6c849 100644 --- a/doc/examples/Makefile.am +++ b/doc/examples/Makefile.am @@ -10,7 +10,7 @@ pkgexamples_DATA = \ lxc-veth.conf \ lxc-complex.conf \ seccomp-v1.conf \ - seccomp-v2-blacklist.conf \ + seccomp-v2-denylist.conf \ seccomp-v2.conf endif @@ -23,10 +23,10 @@ noinst_DATA = \ lxc-veth.conf.in \ lxc-complex.conf.in \ seccomp-v1.conf \ - seccomp-v2-blacklist.conf \ + seccomp-v2-denylist.conf \ seccomp-v2.conf EXTRA_DIST = \ seccomp-v1.conf \ - seccomp-v2-blacklist.conf \ + seccomp-v2-denylist.conf \ seccomp-v2.conf diff --git a/doc/examples/seccomp-v1.conf b/doc/examples/seccomp-v1.conf index 678c9c45f3..62abe1d463 100644 --- a/doc/examples/seccomp-v1.conf +++ b/doc/examples/seccomp-v1.conf @@ -1,5 +1,5 @@ 1 -whitelist +allowlist 0 1 2 diff --git a/doc/examples/seccomp-v2-blacklist.conf b/doc/examples/seccomp-v2-denylist.conf similarity index 84% rename from doc/examples/seccomp-v2-blacklist.conf rename to doc/examples/seccomp-v2-denylist.conf index 1a9222cbbe..bf8796e005 100644 --- a/doc/examples/seccomp-v2-blacklist.conf +++ b/doc/examples/seccomp-v2-denylist.conf @@ -1,7 +1,7 @@ 2 -blacklist +denylist # v2 allows comments after the second line, with '#' in first column, -# blacklist will allow syscalls by default +# denylist will allow syscalls by default # if 'errno 0' was not appended to 'mknod' below, then the task would # simply be killed when it tried to mknod. 'errno 0' means do not allow # the container to mknod, but immediately return 0. diff --git a/doc/examples/seccomp-v2.conf b/doc/examples/seccomp-v2.conf index 1aa82a3b1b..640d806e1c 100644 --- a/doc/examples/seccomp-v2.conf +++ b/doc/examples/seccomp-v2.conf @@ -1,7 +1,7 @@ 2 -whitelist trap -# 'whitelist' would normally mean kill a task doing any syscall which is not -# whitelisted below. By appending 'trap' to the line, we will cause a SIGSYS +allowlist trap +# 'allowlist' would normally mean kill a task doing any syscall which is not +# allowlisted below. By appending 'trap' to the line, we will cause a SIGSYS # to be sent to the task instead. 'errno 0' would mean don't allow the system # call but immediately return 0. 'errno 22' would mean return EINVAL immediately. [x86_64] @@ -20,5 +20,5 @@ read write mount umount2 -# Do note that this policy does not whitelist enough system calls to allow a +# Do note that this policy does not allowlist enough system calls to allow a # system container to boot. diff --git a/doc/ja/lxc.container.conf.sgml.in b/doc/ja/lxc.container.conf.sgml.in index 38b6232433..f3cca8a505 100644 --- a/doc/ja/lxc.container.conf.sgml.in +++ b/doc/ja/lxc.container.conf.sgml.in @@ -2239,7 +2239,7 @@ by KATOH Yasufumi standard namespace identifiers as seen in the /proc/PID/ns directory. The is a - blacklist option, i.e. it is useful when enforcing that containers + denylist option, i.e. it is useful when enforcing that containers must keep a specific set of namespaces. --> コンテナが、作成元のプロセスから継承する (新しい名前空間を作らずに元のプロセスの名前空間のまま実行する) 名前空間を指定します。継承する名前空間はスペース区切りのリストで指定します。指定する名前空間名は、/proc/PID/ns ディレクトリ内に存在する標準の名前空間指示子でなければなりません。 はブラックリストを指定するオプションです。つまり、コンテナに特定の名前空間を使い続けることを強制したい場合に便利です。 @@ -2660,18 +2660,18 @@ by KATOH Yasufumi - 現時点では、バージョン番号は 1 と 2 をサポートしています。バージョン 1 では、ポリシーはシンプルなホワイトリストですので、2 行目は "whitelist" でなければなりません。 + 現時点では、バージョン番号は 1 と 2 をサポートしています。バージョン 1 では、ポリシーはシンプルなホワイトリストですので、2 行目は "allowlist" でなければなりません。 そして残りの行には 1 行に 1 つずつ、システムコール番号を書きます。各行のシステムコール番号がホワイトリスト化され、リストにない番号は、そのコンテナではブラックリストに入ります。 @@ -2679,7 +2679,7 @@ by KATOH Yasufumi @@ -2688,7 +2688,7 @@ by KATOH Yasufumi 2 - blacklist + denylist mknod errno 0 ioctl notify diff --git a/doc/ko/lxc.container.conf.sgml.in b/doc/ko/lxc.container.conf.sgml.in index b72c88122a..f87a293896 100644 --- a/doc/ko/lxc.container.conf.sgml.in +++ b/doc/ko/lxc.container.conf.sgml.in @@ -1736,17 +1736,17 @@ proc proc proc nodev,noexec,nosuid 0 0 - 현재는 버전1과 2만 지원된다. 버전 1에서는 정책은 단순한 화이트리스트이다. 그러므로 두번째 라인은 반드시 "whitelist"여야 한다. 파일의 나머지 내용은 한 줄에 하나의 시스템콜 번호로 채워진다. 화이트리스트에 없는 번호는 컨테이너에서 블랙리스트로 들어간다. + 현재는 버전1과 2만 지원된다. 버전 1에서는 정책은 단순한 화이트리스트이다. 그러므로 두번째 라인은 반드시 "allowlist"여야 한다. 파일의 나머지 내용은 한 줄에 하나의 시스템콜 번호로 채워진다. 화이트리스트에 없는 번호는 컨테이너에서 블랙리스트로 들어간다. @@ -1754,7 +1754,7 @@ proc proc proc nodev,noexec,nosuid 0 0 @@ -1762,7 +1762,7 @@ proc proc proc nodev,noexec,nosuid 0 0 2 -blacklist +denylist mknod errno 0 diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in index 3ed71c2148..6378f6c002 100644 --- a/doc/lxc.container.conf.sgml.in +++ b/doc/lxc.container.conf.sgml.in @@ -1676,7 +1676,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA standard namespace identifiers as seen in the /proc/PID/ns directory. The is a - blacklist option, i.e. it is useful when enforcing that containers + denylist option, i.e. it is useful when enforcing that containers must keep a specific set of namespaces. @@ -1984,26 +1984,26 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Versions 1 and 2 are currently supported. In version 1, the - policy is a simple whitelist. The second line therefore must - read "whitelist", with the rest of the file containing one (numeric) - syscall number per line. Each syscall number is whitelisted, - while every unlisted number is blacklisted for use in the container + policy is a simple allowlist. The second line therefore must + read "allowlist", with the rest of the file containing one (numeric) + syscall number per line. Each syscall number is allowlisted, + while every unlisted number is denylisted for use in the container - In version 2, the policy may be blacklist or whitelist, + In version 2, the policy may be denylist or allowlist, supports per-rule and per-policy default actions, and supports per-architecture system call resolution from textual names. - An example blacklist policy, in which all system calls are + An example denylist policy, in which all system calls are allowed except for mknod, which will simply do nothing and return 0 (success), looks like: 2 - blacklist + denylist mknod errno 0 ioctl notify diff --git a/src/lxc/cgroups/cgroup2_devices.c b/src/lxc/cgroups/cgroup2_devices.c index db220e2ce7..59dc4fee2b 100644 --- a/src/lxc/cgroups/cgroup2_devices.c +++ b/src/lxc/cgroups/cgroup2_devices.c @@ -174,7 +174,7 @@ struct bpf_program *bpf_program_new(uint32_t prog_type) prog->prog_type = prog_type; prog->kernel_fd = -EBADF; /* - * By default a whitelist is used unless the user tells us otherwise. + * By default a allowlist is used unless the user tells us otherwise. */ prog->device_list_type = LXC_BPF_DEVICE_CGROUP_ALLOWLIST; diff --git a/src/lxc/seccomp.c b/src/lxc/seccomp.c index 7820db8b2d..d9541fb572 100644 --- a/src/lxc/seccomp.c +++ b/src/lxc/seccomp.c @@ -99,7 +99,7 @@ static uint32_t get_v2_default_action(char *line) while (*line == ' ') line++; - /* After 'whitelist' or 'blacklist' comes default behavior. */ + /* After 'allowlist' or 'denylist' comes default behavior. */ if (strncmp(line, "kill", 4) == 0) { ret_action = SCMP_ACT_KILL; } else if (strncmp(line, "errno", 5) == 0) { @@ -561,6 +561,27 @@ bool do_resolve_add_rule(uint32_t arch, char *line, scmp_filter_ctx ctx, return true; } +/* + * It is unfortunate, but we can't simply remove those terms since this would + * break way too many users. + */ +#define BACKWARDCOMPAT_TERMINOLOGY_DENYLIST "blacklist" +#define BACKWARDCOMPAT_TERMINOLOGY_ALLOWLIST "whitelist" + +static inline bool is_denylist(const char *type) +{ + return strnequal(type, "denylist", STRLITERALLEN("denylist")) || + strnequal(type, BACKWARDCOMPAT_TERMINOLOGY_DENYLIST, + STRLITERALLEN(BACKWARDCOMPAT_TERMINOLOGY_DENYLIST)); +} + +static inline bool is_allowlist(const char *type) +{ + return strnequal(type, "allowlist", STRLITERALLEN("allowlist")) || + strnequal(type, BACKWARDCOMPAT_TERMINOLOGY_ALLOWLIST, + STRLITERALLEN(BACKWARDCOMPAT_TERMINOLOGY_ALLOWLIST)); +} + /* * v2 consists of * [x86] @@ -580,7 +601,7 @@ static int parse_config_v2(FILE *f, char *line, size_t *line_bufsz, struct lxc_c int ret; char *p; enum lxc_hostarch_t cur_rule_arch, native_arch; - bool blacklist = false; + bool denylist = false; uint32_t default_policy_action = -1, default_rule_action = -1; struct seccomp_v2_rule rule; struct scmp_ctx_info { @@ -589,12 +610,10 @@ static int parse_config_v2(FILE *f, char *line, size_t *line_bufsz, struct lxc_c bool needs_merge[3]; } ctx; - if (strncmp(line, "blacklist", 9) == 0) - blacklist = true; - else if (strncmp(line, "whitelist", 9) != 0) { - ERROR("Bad seccomp policy style \"%s\"", line); - return -1; - } + if (is_denylist(line)) + denylist = true; + else if (!is_allowlist(line)) + return log_error(-EINVAL, "Bad seccomp policy style \"%s\"", line); p = strchr(line, ' '); if (p) { @@ -603,8 +622,8 @@ static int parse_config_v2(FILE *f, char *line, size_t *line_bufsz, struct lxc_c return -1; } - /* for blacklist, allow any syscall which has no rule */ - if (blacklist) { + /* for denylist, allow any syscall which has no rule */ + if (denylist) { if (default_policy_action == -1) default_policy_action = SCMP_ACT_ALLOW; @@ -1079,7 +1098,7 @@ static int parse_config_v2(FILE *f, char *line, struct lxc_conf *conf) * the second line has some directives * then comes policy subject to the directives * right now version must be '1' or '2' - * the directives must include 'whitelist'(version == 1 or 2) or 'blacklist' + * the directives must include 'allowlist'(version == 1 or 2) or 'denylist' * (version == 2) and can include 'debug' (though debug is not yet supported). */ static int parse_config(FILE *f, struct lxc_conf *conf) @@ -1099,8 +1118,8 @@ static int parse_config(FILE *f, struct lxc_conf *conf) goto bad_line; } - if (version == 1 && !strstr(line, "whitelist")) { - ERROR("Only whitelist policy is supported"); + if (version == 1 && !strstr(line, "allowlist")) { + ERROR("Only allowlist policy is supported"); goto bad_line; } diff --git a/src/lxc/string_utils.h b/src/lxc/string_utils.h index 0f7d2ff21e..3bd2883e83 100644 --- a/src/lxc/string_utils.h +++ b/src/lxc/string_utils.h @@ -118,4 +118,14 @@ static inline ssize_t safe_strlcat(char *src, const char *append, size_t len) return (ssize_t)new_len; } +static inline bool strnequal(const char *str, const char *eq, size_t len) +{ + return strncmp(str, eq, len) == 0; +} + +static inline bool strequal(const char *str, const char *eq) +{ + return strcmp(str, eq) == 0; +} + #endif /* __LXC_STRING_UTILS_H */