sysctl: add --strict option to fail if sysctl does not exists

systemd-sysctl currently fails silently under any of these conditions:
- Missing permission to write a sysctl.
- Invalid sysctl (path doesn't exists).
- Ignore failure flag ('-' in front of the sysctl name).

Because of this behaviour, configuration issues can go unnoticed as
there is no way to detect those unless going through the logs.

--strict option forces systemd-sysctl to fail if a sysctl is invalid or
if permission are insufficient. Errors on sysctl marked as "ignore
failure" will still be ignored.
This commit is contained in:
Quentin Deslandes 2022-07-22 17:18:15 +02:00 committed by Daan De Meyer
parent 3657d3a01c
commit e88748c17e
6 changed files with 64 additions and 6 deletions

View file

@ -64,6 +64,14 @@
<para>Only apply rules with the specified prefix.</para>
</listitem>
</varlistentry>
<varlistentry id='strict'>
<term><option>--strict=</option></term>
<listitem>
<para>Always return non-zero exit code on failure (including invalid sysctl variable
name and insufficient permissions), unless the sysctl variable name is prefixed with a "-"
character.</para>
</listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="cat-config" />
<xi:include href="standard-options.xml" xpointer="no-pager" />

View file

@ -28,6 +28,7 @@
static char **arg_prefixes = NULL;
static bool arg_cat_config = false;
static bool arg_strict = false;
static PagerFlags arg_pager_flags = 0;
STATIC_DESTRUCTOR_REGISTER(arg_prefixes, strv_freep);
@ -101,13 +102,16 @@ static int sysctl_write_or_warn(const char *key, const char *value, bool ignore_
r = sysctl_write(key, value);
if (r < 0) {
/* If the sysctl is not available in the kernel or we are running with reduced privileges and
* cannot write it, then log about the issue, and proceed without failing. (EROFS is treated
* as a permission problem here, since that's how container managers usually protected their
* sysctls.) In all other cases log an error and make the tool fail. */
if (ignore_failure || r == -EROFS || ERRNO_IS_PRIVILEGE(r))
/* Proceed without failing if ignore_failure is true.
* If the sysctl is not available in the kernel or we are running with reduced privileges and
* cannot write it, then log about the issue, and proceed without failing. Unless strict mode
* (arg_strict = true) is enabled, in which case we should fail. (EROFS is treated as a
* permission problem here, since that's how container managers usually protected their
* sysctls.)
* In all other cases log an error and make the tool fail. */
if (ignore_failure || (!arg_strict && (r == -EROFS || ERRNO_IS_PRIVILEGE(r))))
log_debug_errno(r, "Couldn't write '%s' to '%s', ignoring: %m", value, key);
else if (r == -ENOENT)
else if (!arg_strict && r == -ENOENT)
log_warning_errno(r, "Couldn't write '%s' to '%s', ignoring: %m", value, key);
else
return log_error_errno(r, "Couldn't write '%s' to '%s': %m", value, key);
@ -326,6 +330,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_CAT_CONFIG,
ARG_PREFIX,
ARG_NO_PAGER,
ARG_STRICT,
};
static const struct option options[] = {
@ -334,6 +339,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "cat-config", no_argument, NULL, ARG_CAT_CONFIG },
{ "prefix", required_argument, NULL, ARG_PREFIX },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "strict", no_argument, NULL, ARG_STRICT },
{}
};
@ -382,6 +388,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_STRICT:
arg_strict = true;
break;
case '?':
return -EINVAL;

View file

@ -0,0 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
all setup run clean clean-again:
@TEST_BASE_DIR=../ ./test.sh --$@
.PHONY: all setup run clean clean-again

10
test/TEST-76-SYSCTL/test.sh Executable file
View file

@ -0,0 +1,10 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="Sysctl-related tests"
# shellcheck source=test/test-functions
. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@"

View file

@ -0,0 +1,8 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-76-SYSCTL
[Service]
ExecStartPre=rm -f /failed /testok
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
Type=oneshot

16
test/units/testsuite-76.sh Executable file
View file

@ -0,0 +1,16 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
export SYSTEMD_LOG_LEVEL=debug
echo "foo.bar=42" > /etc/sysctl.d/foo.conf
[[ $(/usr/lib/systemd/systemd-sysctl /etc/sysctl.d/foo.conf)$? -eq 0 ]]
[[ $(/usr/lib/systemd/systemd-sysctl --strict /etc/sysctl.d/foo.conf)$? -ne 0 ]]
echo "-foo.foo=42" > /etc/sysctl.d/foo.conf
[[ $(/usr/lib/systemd/systemd-sysctl /etc/sysctl.d/foo.conf)$? -eq 0 ]]
[[ $(/usr/lib/systemd/systemd-sysctl --strict /etc/sysctl.d/foo.conf)$? -eq 0 ]]
touch /testok