From: Ryan Wilson Date: Mon, 2 Dec 2024 16:10:05 +0000 (-0800) Subject: core: Add ProtectHostname=private X-Git-Tag: v258-rc1~1924^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=cf48bde7aea52b18ac3fa218d3f60fd3d533ef66;p=thirdparty%2Fsystemd.git core: Add ProtectHostname=private This allows an option for systemd exec units to enable UTS namespaces but not restrict changing hostname via seccomp. Thus, units can change hostname without affecting the host. Fixes: #30348 --- diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 14075cb4e7d..44ee2022dd2 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -2055,8 +2055,11 @@ BindReadOnlyPaths=/var/lib/systemd ProtectHostname= - Takes a boolean argument. When set, sets up a new UTS namespace for the executed - processes. In addition, changing hostname or domainname is prevented. Defaults to off. + Takes a boolean argument or private. If enabled, sets up a new UTS namespace + for the executed processes. If set to a true value, changing hostname or domainname via + sethostname() and setdomainname() system calls is prevented. If set to + private, changing hostname or domainname is allowed but only affects the unit's UTS namespace. + Defaults to off. Note that the implementation of this setting might be impossible (for example if UTS namespaces are not available), and the unit should be written in a way that does not solely rely on this setting @@ -2066,6 +2069,12 @@ BindReadOnlyPaths=/var/lib/systemd the system into the service, it is hence not suitable for services that need to take notice of system hostname changes dynamically. + Note that this option does not prevent changing system hostname via hostnamectl. + However, User= and Group= may be used to run as an unprivileged user + to disallow changing system hostname. See SetHostname() in + org.freedesktop.hostname15 + for more details. + diff --git a/mkosi.conf b/mkosi.conf index 35a19a27aad..535e2bd79bf 100644 --- a/mkosi.conf +++ b/mkosi.conf @@ -101,6 +101,7 @@ Packages= gdb grep gzip + hostname jq kbd kexec-tools diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c index f4aacb55b22..fd306f11431 100644 --- a/src/core/exec-invoke.c +++ b/src/core/exec-invoke.c @@ -1726,15 +1726,17 @@ static int apply_protect_hostname(const ExecContext *c, const ExecParameters *p, "support UTS namespaces, ignoring namespace setup."); #if HAVE_SECCOMP - int r; + if (c->protect_hostname == PROTECT_HOSTNAME_YES) { + int r; - if (skip_seccomp_unavailable(c, p, "ProtectHostname=")) - return 0; + if (skip_seccomp_unavailable(c, p, "ProtectHostname=")) + return 0; - r = seccomp_protect_hostname(); - if (r < 0) { - *ret_exit_status = EXIT_SECCOMP; - return log_exec_error_errno(c, p, r, "Failed to apply hostname restrictions: %m"); + r = seccomp_protect_hostname(); + if (r < 0) { + *ret_exit_status = EXIT_SECCOMP; + return log_exec_error_errno(c, p, r, "Failed to apply hostname restrictions: %m"); + } } #endif @@ -3417,6 +3419,9 @@ static int apply_mount_namespace( .protect_kernel_tunables = needs_sandboxing && context->protect_kernel_tunables, .protect_kernel_modules = needs_sandboxing && context->protect_kernel_modules, .protect_kernel_logs = needs_sandboxing && context->protect_kernel_logs, + /* Only mount /proc/sys/kernel/hostname and domainname read-only if ProtectHostname=yes. Otherwise, ProtectHostname=no + * allows changing hostname for the host and ProtectHostname=private allows changing the hostname in the unit's UTS + * namespace. */ .protect_hostname = needs_sandboxing && context->protect_hostname == PROTECT_HOSTNAME_YES, .private_dev = needs_sandboxing && context->private_devices, diff --git a/src/core/namespace.c b/src/core/namespace.c index c327c9a3ca4..2f3b8f03d13 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -3308,6 +3308,7 @@ DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(protect_home, ProtectHome, PROTECT_HOME_ static const char *const protect_hostname_table[_PROTECT_HOSTNAME_MAX] = { [PROTECT_HOSTNAME_NO] = "no", [PROTECT_HOSTNAME_YES] = "yes", + [PROTECT_HOSTNAME_PRIVATE] = "private", }; DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(protect_hostname, ProtectHostname, PROTECT_HOSTNAME_YES); diff --git a/src/core/namespace.h b/src/core/namespace.h index 8df91e3bdf9..96f62be30a2 100644 --- a/src/core/namespace.h +++ b/src/core/namespace.h @@ -31,6 +31,7 @@ typedef enum ProtectHome { typedef enum ProtectHostname { PROTECT_HOSTNAME_NO, PROTECT_HOSTNAME_YES, + PROTECT_HOSTNAME_PRIVATE, _PROTECT_HOSTNAME_MAX, _PROTECT_HOSTNAME_INVALID = -EINVAL, } ProtectHostname; diff --git a/test/units/TEST-07-PID1.protect-hostname.sh b/test/units/TEST-07-PID1.protect-hostname.sh new file mode 100755 index 00000000000..c2ede395535 --- /dev/null +++ b/test/units/TEST-07-PID1.protect-hostname.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2016 +set -eux +set -o pipefail + +# shellcheck source=test/units/test-control.sh +. "$(dirname "$0")"/test-control.sh +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +LEGACY_HOSTNAME="$(hostname)" +HOSTNAME_FROM_SYSTEMD="$(hostnamectl hostname)" + +testcase_yes() { + # hostnamectl calls SetHostname method via dbus socket which executes in homenamed + # in the init namespace. So hostnamectl is not affected by ProtectHostname=yes or + # private since sethostname() system call is executed in the init namespace. + # + # hostnamed does authentication based on UID via polkit so this guarantees admins + # can only set hostname. + (! systemd-run --wait -p ProtectHostname=yes hostname foo) + + systemd-run --wait -p ProtectHostname=yes -p PrivateMounts=yes \ + findmnt --mountpoint /proc/sys/kernel/hostname +} + +testcase_private() { + systemd-run --wait -p ProtectHostnameEx=private \ + -P bash -xec ' + hostname foo + test "$(hostname)" = "foo" + ' + + # Verify host hostname is unchanged. + test "$(hostname)" = "$LEGACY_HOSTNAME" + test "$(hostnamectl hostname)" = "$HOSTNAME_FROM_SYSTEMD" + + # Verify /proc/sys/kernel/hostname is not bind mounted from host read-only. + (! systemd-run --wait -p ProtectHostnameEx=private -p PrivateMounts=yes \ + findmnt --mountpoint /proc/sys/kernel/hostname) +} + +run_testcases