Ryan Wilson 61c859
From 186eb0d3dc17b700a7709ebb23012ed9e3e41d6a Mon Sep 17 00:00:00 2001
Ryan Wilson 61c859
From: Ryan Wilson <ryantimwilson@meta.com>
Ryan Wilson 61c859
Date: Mon, 2 Dec 2024 07:38:06 -0800
Ryan Wilson 61c859
Subject: [PATCH 1/2] core: Migrate ProtectHostname to use enum vs boolean
Ryan Wilson 61c859
Ryan Wilson 61c859
Migrating ProtectHostname to enum will set the stage for adding more
Ryan Wilson 61c859
properties like ProtectHostname=private in future commits.
Ryan Wilson 61c859
Ryan Wilson 61c859
In addition, we add PrivateHostnameEx property to dbus API which uses
Ryan Wilson 61c859
string instead of boolean.
Ryan Wilson 61c859
---
Ryan Wilson 61c859
 man/org.freedesktop.systemd1.xml      | 34 +++++++++++----
Ryan Wilson 61c859
 src/core/dbus-execute.c               | 59 +++++++++++++++++++++++++--
Ryan Wilson 61c859
 src/core/exec-invoke.c                |  8 ++--
Ryan Wilson 61c859
 src/core/execute-serialize.c          |  9 ++--
Ryan Wilson 61c859
 src/core/execute.c                    |  2 +-
Ryan Wilson 61c859
 src/core/execute.h                    |  2 +-
Ryan Wilson 61c859
 src/core/load-fragment-gperf.gperf.in |  2 +-
Ryan Wilson 61c859
 src/core/load-fragment.c              |  1 +
Ryan Wilson 61c859
 src/core/load-fragment.h              |  1 +
Ryan Wilson 61c859
 src/core/namespace.c                  | 13 ++++--
Ryan Wilson 61c859
 src/core/namespace.h                  | 10 +++++
Ryan Wilson 61c859
 src/shared/bus-unit-util.c            |  1 +
Ryan Wilson 61c859
 12 files changed, 115 insertions(+), 27 deletions(-)
Ryan Wilson 61c859
Ryan Wilson 61c859
diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
Ryan Wilson 61c859
index 9cd6a69311a97..d196f4767cea2 100644
Ryan Wilson 61c859
--- a/man/org.freedesktop.systemd1.xml
Ryan Wilson 61c859
+++ b/man/org.freedesktop.systemd1.xml
Ryan Wilson 61c859
@@ -3359,6 +3359,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
Ryan Wilson 61c859
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
Ryan Wilson 61c859
       readonly b ProtectHostname = ...;
Ryan Wilson 61c859
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
Ryan Wilson 61c859
+      readonly s ProtectHostnameEx = '...';
Ryan Wilson 61c859
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
Ryan Wilson 61c859
       readonly b MemoryKSM = ...;
Ryan Wilson 61c859
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
Ryan Wilson 61c859
       readonly s NetworkNamespacePath = '...';
Ryan Wilson 61c859
@@ -3958,8 +3960,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
Ryan Wilson 61c859
 
Ryan Wilson 61c859
     
Ryan Wilson 61c859
 
Ryan Wilson 61c859
-    
Ryan Wilson 61c859
-
Ryan Wilson 61c859
     
Ryan Wilson 61c859
 
Ryan Wilson 61c859
     
Ryan Wilson 61c859
@@ -4682,6 +4682,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
Ryan Wilson 61c859
 
Ryan Wilson 61c859
     <variablelist class="dbus-property" generated="True" extra-ref="ProtectHostname"/>
Ryan Wilson 61c859
 
Ryan Wilson 61c859
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectHostnameEx"/>
Ryan Wilson 61c859
+
Ryan Wilson 61c859
     <variablelist class="dbus-property" generated="True" extra-ref="MemoryKSM"/>
Ryan Wilson 61c859
 
Ryan Wilson 61c859
     <variablelist class="dbus-property" generated="True" extra-ref="NetworkNamespacePath"/>
Ryan Wilson 61c859
@@ -4879,6 +4881,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
Ryan Wilson 61c859
       unit file setting <varname>PrivatePIDs=</varname> listed in
Ryan Wilson 61c859
       <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
Ryan Wilson 61c859
       Note <varname>PrivatePIDs</varname> is a string type to allow adding more values in the future.</para>
Ryan Wilson 61c859
+
Ryan Wilson 61c859
+      <para><varname>ProtectHostnameEx</varname> implement the destination parameter of the
Ryan Wilson 61c859
+      unit file setting <varname>ProtectHostname=</varname> listed in
Ryan Wilson 61c859
+      <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
Ryan Wilson 61c859
+      Unlike boolean <varname>ProtectHostname</varname>, <varname>ProtectHostnameEx</varname>
Ryan Wilson 61c859
+      is a string type.</para>
Ryan Wilson 61c859
     </refsect2>
Ryan Wilson 61c859
   </refsect1>
Ryan Wilson 61c859
 
Ryan Wilson 61c859
@@ -5544,6 +5552,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
Ryan Wilson 61c859
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
Ryan Wilson 61c859
       readonly b ProtectHostname = ...;
Ryan Wilson 61c859
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
Ryan Wilson 61c859
+      readonly s ProtectHostnameEx = '...';
Ryan Wilson 61c859
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
Ryan Wilson 61c859
       readonly b MemoryKSM = ...;
Ryan Wilson 61c859
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
Ryan Wilson 61c859
       readonly s NetworkNamespacePath = '...';
Ryan Wilson 61c859
@@ -6155,8 +6165,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
Ryan Wilson 61c859
 
Ryan Wilson 61c859
     
Ryan Wilson 61c859
 
Ryan Wilson 61c859
-    
Ryan Wilson 61c859
-
Ryan Wilson 61c859
     
Ryan Wilson 61c859
 
Ryan Wilson 61c859
     
Ryan Wilson 61c859
@@ -6851,6 +6859,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
Ryan Wilson 61c859
 
Ryan Wilson 61c859
     <variablelist class="dbus-property" generated="True" extra-ref="ProtectHostname"/>
Ryan Wilson 61c859
 
Ryan Wilson 61c859
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectHostnameEx"/>
Ryan Wilson 61c859
+
Ryan Wilson 61c859
     <variablelist class="dbus-property" generated="True" extra-ref="MemoryKSM"/>
Ryan Wilson 61c859
 
Ryan Wilson 61c859
     <variablelist class="dbus-property" generated="True" extra-ref="NetworkNamespacePath"/>
Ryan Wilson 61c859
@@ -7551,6 +7561,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
Ryan Wilson 61c859
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
Ryan Wilson 61c859
       readonly b ProtectHostname = ...;
