diff options
author | Quentin Deslandes <qde@naccy.de> | 2022-09-13 17:15:13 +0200 |
---|---|---|
committer | Quentin Deslandes <qde@naccy.de> | 2022-12-15 10:57:39 +0100 |
commit | 87a13dabbd81c2e31fd5ac7b81cce61baf23e59c (patch) | |
tree | 83b4ddf0c6602b544ccf97a5110586760b1a5c25 | |
parent | Create hash_ops structure to free keys of type pcre2_code (diff) | |
download | systemd-87a13dabbd81c2e31fd5ac7b81cce61baf23e59c.tar.xz systemd-87a13dabbd81c2e31fd5ac7b81cce61baf23e59c.zip |
journal: filter log based on LogFilterPatterns
Use LogFilterPatterns from the unit's cgroup xattr in order to keep or
discard log messages before writing them to the journal.
When a log message is discarded, it won't be written to syslog, console...
either.
When a native, syslog, or standard output log message is received,
systemd-journald will process it if it matches against at least one
allowed pattern (if any) and none of the denied patterns (if any).
Diffstat (limited to '')
-rw-r--r-- | src/journal/journald-client.c | 110 | ||||
-rw-r--r-- | src/journal/journald-client.h | 7 | ||||
-rw-r--r-- | src/journal/journald-context.c | 6 | ||||
-rw-r--r-- | src/journal/journald-context.h | 4 | ||||
-rw-r--r-- | src/journal/journald-native.c | 8 | ||||
-rw-r--r-- | src/journal/journald-stream.c | 5 | ||||
-rw-r--r-- | src/journal/journald-syslog.c | 4 | ||||
-rw-r--r-- | src/journal/meson.build | 2 |
8 files changed, 146 insertions, 0 deletions
diff --git a/src/journal/journald-client.c b/src/journal/journald-client.c new file mode 100644 index 0000000000..22090aa93c --- /dev/null +++ b/src/journal/journald-client.c @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "cgroup-util.h" +#include "errno-util.h" +#include "journald-client.h" +#include "nulstr-util.h" +#include "pcre2-util.h" + +/* This consumes both `allow_list` and `deny_list` arguments. Hence, those arguments are not owned by the + * caller anymore and should not be freed. */ +static void client_set_filtering_patterns(ClientContext *c, Set *allow_list, Set *deny_list) { + assert(c); + + set_free_and_replace(c->log_filter_allowed_patterns, allow_list); + set_free_and_replace(c->log_filter_denied_patterns, deny_list); +} + +static int client_parse_log_filter_nulstr(const char *nulstr, size_t len, Set **ret) { + _cleanup_set_free_ Set *s = NULL; + _cleanup_strv_free_ char **patterns_strv = NULL; + int r; + + assert(nulstr); + assert(ret); + + patterns_strv = strv_parse_nulstr(nulstr, len); + if (!patterns_strv) + return log_oom_debug(); + + STRV_FOREACH(pattern, patterns_strv) { + _cleanup_(pattern_freep) pcre2_code *compiled_pattern = NULL; + + r = pattern_compile_and_log(*pattern, 0, &compiled_pattern); + if (r < 0) + return r; + + r = set_ensure_consume(&s, &pcre2_code_hash_ops_free, TAKE_PTR(compiled_pattern)); + if (r < 0) + return log_debug_errno(r, "Failed to insert regex into set: %m"); + } + + *ret = TAKE_PTR(s); + + return 0; +} + +int client_context_read_log_filter_patterns(ClientContext *c, const char *cgroup) { + char *deny_list_xattr, *xattr_end; + _cleanup_free_ char *xattr = NULL; + _cleanup_set_free_ Set *allow_list = NULL, *deny_list = NULL; + int r; + + assert(c); + + r = cg_get_xattr_malloc(SYSTEMD_CGROUP_CONTROLLER, cgroup, "user.journald_log_filter_patterns", &xattr); + if (r < 0) { + if (!ERRNO_IS_XATTR_ABSENT(r)) + return log_debug_errno(r, "Failed to get user.journald_log_filter_patterns xattr for %s: %m", cgroup); + + client_set_filtering_patterns(c, NULL, NULL); + return 0; + } + + xattr_end = xattr + r; + + /* We expect '0xff' to be present in the attribute, even if the lists are empty. We expect the + * following: + * - Allow list, but no deny list: 0xXX, ...., 0xff + * - No allow list, but deny list: 0xff, 0xXX, .... + * - Allow list, and deny list: 0xXX, ...., 0xff, 0xXX, .... + * This is due to the fact allowed and denied patterns list are two nulstr joined together with '0xff'. + * None of the allowed or denied nulstr have a nul-termination character. + * + * We do not expect both the allow list and deny list to be empty, as this condition is tested + * before writing to xattr. */ + deny_list_xattr = memchr(xattr, (char)0xff, r); + if (!deny_list_xattr) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Missing delimiter in cgroup user.journald_log_filter_patterns attribute: %m"); + + r = client_parse_log_filter_nulstr(xattr, deny_list_xattr - xattr, &allow_list); + if (r < 0) + return r; + + /* Use 'deny_list_xattr + 1' to skip '0xff'. */ + ++deny_list_xattr; + r = client_parse_log_filter_nulstr(deny_list_xattr, xattr_end - deny_list_xattr, &deny_list); + if (r < 0) + return r; + + client_set_filtering_patterns(c, TAKE_PTR(allow_list), TAKE_PTR(deny_list)); + + return 0; +} + +int client_context_check_keep_log(ClientContext *c, const char *message, size_t len) { + pcre2_code *regex; + + if (!c || !message) + return true; + + SET_FOREACH(regex, c->log_filter_denied_patterns) + if (pattern_matches_and_log(regex, message, len, NULL) > 0) + return false; + + SET_FOREACH(regex, c->log_filter_allowed_patterns) + if (pattern_matches_and_log(regex, message, len, NULL) > 0) + return true; + + return set_isempty(c->log_filter_allowed_patterns); +} diff --git a/src/journal/journald-client.h b/src/journal/journald-client.h new file mode 100644 index 0000000000..629cd41c7d --- /dev/null +++ b/src/journal/journald-client.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "journald-context.h" + +int client_context_read_log_filter_patterns(ClientContext *c, const char *cgroup); +int client_context_check_keep_log(ClientContext *c, const char *message, size_t len); diff --git a/src/journal/journald-context.c b/src/journal/journald-context.c index 222855ae60..55e657c7e1 100644 --- a/src/journal/journald-context.c +++ b/src/journal/journald-context.c @@ -14,6 +14,7 @@ #include "io-util.h" #include "journal-internal.h" #include "journal-util.h" +#include "journald-client.h" #include "journald-context.h" #include "parse-util.h" #include "path-util.h" @@ -180,6 +181,9 @@ static void client_context_reset(Server *s, ClientContext *c) { c->log_ratelimit_interval = s->ratelimit_interval; c->log_ratelimit_burst = s->ratelimit_burst; + + c->log_filter_allowed_patterns = set_free(c->log_filter_allowed_patterns); + c->log_filter_denied_patterns = set_free(c->log_filter_denied_patterns); } static ClientContext* client_context_free(Server *s, ClientContext *c) { @@ -290,6 +294,8 @@ static int client_context_read_cgroup(Server *s, ClientContext *c, const char *u return r; } + (void) client_context_read_log_filter_patterns(c, t); + /* Let's shortcut this if the cgroup path didn't change */ if (streq_ptr(c->cgroup, t)) return 0; diff --git a/src/journal/journald-context.h b/src/journal/journald-context.h index 9bf74b2347..4a998ba42e 100644 --- a/src/journal/journald-context.h +++ b/src/journal/journald-context.h @@ -7,6 +7,7 @@ #include "sd-id128.h" +#include "set.h" #include "time-util.h" typedef struct ClientContext ClientContext; @@ -55,6 +56,9 @@ struct ClientContext { usec_t log_ratelimit_interval; unsigned log_ratelimit_burst; + + Set *log_filter_allowed_patterns; + Set *log_filter_denied_patterns; }; int client_context_get( diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c index 847f69c1ff..ca23508454 100644 --- a/src/journal/journald-native.c +++ b/src/journal/journald-native.c @@ -13,6 +13,7 @@ #include "journal-importer.h" #include "journal-internal.h" #include "journal-util.h" +#include "journald-client.h" #include "journald-console.h" #include "journald-kmsg.h" #include "journald-native.h" @@ -261,6 +262,13 @@ static int server_process_entry( goto finish; if (message) { + /* Ensure message is not NULL, otherwise strlen(message) would crash. This check needs to + * be here until server_process_entry() is able to process messages containing \0 characters, + * as we would have access to the actual size of message. */ + r = client_context_check_keep_log(context, message, strlen(message)); + if (r <= 0) + goto finish; + if (s->forward_to_syslog) server_forward_syslog(s, syslog_fixup_facility(priority), identifier, message, ucred, tv); diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index 49f28972ea..4446b2daec 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -20,6 +20,7 @@ #include "fs-util.h" #include "io-util.h" #include "journal-internal.h" +#include "journald-client.h" #include "journald-console.h" #include "journald-context.h" #include "journald-kmsg.h" @@ -284,6 +285,10 @@ static int stdout_stream_log( if (isempty(p)) return 0; + r = client_context_check_keep_log(s->context, p, strlen(p)); + if (r <= 0) + return r; + if (s->forward_to_syslog || s->server->forward_to_syslog) server_forward_syslog(s->server, syslog_fixup_facility(priority), s->identifier, p, &s->ucred, NULL); diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c index d8708b0775..1ecbd226da 100644 --- a/src/journal/journald-syslog.c +++ b/src/journal/journald-syslog.c @@ -11,6 +11,7 @@ #include "format-util.h" #include "io-util.h" #include "journal-internal.h" +#include "journald-client.h" #include "journald-console.h" #include "journald-kmsg.h" #include "journald-server.h" @@ -374,6 +375,9 @@ void server_process_syslog_message( if (!client_context_test_priority(context, priority)) return; + if (client_context_check_keep_log(context, msg, strlen(msg)) <= 0) + return; + syslog_ts = msg; syslog_ts_len = syslog_skip_timestamp(&msg); if (syslog_ts_len == 0) diff --git a/src/journal/meson.build b/src/journal/meson.build index 1e41ea149b..9abab298cc 100644 --- a/src/journal/meson.build +++ b/src/journal/meson.build @@ -3,6 +3,8 @@ sources = files( 'journald-audit.c', 'journald-audit.h', + 'journald-client.c', + 'journald-client.h', 'journald-console.c', 'journald-console.h', 'journald-context.c', |