1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-11 20:58:27 +03:00

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
This commit is contained in:
Ryan Wilson 2024-12-02 08:10:05 -08:00
parent 6746f28854
commit cf48bde7ae
6 changed files with 70 additions and 9 deletions

View File

@ -2055,8 +2055,11 @@ BindReadOnlyPaths=/var/lib/systemd</programlisting>
<varlistentry>
<term><varname>ProtectHostname=</varname></term>
<listitem><para>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.</para>
<listitem><para>Takes a boolean argument or <literal>private</literal>. If enabled, sets up a new UTS namespace
for the executed processes. If set to a true value, changing hostname or domainname via
<function>sethostname()</function> and <function>setdomainname()</function> system calls is prevented. If set to
<literal>private</literal>, changing hostname or domainname is allowed but only affects the unit's UTS namespace.
Defaults to off.</para>
<para>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</programlisting>
the system into the service, it is hence not suitable for services that need to take notice of system
hostname changes dynamically.</para>
<para>Note that this option does not prevent changing system hostname via <command>hostnamectl</command>.
However, <varname>User=</varname> and <varname>Group=</varname> may be used to run as an unprivileged user
to disallow changing system hostname. See <function>SetHostname()</function> in
<citerefentry project="man-pages"><refentrytitle>org.freedesktop.hostname1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for more details.</para>
<xi:include href="system-or-user-ns.xml" xpointer="singular"/>
<xi:include href="version-info.xml" xpointer="v242"/></listitem>

View File

@ -101,6 +101,7 @@ Packages=
gdb
grep
gzip
hostname
jq
kbd
kexec-tools

View File

@ -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,

View File

@ -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);

View File

@ -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;

View File

@ -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