Ryan Wilson 61c859
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
Ryan Wilson 61c859
+      readonly s ProtectHostnameEx = '...';
Ryan Wilson 61c859
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
Ryan Wilson 61c859
       readonly b MemoryKSM = ...;
Ryan Wilson 61c859
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
Ryan Wilson 61c859
       readonly s NetworkNamespacePath = '...';
Ryan Wilson 61c859
@@ -8088,8 +8100,6 @@ node /org/freedesktop/systemd1/unit/home_2emount {
Ryan Wilson 61c859
 
Ryan Wilson 61c859
     
Ryan Wilson 61c859
 
Ryan Wilson 61c859
-    
Ryan Wilson 61c859
-
Ryan Wilson 61c859
     
Ryan Wilson 61c859
 
Ryan Wilson 61c859
     
Ryan Wilson 61c859
@@ -8696,6 +8706,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
Ryan Wilson 61c859
 
Ryan Wilson 61c859
     <variablelist class="dbus-property" generated="True" extra-ref="ProtectHostname"/>
Ryan Wilson 61c859
 
Ryan Wilson 61c859
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectHostnameEx"/>
Ryan Wilson 61c859
+
Ryan Wilson 61c859
     <variablelist class="dbus-property" generated="True" extra-ref="MemoryKSM"/>
Ryan Wilson 61c859
 
Ryan Wilson 61c859
     <variablelist class="dbus-property" generated="True" extra-ref="NetworkNamespacePath"/>
Ryan Wilson 61c859
@@ -9525,6 +9537,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
Ryan Wilson 61c859
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
Ryan Wilson 61c859
       readonly b ProtectHostname = ...;
Ryan Wilson 61c859
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
Ryan Wilson 61c859
+      readonly s ProtectHostnameEx = '...';
Ryan Wilson 61c859
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
Ryan Wilson 61c859
       readonly b MemoryKSM = ...;
Ryan Wilson 61c859
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
Ryan Wilson 61c859
       readonly s NetworkNamespacePath = '...';
Ryan Wilson 61c859
@@ -10048,8 +10062,6 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
Ryan Wilson 61c859
 
Ryan Wilson 61c859
     
Ryan Wilson 61c859
 
Ryan Wilson 61c859
-    
Ryan Wilson 61c859
-
Ryan Wilson 61c859
     
Ryan Wilson 61c859
 
Ryan Wilson 61c859
     
Ryan Wilson 61c859
@@ -10642,6 +10654,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
Ryan Wilson 61c859
 
Ryan Wilson 61c859
     <variablelist class="dbus-property" generated="True" extra-ref="ProtectHostname"/>
Ryan Wilson 61c859
 
Ryan Wilson 61c859
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectHostnameEx"/>
Ryan Wilson 61c859
+
Ryan Wilson 61c859
     <variablelist class="dbus-property" generated="True" extra-ref="MemoryKSM"/>
Ryan Wilson 61c859
 
Ryan Wilson 61c859
     <variablelist class="dbus-property" generated="True" extra-ref="NetworkNamespacePath"/>
Ryan Wilson 61c859
@@ -12305,6 +12319,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
Ryan Wilson 61c859
       <varname>ProtectControlGroupsEx</varname>,
Ryan Wilson 61c859
       <varname>PrivateUsersEx</varname>, and
Ryan Wilson 61c859
       <varname>PrivatePIDs</varname> were added in version 257.</para>
Ryan Wilson 61c859
+      <para><varname>ProtectHostnameEx</varname> was added in version 258.</para>
Ryan Wilson 61c859
     </refsect2>
Ryan Wilson 61c859
     <refsect2>
Ryan Wilson 61c859
       <title>Socket Unit Objects</title>
Ryan Wilson 61c859
@@ -12348,6 +12363,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
Ryan Wilson 61c859
       <varname>ManagedOOMMemoryPressureDurationUSec</varname>,
Ryan Wilson 61c859
       <varname>ProtectControlGroupsEx</varname>, and
Ryan Wilson 61c859
       <varname>PrivatePIDs</varname> were added in version 257.</para>
Ryan Wilson 61c859
+      <para><varname>ProtectHostnameEx</varname> was added in version 258.</para>
Ryan Wilson 61c859
     </refsect2>
Ryan Wilson 61c859
     <refsect2>
Ryan Wilson 61c859
       <title>Mount Unit Objects</title>
Ryan Wilson 61c859
@@ -12388,6 +12404,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
Ryan Wilson 61c859
       <varname>ManagedOOMMemoryPressureDurationUSec</varname>,
Ryan Wilson 61c859
       <varname>ProtectControlGroupsEx</varname>, and
Ryan Wilson 61c859
       <varname>PrivatePIDs</varname> were added in version 257.</para>
Ryan Wilson 61c859
+      <para><varname>ProtectHostnameEx</varname> was added in version 258.</para>
Ryan Wilson 61c859
     </refsect2>
Ryan Wilson 61c859
     <refsect2>
Ryan Wilson 61c859
       <title>Swap Unit Objects</title>
Ryan Wilson 61c859
@@ -12428,6 +12445,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
Ryan Wilson 61c859
       <varname>ManagedOOMMemoryPressureDurationUSec</varname>,
Ryan Wilson 61c859
       <varname>ProtectControlGroupsEx</varname>, and
Ryan Wilson 61c859
       <varname>PrivatePIDs</varname> were added in version 257.</para>
Ryan Wilson 61c859
+      <para><varname>ProtectHostnameEx</varname> was added in version 258.</para>
Ryan Wilson 61c859
     </refsect2>
Ryan Wilson 61c859
     <refsect2>
Ryan Wilson 61c859
       <title>Slice Unit Objects</title>
Ryan Wilson 61c859
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
Ryan Wilson 61c859
index e297323f1d3e7..bfd6694683cf1 100644
Ryan Wilson 61c859
--- a/src/core/dbus-execute.c
Ryan Wilson 61c859
+++ b/src/core/dbus-execute.c
Ryan Wilson 61c859
@@ -64,6 +64,7 @@ static BUS_DEFINE_PROPERTY_GET_REF(property_get_private_tmp_ex, "s", PrivateTmp,
Ryan Wilson 61c859
 static BUS_DEFINE_PROPERTY_GET_REF(property_get_private_users_ex, "s", PrivateUsers, private_users_to_string);
Ryan Wilson 61c859
 static BUS_DEFINE_PROPERTY_GET_REF(property_get_protect_control_groups_ex, "s", ProtectControlGroups, protect_control_groups_to_string);
Ryan Wilson 61c859
 static BUS_DEFINE_PROPERTY_GET_REF(property_get_private_pids, "s", PrivatePIDs, private_pids_to_string);
Ryan Wilson 61c859
+static BUS_DEFINE_PROPERTY_GET_REF(property_get_protect_hostname_ex, "s", ProtectHostname, protect_hostname_to_string);
Ryan Wilson 61c859
 static BUS_DEFINE_PROPERTY_GET_REF(property_get_syslog_level, "i", int, LOG_PRI);
Ryan Wilson 61c859
 static BUS_DEFINE_PROPERTY_GET_REF(property_get_syslog_facility, "i", int, LOG_FAC);
Ryan Wilson 61c859
 static BUS_DEFINE_PROPERTY_GET(property_get_cpu_affinity_from_numa, "b", ExecContext, exec_context_get_cpu_affinity_from_numa);
Ryan Wilson 61c859
@@ -1068,6 +1069,21 @@ static int property_get_protect_control_groups(
Ryan Wilson 61c859
         return sd_bus_message_append_basic(reply, 'b', &b);
Ryan Wilson 61c859
 }
Ryan Wilson 61c859
 
Ryan Wilson 61c859
+static int property_get_protect_hostname(
Ryan Wilson 61c859
+                sd_bus *bus,
Ryan Wilson 61c859
+                const char *path,
Ryan Wilson 61c859
+                const char *interface,
Ryan Wilson 61c859
+                const char *property,
Ryan Wilson 61c859
+                sd_bus_message *reply,
Ryan Wilson 61c859
+                void *userdata,
Ryan Wilson 61c859
+                sd_bus_error *error) {
Ryan Wilson 61c859
+
Ryan Wilson 61c859
+        ProtectHostname *p = ASSERT_PTR(userdata);
Ryan Wilson 61c859
+        int b = *p != PROTECT_HOSTNAME_NO;
Ryan Wilson 61c859
+
Ryan Wilson 61c859
+        return sd_bus_message_append_basic(reply, 'b', &b);
Ryan Wilson 61c859
+}
Ryan Wilson 61c859
+
Ryan Wilson 61c859
 const sd_bus_vtable bus_exec_vtable[] = {
Ryan Wilson 61c859
         SD_BUS_VTABLE_START(0),
Ryan Wilson 61c859
         SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST),
Ryan Wilson 61c859
@@ -1242,7 +1258,8 @@ const sd_bus_vtable bus_exec_vtable[] = {
Ryan Wilson 61c859
         SD_BUS_PROPERTY("KeyringMode", "s", property_get_exec_keyring_mode, offsetof(ExecContext, keyring_mode), SD_BUS_VTABLE_PROPERTY_CONST),
Ryan Wilson 61c859
         SD_BUS_PROPERTY("ProtectProc", "s", property_get_protect_proc, offsetof(ExecContext, protect_proc), SD_BUS_VTABLE_PROPERTY_CONST),
Ryan Wilson 61c859
         SD_BUS_PROPERTY("ProcSubset", "s", property_get_proc_subset, offsetof(ExecContext, proc_subset), SD_BUS_VTABLE_PROPERTY_CONST),
Ryan Wilson 61c859
-        SD_BUS_PROPERTY("ProtectHostname", "b", bus_property_get_bool, offsetof(ExecContext, protect_hostname), SD_BUS_VTABLE_PROPERTY_CONST),
Ryan Wilson 61c859
+        SD_BUS_PROPERTY("ProtectHostname", "b", property_get_protect_hostname, offsetof(ExecContext, protect_hostname), SD_BUS_VTABLE_PROPERTY_CONST),
Ryan Wilson 61c859
+        SD_BUS_PROPERTY("ProtectHostnameEx", "s", property_get_protect_hostname_ex, offsetof(ExecContext, protect_hostname), SD_BUS_VTABLE_PROPERTY_CONST),
Ryan Wilson 61c859
         SD_BUS_PROPERTY("MemoryKSM", "b", bus_property_get_tristate, offsetof(ExecContext, memory_ksm), SD_BUS_VTABLE_PROPERTY_CONST),
Ryan Wilson 61c859
         SD_BUS_PROPERTY("NetworkNamespacePath", "s", NULL, offsetof(ExecContext, network_namespace_path), SD_BUS_VTABLE_PROPERTY_CONST),
Ryan Wilson 61c859
         SD_BUS_PROPERTY("IPCNamespacePath", "s", NULL, offsetof(ExecContext, ipc_namespace_path), SD_BUS_VTABLE_PROPERTY_CONST),
Ryan Wilson 61c859
@@ -1993,6 +2010,43 @@ int bus_exec_context_set_transient_property(
Ryan Wilson 61c859
                 return 1;
Ryan Wilson 61c859
         }
Ryan Wilson 61c859
 
Ryan Wilson 61c859
+        if (streq(name, "ProtectHostname")) {
Ryan Wilson 61c859
+                int v;
Ryan Wilson 61c859
+
Ryan Wilson 61c859
+                r = sd_bus_message_read(message, "b", &v);
Ryan Wilson 61c859
+                if (r < 0)
Ryan Wilson 61c859
+                        return r;
Ryan Wilson 61c859
+
Ryan Wilson 61c859
+                if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
Ryan Wilson 61c859
+                        c->protect_hostname = v ? PROTECT_HOSTNAME_YES : PROTECT_HOSTNAME_NO;
Ryan Wilson 61c859
+                        (void) unit_write_settingf(u, flags, name, "%s=%s", name, yes_no(v));
Ryan Wilson 61c859
+                }
Ryan Wilson 61c859
+
Ryan Wilson 61c859
+                return 1;
Ryan Wilson 61c859
+
Ryan Wilson 61c859
+        }
Ryan Wilson 61c859
+
Ryan Wilson 61c859
+        if (streq(name, "ProtectHostnameEx")) {
Ryan Wilson 61c859
+                const char *s;
Ryan Wilson 61c859
+                ProtectHostname t;
Ryan Wilson 61c859
+
Ryan Wilson 61c859
+                r = sd_bus_message_read(message, "s", &s);
Ryan Wilson 61c859
+                if (r < 0)
Ryan Wilson 61c859
+                        return r;
Ryan Wilson 61c859
+
Ryan Wilson 61c859
+                t = protect_hostname_from_string(s);
Ryan Wilson 61c859
+                if (t < 0)
Ryan Wilson 61c859
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s setting: %s", name, s);
Ryan Wilson 61c859
+
Ryan Wilson 61c859
+                if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
Ryan Wilson 61c859
+                        c->protect_hostname = t;
Ryan Wilson 61c859
+                        (void) unit_write_settingf(u, flags, name, "ProtectHostname=%s",
Ryan Wilson 61c859
+                                                   protect_hostname_to_string(c->protect_hostname));
Ryan Wilson 61c859
+                }
Ryan Wilson 61c859
+
Ryan Wilson 61c859
+                return 1;
Ryan Wilson 61c859
+        }
Ryan Wilson 61c859
+
Ryan Wilson 61c859
         if (streq(name, "PrivateDevices"))
Ryan Wilson 61c859
                 return bus_set_transient_bool(u, name, &c->private_devices, message, flags, error);
Ryan Wilson 61c859
 
Ryan Wilson 61c859
@@ -2053,9 +2107,6 @@ int bus_exec_context_set_transient_property(
Ryan Wilson 61c859
         if (streq(name, "LockPersonality"))
Ryan Wilson 61c859
                 return bus_set_transient_bool(u, name, &c->lock_personality, message, flags, error);
Ryan Wilson 61c859
 
Ryan Wilson 61c859
-        if (streq(name, "ProtectHostname"))
Ryan Wilson 61c859
-                return bus_set_transient_bool(u, name, &c->protect_hostname, message, flags, error);
Ryan Wilson 61c859
-
Ryan Wilson 61c859
         if (streq(name, "MemoryKSM"))
Ryan Wilson 61c859
                 return bus_set_transient_tristate(u, name, &c->memory_ksm, message, flags, error);
Ryan Wilson 61c859
 
Ryan Wilson 61c859
diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c
Ryan Wilson 61c859
index 9d636f552950d..f4aacb55b22bd 100644
Ryan Wilson 61c859
--- a/src/core/exec-invoke.c
Ryan Wilson 61c859
+++ b/src/core/exec-invoke.c
Ryan Wilson 61c859
@@ -1341,7 +1341,7 @@ static bool context_has_seccomp(const ExecContext *c) {
Ryan Wilson 61c859
                 c->memory_deny_write_execute ||
Ryan Wilson 61c859
                 c->private_devices ||
Ryan Wilson 61c859
                 c->protect_clock ||
Ryan Wilson 61c859
-                c->protect_hostname ||
Ryan Wilson 61c859
+                c->protect_hostname == PROTECT_HOSTNAME_YES ||
Ryan Wilson 61c859
                 c->protect_kernel_tunables ||
Ryan Wilson 61c859
                 c->protect_kernel_modules ||
Ryan Wilson 61c859
                 c->protect_kernel_logs ||
Ryan Wilson 61c859
@@ -1701,7 +1701,7 @@ static int apply_protect_hostname(const ExecContext *c, const ExecParameters *p,
Ryan Wilson 61c859
         assert(c);
Ryan Wilson 61c859
         assert(p);
Ryan Wilson 61c859
 
Ryan Wilson 61c859
-        if (!c->protect_hostname)
Ryan Wilson 61c859
+        if (c->protect_hostname == PROTECT_HOSTNAME_NO)
Ryan Wilson 61c859
                 return 0;
Ryan Wilson 61c859
 
Ryan Wilson 61c859
         if (ns_type_supported(NAMESPACE_UTS)) {
Ryan Wilson 61c859
@@ -3417,7 +3417,7 @@ static int apply_mount_namespace(
Ryan Wilson 61c859
                 .protect_kernel_tunables = needs_sandboxing && context->protect_kernel_tunables,
Ryan Wilson 61c859
                 .protect_kernel_modules = needs_sandboxing && context->protect_kernel_modules,
Ryan Wilson 61c859
                 .protect_kernel_logs = needs_sandboxing && context->protect_kernel_logs,
Ryan Wilson 61c859
-                .protect_hostname = needs_sandboxing && context->protect_hostname,
Ryan Wilson 61c859
+                .protect_hostname = needs_sandboxing && context->protect_hostname == PROTECT_HOSTNAME_YES,
Ryan Wilson 61c859
 
Ryan Wilson 61c859
                 .private_dev = needs_sandboxing && context->private_devices,
Ryan Wilson 61c859
                 .private_network = needs_sandboxing && exec_needs_network_namespace(context),
Ryan Wilson 61c859
@@ -4055,7 +4055,7 @@ static bool exec_context_need_unprivileged_private_users(
Ryan Wilson 61c859
                context->protect_kernel_logs ||
Ryan Wilson 61c859
                exec_needs_cgroup_mount(context, params) ||
Ryan Wilson 61c859
                context->protect_clock ||
Ryan Wilson 61c859
-               context->protect_hostname ||
Ryan Wilson 61c859
+               context->protect_hostname != PROTECT_HOSTNAME_NO ||
Ryan Wilson 61c859
                !strv_isempty(context->read_write_paths) ||
Ryan Wilson 61c859
                !strv_isempty(context->read_only_paths) ||
Ryan Wilson 61c859
                !strv_isempty(context->inaccessible_paths) ||
Ryan Wilson 61c859
diff --git a/src/core/execute-serialize.c b/src/core/execute-serialize.c
Ryan Wilson 61c859
index bf6592faedcd2..9dce5a9c2587e 100644
Ryan Wilson 61c859
--- a/src/core/execute-serialize.c
Ryan Wilson 61c859
+++ b/src/core/execute-serialize.c
Ryan Wilson 61c859
@@ -1978,7 +1978,7 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
Ryan Wilson 61c859
         if (r < 0)
Ryan Wilson 61c859
                 return r;
Ryan Wilson 61c859
 
Ryan Wilson 61c859
-        r = serialize_bool_elide(f, "exec-context-protect-hostname", c->protect_hostname);
Ryan Wilson 61c859
+        r = serialize_item(f, "exec-context-protect-hostname", protect_hostname_to_string(c->protect_hostname));
Ryan Wilson 61c859
         if (r < 0)
Ryan Wilson 61c859
                 return r;
Ryan Wilson 61c859
 
Ryan Wilson 61c859
@@ -2881,10 +2881,9 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
Ryan Wilson 61c859
                         if (c->keyring_mode < 0)
Ryan Wilson 61c859
                                 return -EINVAL;
Ryan Wilson 61c859
                 } else if ((val = startswith(l, "exec-context-protect-hostname="))) {
Ryan Wilson 61c859
-                        r = parse_boolean(val);
Ryan Wilson 61c859
-                        if (r < 0)
Ryan Wilson 61c859
-                                return r;
Ryan Wilson 61c859
-                        c->protect_hostname = r;
Ryan Wilson 61c859
+                        c->protect_hostname = protect_hostname_from_string(val);
Ryan Wilson 61c859
+                        if (c->protect_hostname < 0)
Ryan Wilson 61c859
+                                return -EINVAL;
Ryan Wilson 61c859
                 } else if ((val = startswith(l, "exec-context-protect-proc="))) {
Ryan Wilson 61c859
                         c->protect_proc = protect_proc_from_string(val);
Ryan Wilson 61c859
                         if (c->protect_proc < 0)
Ryan Wilson 61c859
diff --git a/src/core/execute.c b/src/core/execute.c
Ryan Wilson 61c859
index 3d55b0b772ece..40ab0ad1c53a9 100644
Ryan Wilson 61c859
--- a/src/core/execute.c
Ryan Wilson 61c859
+++ b/src/core/execute.c
Ryan Wilson 61c859
@@ -1071,7 +1071,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
Ryan Wilson 61c859
                 prefix, yes_no(c->restrict_realtime),
Ryan Wilson 61c859
                 prefix, yes_no(c->restrict_suid_sgid),
Ryan Wilson 61c859
                 prefix, exec_keyring_mode_to_string(c->keyring_mode),
Ryan Wilson 61c859
-                prefix, yes_no(c->protect_hostname),
Ryan Wilson 61c859
+                prefix, protect_hostname_to_string(c->protect_hostname),
Ryan Wilson 61c859
                 prefix, protect_proc_to_string(c->protect_proc),
Ryan Wilson 61c859
                 prefix, proc_subset_to_string(c->proc_subset));
Ryan Wilson 61c859
 
Ryan Wilson 61c859
diff --git a/src/core/execute.h b/src/core/execute.h
Ryan Wilson 61c859
index 32dabf177f44a..63a56a900cb8c 100644
Ryan Wilson 61c859
--- a/src/core/execute.h
Ryan Wilson 61c859
+++ b/src/core/execute.h
Ryan Wilson 61c859
@@ -336,7 +336,7 @@ struct ExecContext {
Ryan Wilson 61c859
         ProtectSystem protect_system;
Ryan Wilson 61c859
         ProtectHome protect_home;
Ryan Wilson 61c859
         PrivatePIDs private_pids;
Ryan Wilson 61c859
-        bool protect_hostname;
Ryan Wilson 61c859
+        ProtectHostname protect_hostname;
Ryan Wilson 61c859
 
Ryan Wilson 61c859
         bool dynamic_user;
Ryan Wilson 61c859
         bool remove_ipc;
Ryan Wilson 61c859
diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in
Ryan Wilson 61c859
index d7564b3767a06..fa12580ae1113 100644
Ryan Wilson 61c859
--- a/src/core/load-fragment-gperf.gperf.in
Ryan Wilson 61c859
+++ b/src/core/load-fragment-gperf.gperf.in
Ryan Wilson 61c859
@@ -180,7 +180,7 @@
Ryan Wilson 61c859
 {% else %}
Ryan Wilson 61c859
 {{type}}.SmackProcessLabel,                   config_parse_warn_compat,                           DISABLED_CONFIGURATION,             0
Ryan Wilson 61c859
 {% endif %}
Ryan Wilson 61c859
-{{type}}.ProtectHostname,                     config_parse_bool,                                  0,                                  offsetof({{type}}, exec_context.protect_hostname)
Ryan Wilson 61c859
+{{type}}.ProtectHostname,                     config_parse_protect_hostname,                      0,                                  offsetof({{type}}, exec_context.protect_hostname)
Ryan Wilson 61c859
 {{type}}.MemoryKSM,                           config_parse_tristate,                              0,                                  offsetof({{type}}, exec_context.memory_ksm)
Ryan Wilson 61c859
 {%- endmacro -%}
Ryan Wilson 61c859
 
Ryan Wilson 61c859
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
Ryan Wilson 61c859
index f34c930f4e4e0..a108216a96045 100644
Ryan Wilson 61c859
--- a/src/core/load-fragment.c
Ryan Wilson 61c859
+++ b/src/core/load-fragment.c
Ryan Wilson 61c859
@@ -141,6 +141,7 @@ DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMo
Ryan Wilson 61c859
 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode);
Ryan Wilson 61c859
 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess);
Ryan Wilson 61c859
 DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_home, protect_home, ProtectHome);
Ryan Wilson 61c859
+DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_hostname, protect_hostname, ProtectHostname);
Ryan Wilson 61c859
 DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_system, protect_system, ProtectSystem);
Ryan Wilson 61c859
 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_preserve_mode, exec_preserve_mode, ExecPreserveMode);
Ryan Wilson 61c859
 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType);
