mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-08 21:17:47 +03:00
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).
This commit is contained in:
parent
b8c0565ec4
commit
87a13dabbd
110
src/journal/journald-client.c
Normal file
110
src/journal/journald-client.c
Normal file
@ -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);
|
||||
}
|
7
src/journal/journald-client.h
Normal file
7
src/journal/journald-client.h
Normal file
@ -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);
|
@ -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;
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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',
|
||||
|
Loading…
Reference in New Issue
Block a user