Ryan Wilson 61c859
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
Ryan Wilson 61c859
index 8ac962a94bd14..881ce152d550b 100644
Ryan Wilson 61c859
--- a/src/core/load-fragment.h
Ryan Wilson 61c859
+++ b/src/core/load-fragment.h
Ryan Wilson 61c859
@@ -119,6 +119,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_protect_control_groups);
Ryan Wilson 61c859
 CONFIG_PARSER_PROTOTYPE(config_parse_cpu_quota);
Ryan Wilson 61c859
 CONFIG_PARSER_PROTOTYPE(config_parse_allowed_cpuset);
Ryan Wilson 61c859
 CONFIG_PARSER_PROTOTYPE(config_parse_protect_home);
Ryan Wilson 61c859
+CONFIG_PARSER_PROTOTYPE(config_parse_protect_hostname);
Ryan Wilson 61c859
 CONFIG_PARSER_PROTOTYPE(config_parse_protect_system);
Ryan Wilson 61c859
 CONFIG_PARSER_PROTOTYPE(config_parse_bus_name);
Ryan Wilson 61c859
 CONFIG_PARSER_PROTOTYPE(config_parse_exec_utmp_mode);
Ryan Wilson 61c859
diff --git a/src/core/namespace.c b/src/core/namespace.c
Ryan Wilson 61c859
index 57dbbc4fc7dc5..c327c9a3ca488 100644
Ryan Wilson 61c859
--- a/src/core/namespace.c
Ryan Wilson 61c859
+++ b/src/core/namespace.c
Ryan Wilson 61c859
@@ -250,7 +250,7 @@ static const MountEntry protect_system_strict_table[] = {
Ryan Wilson 61c859
 };
Ryan Wilson 61c859
 
Ryan Wilson 61c859
 /* ProtectHostname=yes able */
Ryan Wilson 61c859
-static const MountEntry protect_hostname_table[] = {
Ryan Wilson 61c859
+static const MountEntry protect_hostname_yes_table[] = {
Ryan Wilson 61c859
         { "/proc/sys/kernel/hostname",   MOUNT_READ_ONLY, false },
Ryan Wilson 61c859
         { "/proc/sys/kernel/domainname", MOUNT_READ_ONLY, false },
Ryan Wilson 61c859
 };
Ryan Wilson 61c859
@@ -2642,8 +2642,8 @@ int setup_namespace(const NamespaceParameters *p, char **reterr_path) {
Ryan Wilson 61c859
         if (p->protect_hostname) {
Ryan Wilson 61c859
                 r = append_static_mounts(
Ryan Wilson 61c859
                                 &ml,
Ryan Wilson 61c859
-                                protect_hostname_table,
Ryan Wilson 61c859
-                                ELEMENTSOF(protect_hostname_table),
Ryan Wilson 61c859
+                                protect_hostname_yes_table,
Ryan Wilson 61c859
+                                ELEMENTSOF(protect_hostname_yes_table),
Ryan Wilson 61c859
                                 ignore_protect_proc);
Ryan Wilson 61c859
                 if (r < 0)
Ryan Wilson 61c859
                         return r;
Ryan Wilson 61c859
@@ -3305,6 +3305,13 @@ static const char *const protect_home_table[_PROTECT_HOME_MAX] = {
Ryan Wilson 61c859
 
Ryan Wilson 61c859
 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(protect_home, ProtectHome, PROTECT_HOME_YES);
Ryan Wilson 61c859
 
Ryan Wilson 61c859
+static const char *const protect_hostname_table[_PROTECT_HOSTNAME_MAX] = {
Ryan Wilson 61c859
+        [PROTECT_HOSTNAME_NO]      = "no",
Ryan Wilson 61c859
+        [PROTECT_HOSTNAME_YES]     = "yes",
Ryan Wilson 61c859
+};
Ryan Wilson 61c859
+
Ryan Wilson 61c859
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(protect_hostname, ProtectHostname, PROTECT_HOSTNAME_YES);
Ryan Wilson 61c859
+
Ryan Wilson 61c859
 static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = {
Ryan Wilson 61c859
         [PROTECT_SYSTEM_NO]     = "no",
Ryan Wilson 61c859
         [PROTECT_SYSTEM_YES]    = "yes",
Ryan Wilson 61c859
diff --git a/src/core/namespace.h b/src/core/namespace.h
Ryan Wilson 61c859
index bd48aa31da71c..8df91e3bdf906 100644
Ryan Wilson 61c859
--- a/src/core/namespace.h
Ryan Wilson 61c859
+++ b/src/core/namespace.h
Ryan Wilson 61c859
@@ -28,6 +28,13 @@ typedef enum ProtectHome {
Ryan Wilson 61c859
         _PROTECT_HOME_INVALID = -EINVAL,
Ryan Wilson 61c859
 } ProtectHome;
Ryan Wilson 61c859
 
Ryan Wilson 61c859
+typedef enum ProtectHostname {
Ryan Wilson 61c859
+        PROTECT_HOSTNAME_NO,
Ryan Wilson 61c859
+        PROTECT_HOSTNAME_YES,
Ryan Wilson 61c859
+        _PROTECT_HOSTNAME_MAX,
Ryan Wilson 61c859
+        _PROTECT_HOSTNAME_INVALID = -EINVAL,
Ryan Wilson 61c859
+} ProtectHostname;
Ryan Wilson 61c859
+
Ryan Wilson 61c859
 typedef enum ProtectSystem {
Ryan Wilson 61c859
         PROTECT_SYSTEM_NO,
Ryan Wilson 61c859
         PROTECT_SYSTEM_YES,
Ryan Wilson 61c859
@@ -215,6 +222,9 @@ int open_shareable_ns_path(int netns_storage_socket[static 2], const char *path,
Ryan Wilson 61c859
 const char* protect_home_to_string(ProtectHome p) _const_;
Ryan Wilson 61c859
 ProtectHome protect_home_from_string(const char *s) _pure_;
Ryan Wilson 61c859
 
Ryan Wilson 61c859
+const char* protect_hostname_to_string(ProtectHostname p) _const_;
Ryan Wilson 61c859
+ProtectHostname protect_hostname_from_string(const char *s) _pure_;
Ryan Wilson 61c859
+
Ryan Wilson 61c859
 const char* protect_system_to_string(ProtectSystem p) _const_;
Ryan Wilson 61c859
 ProtectSystem protect_system_from_string(const char *s) _pure_;
Ryan Wilson 61c859
 
Ryan Wilson 61c859
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
Ryan Wilson 61c859
index 06bfb90c8fa5d..4e623036d0353 100644
Ryan Wilson 61c859
--- a/src/shared/bus-unit-util.c
Ryan Wilson 61c859
+++ b/src/shared/bus-unit-util.c
Ryan Wilson 61c859
@@ -1045,6 +1045,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
Ryan Wilson 61c859
                               "SyslogIdentifier",
Ryan Wilson 61c859
                               "ProtectSystem",
Ryan Wilson 61c859
                               "ProtectHome",
Ryan Wilson 61c859
+                              "ProtectHostnameEx",
Ryan Wilson 61c859
                               "PrivateTmpEx",
Ryan Wilson 61c859
                               "PrivateUsersEx",
Ryan Wilson 61c859
                               "ProtectControlGroupsEx",
Ryan Wilson 61c859
Ryan Wilson 61c859
From 0ca5c9a361732b6b43a8ee9d981539aa24d83623 Mon Sep 17 00:00:00 2001
Ryan Wilson 61c859
From: Ryan Wilson <ryantimwilson@meta.com>
Ryan Wilson 61c859
Date: Mon, 2 Dec 2024 08:10:05 -0800
Ryan Wilson 61c859
Subject: [PATCH 2/2] core: Add ProtectHostname=private
Ryan Wilson 61c859
Ryan Wilson 61c859
This allows an option for systemd exec units to enable UTS namespaces
Ryan Wilson 61c859
but not restrict changing hostname via seccomp. Thus, units can change
Ryan Wilson 61c859
hostname without affecting the host.
Ryan Wilson 61c859
Ryan Wilson 61c859
Fixes: #30348
Ryan Wilson 61c859
---
Ryan Wilson 61c859
 man/systemd.exec.xml                        | 13 +++++-
Ryan Wilson 61c859
 mkosi.conf                                  |  1 +
Ryan Wilson 61c859
 src/core/exec-invoke.c                      | 19 +++++----
Ryan Wilson 61c859
 src/core/namespace.c                        |  1 +
Ryan Wilson 61c859
 src/core/namespace.h                        |  1 +
Ryan Wilson 61c859
 test/TEST-07-PID1/test.sh                   |  2 +-
Ryan Wilson 61c859
 test/units/TEST-07-PID1.protect-hostname.sh | 44 +++++++++++++++++++++
Ryan Wilson 61c859
 7 files changed, 71 insertions(+), 10 deletions(-)
Ryan Wilson 61c859
 create mode 100755 test/units/TEST-07-PID1.protect-hostname.sh
Ryan Wilson 61c859
Ryan Wilson 61c859
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
Ryan Wilson 61c859
index 607c88128ded4..5759874741815 100644
Ryan Wilson 61c859
--- a/man/systemd.exec.xml
Ryan Wilson 61c859
+++ b/man/systemd.exec.xml
Ryan Wilson 61c859
@@ -2055,8 +2055,11 @@ BindReadOnlyPaths=/var/lib/systemd</programlisting>
Ryan Wilson 61c859
       <varlistentry>
Ryan Wilson 61c859
         <term><varname>ProtectHostname=</varname></term>
Ryan Wilson 61c859
 
Ryan Wilson 61c859
-        <listitem><para>Takes a boolean argument. When set, sets up a new UTS namespace for the executed
Ryan Wilson 61c859
-        processes. In addition, changing hostname or domainname is prevented. Defaults to off.</para>
Ryan Wilson 61c859
+        <listitem><para>Takes a boolean argument or <literal>private</literal>. If enabled, sets up a new UTS namespace
Ryan Wilson 61c859
+        for the executed processes. If set to a true value, changing hostname or domainname via
Ryan Wilson 61c859
+        <function>sethostname()</function> and <function>setdomainname()</function> system calls is prevented. If set to
Ryan Wilson 61c859
+        <literal>private</literal>, changing hostname or domainname is allowed but only affects the unit's UTS namespace.
Ryan Wilson 61c859
+        Defaults to off.</para>
Ryan Wilson 61c859
 
Ryan Wilson 61c859
         <para>Note that the implementation of this setting might be impossible (for example if UTS namespaces
Ryan Wilson 61c859
         are not available), and the unit should be written in a way that does not solely rely on this setting
Ryan Wilson 61c859
@@ -2066,6 +2069,12 @@ BindReadOnlyPaths=/var/lib/systemd</programlisting>
Ryan Wilson 61c859
         the system into the service, it is hence not suitable for services that need to take notice of system
Ryan Wilson 61c859
         hostname changes dynamically.</para>
Ryan Wilson 61c859
 
Ryan Wilson 61c859
+        <para>Note that this option does not prevent changing system hostname via <command>hostnamectl</command>.
Ryan Wilson 61c859
+        However, <varname>User=</varname> and <varname>Group=</varname> may be used to run as an unprivileged user
Ryan Wilson 61c859
+        to disallow changing system hostname. See <function>SetHostname()</function> in
Ryan Wilson 61c859
+        <citerefentry project="man-pages"><refentrytitle>org.freedesktop.hostname1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
Ryan Wilson 61c859
+        for more details.</para>
Ryan Wilson 61c859
+
Ryan Wilson 61c859
         <xi:include href="system-or-user-ns.xml" xpointer="singular"/>
Ryan Wilson 61c859
 
Ryan Wilson 61c859
         <xi:include href="version-info.xml" xpointer="v242"/></listitem>
Ryan Wilson 61c859
diff --git a/mkosi.conf b/mkosi.conf
Ryan Wilson 61c859
index 35a19a27aad39..535e2bd79bf43 100644
Ryan Wilson 61c859
--- a/mkosi.conf
Ryan Wilson 61c859
+++ b/mkosi.conf
Ryan Wilson 61c859
@@ -101,6 +101,7 @@ Packages=
Ryan Wilson 61c859
         gdb
Ryan Wilson 61c859
         grep
Ryan Wilson 61c859
         gzip
Ryan Wilson 61c859
+        hostname
Ryan Wilson 61c859
         jq
Ryan Wilson 61c859
         kbd
Ryan Wilson 61c859
         kexec-tools
Ryan Wilson 61c859
diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c
Ryan Wilson 61c859
index f4aacb55b22bd..fd306f1143125 100644
Ryan Wilson 61c859
--- a/src/core/exec-invoke.c
Ryan Wilson 61c859
+++ b/src/core/exec-invoke.c
Ryan Wilson 61c859
@@ -1726,15 +1726,17 @@ static int apply_protect_hostname(const ExecContext *c, const ExecParameters *p,
Ryan Wilson 61c859
                                  "support UTS namespaces, ignoring namespace setup.");
Ryan Wilson 61c859
 
Ryan Wilson 61c859
 #if HAVE_SECCOMP
Ryan Wilson 61c859
-        int r;
Ryan Wilson 61c859
+        if (c->protect_hostname == PROTECT_HOSTNAME_YES) {
Ryan Wilson 61c859
+                int r;
Ryan Wilson 61c859
 
Ryan Wilson 61c859
-        if (skip_seccomp_unavailable(c, p, "ProtectHostname="))
Ryan Wilson 61c859
-                return 0;
Ryan Wilson 61c859
+                if (skip_seccomp_unavailable(c, p, "ProtectHostname="))
Ryan Wilson 61c859
+                        return 0;
Ryan Wilson 61c859
 
Ryan Wilson 61c859
-        r = seccomp_protect_hostname();
Ryan Wilson 61c859
-        if (r < 0) {
Ryan Wilson 61c859
-                *ret_exit_status = EXIT_SECCOMP;
Ryan Wilson 61c859
-                return log_exec_error_errno(c, p, r, "Failed to apply hostname restrictions: %m");
Ryan Wilson 61c859
+                r = seccomp_protect_hostname();
Ryan Wilson 61c859
+                if (r < 0) {
Ryan Wilson 61c859
+                        *ret_exit_status = EXIT_SECCOMP;
Ryan Wilson 61c859
+                        return log_exec_error_errno(c, p, r, "Failed to apply hostname restrictions: %m");
Ryan Wilson 61c859
+                }
Ryan Wilson 61c859
         }
Ryan Wilson 61c859
 #endif
Ryan Wilson 61c859
 
Ryan Wilson 61c859
@@ -3417,6 +3419,9 @@ static int apply_mount_namespace(
Ryan Wilson 61c859
                 .protect_kernel_tunables = needs_sandboxing && context->protect_kernel_tunables,
Ryan Wilson 61c859
                 .protect_kernel_modules = needs_sandboxing && context->protect_kernel_modules,
Ryan Wilson 61c859
                 .protect_kernel_logs = needs_sandboxing && context->protect_kernel_logs,
Ryan Wilson 61c859
+                /* Only mount /proc/sys/kernel/hostname and domainname read-only if ProtectHostname=yes. Otherwise, ProtectHostname=no
Ryan Wilson 61c859
+                 * allows changing hostname for the host and ProtectHostname=private allows changing the hostname in the unit's UTS
Ryan Wilson 61c859
+                 * namespace. */
Ryan Wilson 61c859
                 .protect_hostname = needs_sandboxing && context->protect_hostname == PROTECT_HOSTNAME_YES,
Ryan Wilson 61c859
 
Ryan Wilson 61c859
                 .private_dev = needs_sandboxing && context->private_devices,
Ryan Wilson 61c859
diff --git a/src/core/namespace.c b/src/core/namespace.c
Ryan Wilson 61c859
index c327c9a3ca488..2f3b8f03d1308 100644
Ryan Wilson 61c859
--- a/src/core/namespace.c
Ryan Wilson 61c859
+++ b/src/core/namespace.c
Ryan Wilson 61c859
@@ -3308,6 +3308,7 @@ DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(protect_home, ProtectHome, PROTECT_HOME_
Ryan Wilson 61c859
 static const char *const protect_hostname_table[_PROTECT_HOSTNAME_MAX] = {
Ryan Wilson 61c859
         [PROTECT_HOSTNAME_NO]      = "no",
Ryan Wilson 61c859
         [PROTECT_HOSTNAME_YES]     = "yes",
Ryan Wilson 61c859
+        [PROTECT_HOSTNAME_PRIVATE] = "private",
Ryan Wilson 61c859
 };
Ryan Wilson 61c859
 
Ryan Wilson 61c859
 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(protect_hostname, ProtectHostname, PROTECT_HOSTNAME_YES);
Ryan Wilson 61c859
diff --git a/src/core/namespace.h b/src/core/namespace.h
Ryan Wilson 61c859
index 8df91e3bdf906..96f62be30a269 100644
Ryan Wilson 61c859
--- a/src/core/namespace.h
Ryan Wilson 61c859
+++ b/src/core/namespace.h
Ryan Wilson 61c859
@@ -31,6 +31,7 @@ typedef enum ProtectHome {
Ryan Wilson 61c859
 typedef enum ProtectHostname {
Ryan Wilson 61c859
         PROTECT_HOSTNAME_NO,
Ryan Wilson 61c859
         PROTECT_HOSTNAME_YES,
Ryan Wilson 61c859
+        PROTECT_HOSTNAME_PRIVATE,
Ryan Wilson 61c859
         _PROTECT_HOSTNAME_MAX,
Ryan Wilson 61c859
         _PROTECT_HOSTNAME_INVALID = -EINVAL,
Ryan Wilson 61c859
 } ProtectHostname;
Ryan Wilson 61c859
diff --git a/test/TEST-07-PID1/test.sh b/test/TEST-07-PID1/test.sh
Ryan Wilson 61c859
index 66e1b684ea8a3..8e8a799a7150c 100755
Ryan Wilson 61c859
--- a/test/TEST-07-PID1/test.sh
Ryan Wilson 61c859
+++ b/test/TEST-07-PID1/test.sh
Ryan Wilson 61c859
@@ -13,7 +13,7 @@ TEST_INSTALL_VERITY_MINIMAL=1
Ryan Wilson 61c859
 . "${TEST_BASE_DIR:?}/test-functions"
Ryan Wilson 61c859
 
Ryan Wilson 61c859
 test_append_files() {
Ryan Wilson 61c859
-    image_install logger socat
Ryan Wilson 61c859
+    image_install logger socat hostname
Ryan Wilson 61c859
     inst_binary mksquashfs
Ryan Wilson 61c859
     inst_binary unsquashfs
Ryan Wilson 61c859
     install_verity_minimal
Ryan Wilson 61c859
diff --git a/test/units/TEST-07-PID1.protect-hostname.sh b/test/units/TEST-07-PID1.protect-hostname.sh
Ryan Wilson 61c859
new file mode 100755
Ryan Wilson 61c859
index 0000000000000..c2ede395535f5
Ryan Wilson 61c859
--- /dev/null
Ryan Wilson 61c859
+++ b/test/units/TEST-07-PID1.protect-hostname.sh
Ryan Wilson 61c859
@@ -0,0 +1,44 @@
Ryan Wilson 61c859
+#!/usr/bin/env bash
Ryan Wilson 61c859
+# SPDX-License-Identifier: LGPL-2.1-or-later
Ryan Wilson 61c859
+# shellcheck disable=SC2016
Ryan Wilson 61c859
+set -eux
Ryan Wilson 61c859
+set -o pipefail
Ryan Wilson 61c859
+
Ryan Wilson 61c859
+# shellcheck source=test/units/test-control.sh
Ryan Wilson 61c859
+. "$(dirname "$0")"/test-control.sh
Ryan Wilson 61c859
+# shellcheck source=test/units/util.sh
Ryan Wilson 61c859
+. "$(dirname "$0")"/util.sh
Ryan Wilson 61c859
+
Ryan Wilson 61c859
+LEGACY_HOSTNAME="$(hostname)"
Ryan Wilson 61c859
+HOSTNAME_FROM_SYSTEMD="$(hostnamectl hostname)"
Ryan Wilson 61c859
+
Ryan Wilson 61c859
+testcase_yes() {
Ryan Wilson 61c859
+    # hostnamectl calls SetHostname method via dbus socket which executes in homenamed
Ryan Wilson 61c859
+    # in the init namespace. So hostnamectl is not affected by ProtectHostname=yes or
Ryan Wilson 61c859
+    # private since sethostname() system call is executed in the init namespace.
Ryan Wilson 61c859
+    #
Ryan Wilson 61c859
+    # hostnamed does authentication based on UID via polkit so this guarantees admins
Ryan Wilson 61c859
+    # can only set hostname.
Ryan Wilson 61c859
+    (! systemd-run --wait -p ProtectHostname=yes hostname foo)
Ryan Wilson 61c859
+
Ryan Wilson 61c859
+    systemd-run --wait -p ProtectHostname=yes -p PrivateMounts=yes \
Ryan Wilson 61c859
+        findmnt --mountpoint /proc/sys/kernel/hostname
Ryan Wilson 61c859
+}
Ryan Wilson 61c859
+
Ryan Wilson 61c859
+testcase_private() {
Ryan Wilson 61c859
+    systemd-run --wait -p ProtectHostnameEx=private \
Ryan Wilson 61c859
+        -P bash -xec '
Ryan Wilson 61c859
+            hostname foo
Ryan Wilson 61c859
+            test "$(hostname)" = "foo"
Ryan Wilson 61c859
+        '
Ryan Wilson 61c859
+
Ryan Wilson 61c859
+    # Verify host hostname is unchanged.
Ryan Wilson 61c859
+    test "$(hostname)" = "$LEGACY_HOSTNAME"
Ryan Wilson 61c859
+    test "$(hostnamectl hostname)" = "$HOSTNAME_FROM_SYSTEMD"
Ryan Wilson 61c859
+
Ryan Wilson 61c859
+    # Verify /proc/sys/kernel/hostname is not bind mounted from host read-only.
Ryan Wilson 61c859
+    (! systemd-run --wait -p ProtectHostnameEx=private -p PrivateMounts=yes \
Ryan Wilson 61c859
+        findmnt --mountpoint /proc/sys/kernel/hostname)
Ryan Wilson 61c859
+}
Ryan Wilson 61c859
+
Ryan Wilson 61c859
+run_testcases