]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: expose PrivateTmp=disconnected 33439/head
authorMike Yuan <me@yhndnzj.com>
Tue, 18 Jun 2024 14:18:56 +0000 (16:18 +0200)
committerMike Yuan <me@yhndnzj.com>
Fri, 21 Jun 2024 15:31:44 +0000 (17:31 +0200)
As discussed in https://github.com/systemd/systemd/pull/32724#discussion_r1638963071

I don't find the opposite reasoning particularly convincing.
We have ProtectHome=tmpfs and friends, and those can be
pretty much trivially implemented through TemporaryFileSystem=
too. The new logic brings many benefits, and is completely generic,
hence I see no reason not to expose it. We can even get more tests
for the code path if we make it public.

man/org.freedesktop.systemd1.xml
man/systemd.exec.xml
src/core/dbus-execute.c
src/core/load-fragment-gperf.gperf.in
src/core/load-fragment.c
src/core/namespace.c

index a806ca9c38ed199ec36c4e996713598d6f3dfb6d..31e6194bec35587a3d71f73d71bebcad58d29f0e 100644 (file)
@@ -3207,6 +3207,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b PrivateTmp = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s PrivateTmpEx = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b PrivateDevices = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b ProtectClock = ...;
@@ -3816,6 +3818,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <!--property PrivateTmp is not documented!-->
 
+    <!--property PrivateTmpEx is not documented!-->
+
     <!--property PrivateDevices is not documented!-->
 
     <!--property ProtectClock is not documented!-->
@@ -4504,6 +4508,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
 
     <variablelist class="dbus-property" generated="True" extra-ref="PrivateTmp"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateTmpEx"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="PrivateDevices"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="ProtectClock"/>
@@ -5326,6 +5332,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b PrivateTmp = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s PrivateTmpEx = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b PrivateDevices = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b ProtectClock = ...;
@@ -5949,6 +5957,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <!--property PrivateTmp is not documented!-->
 
+    <!--property PrivateTmpEx is not documented!-->
+
     <!--property PrivateDevices is not documented!-->
 
     <!--property ProtectClock is not documented!-->
@@ -6613,6 +6623,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
 
     <variablelist class="dbus-property" generated="True" extra-ref="PrivateTmp"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateTmpEx"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="PrivateDevices"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="ProtectClock"/>
@@ -7299,6 +7311,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b PrivateTmp = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s PrivateTmpEx = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b PrivateDevices = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b ProtectClock = ...;
@@ -7848,6 +7862,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
 
     <!--property PrivateTmp is not documented!-->
 
+    <!--property PrivateTmpEx is not documented!-->
+
     <!--property PrivateDevices is not documented!-->
 
     <!--property ProtectClock is not documented!-->
@@ -8424,6 +8440,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
 
     <variablelist class="dbus-property" generated="True" extra-ref="PrivateTmp"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateTmpEx"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="PrivateDevices"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="ProtectClock"/>
@@ -9233,6 +9251,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b PrivateTmp = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s PrivateTmpEx = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b PrivateDevices = ...;
       @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
       readonly b ProtectClock = ...;
@@ -9768,6 +9788,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
 
     <!--property PrivateTmp is not documented!-->
 
+    <!--property PrivateTmpEx is not documented!-->
+
     <!--property PrivateDevices is not documented!-->
 
     <!--property ProtectClock is not documented!-->
@@ -10330,6 +10352,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
 
     <variablelist class="dbus-property" generated="True" extra-ref="PrivateTmp"/>
 
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateTmpEx"/>
+
     <variablelist class="dbus-property" generated="True" extra-ref="PrivateDevices"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="ProtectClock"/>
@@ -12074,8 +12098,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <varname>MemoryZSwapWriteback</varname>,
       <varname>ExecMainHandoffTimestampMonotonic</varname>, and
       <varname>ExecMainHandoffTimestamp</varname> were added in version 256.</para>
-      <para><varname>StatusBusError</varname> and
-      <varname>StatusVarlinkError</varname> were added in version 257.</para>
+      <para><varname>StatusBusError</varname>,
+      <varname>StatusVarlinkError</varname>, and
+      <varname>PrivateTmpEx</varname> were added in version 257.</para>
     </refsect2>
     <refsect2>
       <title>Socket Unit Objects</title>
@@ -12112,6 +12137,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <varname>EffectiveTasksMax</varname>,
       <varname>MemoryZSwapWriteback</varname>, and
       <varname>PassFileDescriptorsToExec</varname> were added in version 256.</para>
+      <para><varname>PrivateTmpEx</varname> was added in version 257.</para>
     </refsect2>
     <refsect2>
       <title>Mount Unit Objects</title>
@@ -12145,6 +12171,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <varname>EffectiveMemoryMax</varname>,
       <varname>EffectiveTasksMax</varname>, and
       <varname>MemoryZSwapWriteback</varname> were added in version 256.</para>
+      <para><varname>PrivateTmpEx</varname> was added in version 257.</para>
     </refsect2>
     <refsect2>
       <title>Swap Unit Objects</title>
@@ -12178,6 +12205,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
       <varname>EffectiveMemoryMax</varname>,
       <varname>EffectiveTasksMax</varname>, and
       <varname>MemoryZSwapWriteback</varname> were added in version 256.</para>
+      <para><varname>PrivateTmpEx</varname> was added in version 257.</para>
     </refsect2>
     <refsect2>
       <title>Slice Unit Objects</title>
index 9e621b9aa3023f00b2a82161664edcabda1428f2..7a2fc76b656ab7a17837b4840d3bc95bff044616 100644 (file)
         of IPC objects and temporary files created by the executed processes is bound to the runtime of the
         service, and hence the lifetime of the dynamic user/group. Since <filename>/tmp/</filename> and
         <filename>/var/tmp/</filename> are usually the only world-writable directories on a system, unless
-        <varname>PrivateTmp=</varname> is manually enabled, those directories will be placed on a private
-        tmpfs filesystem, as this ensures that a unit making use of dynamic user/group allocation cannot
+        <varname>PrivateTmp=</varname> is manually set to <literal>true</literal>, <literal>disconnected</literal>
+        would be implied. This ensures that a unit making use of dynamic user/group allocation cannot
         leave files around after unit termination. Furthermore
         <varname>NoNewPrivileges=</varname> and <varname>RestrictSUIDSGID=</varname> are implicitly enabled
         (and cannot be disabled), to ensure that processes invoked cannot take benefit or create SUID/SGID
@@ -1748,20 +1748,27 @@ BindReadOnlyPaths=/var/lib/systemd</programlisting>
       <varlistentry>
         <term><varname>PrivateTmp=</varname></term>
 
-        <listitem><para>Takes a boolean argument. If true, sets up a new file system namespace for the
-        executed processes and mounts private <filename>/tmp/</filename> and <filename>/var/tmp/</filename>
-        directories inside it that are not shared by processes outside of the namespace. This is useful to
-        secure access to temporary files of the process, but makes sharing between processes via
-        <filename>/tmp/</filename> or <filename>/var/tmp/</filename> impossible. If true, all temporary files
-        created by a service in these directories will be removed after the service is stopped. Defaults to
-        false. It is possible to run two or more units within the same private <filename>/tmp/</filename> and
-        <filename>/var/tmp/</filename> namespace by using the <varname>JoinsNamespaceOf=</varname> directive,
-        see <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-        for details. This setting is implied if <varname>DynamicUser=</varname> is set. For this setting, the
-        same restrictions regarding mount propagation and privileges apply as for
-        <varname>ReadOnlyPaths=</varname> and related calls, see above. Enabling this setting has the side
-        effect of adding <varname>Requires=</varname> and <varname>After=</varname> dependencies on all mount
-        units necessary to access <filename>/tmp/</filename> and <filename>/var/tmp/</filename>. Moreover an
+        <listitem><para>Takes a boolean argument, or <literal>disconnected</literal>. If enabled, a new
+        file system namespace will be set up for the executed processes, and <filename>/tmp/</filename>
+        and <filename>/var/tmp/</filename> directories inside it are not shared with processes outside of
+        the namespace, plus all temporary files created by a service in these directories will be removed after
+        the service is stopped. If <literal>true</literal>, the backing storage of the private temporary directories
+        will remain on the host's <filename>/tmp/</filename> and <filename>/var/tmp/</filename> directories.
+        If <literal>disconnected</literal>, the directories will be backed by a completely new tmpfs instance,
+        meaning that the storage is fully disconnected from the host namespace. Defaults to false.</para>
+
+        <para>This setting is useful to secure access to temporary files of the process, but makes sharing
+        between processes via <filename>/tmp/</filename> or <filename>/var/tmp/</filename> impossible.
+        If not set to <literal>disconnected</literal>, it is possible to run two or more units within
+        the same private <filename>/tmp/</filename> and <filename>/var/tmp/</filename> namespace by using
+        the <varname>JoinsNamespaceOf=</varname> directive, see
+        <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        for details. This setting is implied if <varname>DynamicUser=</varname> is set. For this setting,
+        the same restrictions regarding mount propagation and privileges apply as for
+        <varname>ReadOnlyPaths=</varname> and related calls, see above. If set to <literal>true</literal>
+        (as opposed to <literal>disconnected</literal>), this has the side effect of adding
+        <varname>Requires=</varname> and <varname>After=</varname> dependencies on all mount units necessary
+        to access <filename>/tmp/</filename> and <filename>/var/tmp/</filename> on the host. Moreover an
         implicitly <varname>After=</varname> ordering on
         <citerefentry><refentrytitle>systemd-tmpfiles-setup.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
         is added.</para>
index dde15e18fc2fab21f67311f03e116ff88b71bf99..0346ee1478a52a47e3de83fdd07e053a71650e21 100644 (file)
@@ -58,6 +58,7 @@ static BUS_DEFINE_PROPERTY_GET(property_get_mount_apivfs, "b", ExecContext, exec
 static BUS_DEFINE_PROPERTY_GET2(property_get_ioprio_class, "i", ExecContext, exec_context_get_effective_ioprio, ioprio_prio_class);
 static BUS_DEFINE_PROPERTY_GET2(property_get_ioprio_priority, "i", ExecContext, exec_context_get_effective_ioprio, ioprio_prio_data);
 static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_empty_string, "s", NULL);
+static BUS_DEFINE_PROPERTY_GET_REF(property_get_private_tmp_ex, "s", PrivateTmp, private_tmp_to_string);
 static BUS_DEFINE_PROPERTY_GET_REF(property_get_syslog_level, "i", int, LOG_PRI);
 static BUS_DEFINE_PROPERTY_GET_REF(property_get_syslog_facility, "i", int, LOG_FAC);
 static BUS_DEFINE_PROPERTY_GET(property_get_cpu_affinity_from_numa, "b", ExecContext, exec_context_get_cpu_affinity_from_numa);
@@ -1078,6 +1079,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
         SD_BUS_PROPERTY("ExecSearchPath", "as", NULL, offsetof(ExecContext, exec_search_path), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("MountFlags", "t", bus_property_get_ulong, offsetof(ExecContext, mount_propagation_flag), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("PrivateTmp", "b", property_get_private_tmp, offsetof(ExecContext, private_tmp), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("PrivateTmpEx", "s", property_get_private_tmp_ex, offsetof(ExecContext, private_tmp), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("PrivateDevices", "b", bus_property_get_bool, offsetof(ExecContext, private_devices), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("ProtectClock", "b", bus_property_get_bool, offsetof(ExecContext, protect_clock), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("ProtectKernelTunables", "b", bus_property_get_bool, offsetof(ExecContext, protect_kernel_tunables), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1769,6 +1771,26 @@ int bus_exec_context_set_transient_property(
                         (void) unit_write_settingf(u, flags, name, "%s=%s", name, yes_no(v));
                 }
 
+                return 1;
+
+        } else if (streq(name, "PrivateTmpEx")) {
+                const char *s;
+                PrivateTmp t;
+
+                r = sd_bus_message_read(message, "s", &s);
+                if (r < 0)
+                        return r;
+
+                t = private_tmp_from_string(s);
+                if (t < 0)
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s setting: %s", name, s);
+
+                if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+                        c->private_tmp = t;
+                        (void) unit_write_settingf(u, flags, name, "PrivateTmp=%s",
+                                                   private_tmp_to_string(c->private_tmp));
+                }
+
                 return 1;
         }
 
index 46c2198ac71f49f0d898d0a96257851988f95b82..e04450d869dd2a0cdfa12be8ee621b623c70eeba 100644 (file)
 {{type}}.BindPaths,                        config_parse_bind_paths,                     0,                                  offsetof({{type}}, exec_context)
 {{type}}.BindReadOnlyPaths,                config_parse_bind_paths,                     0,                                  offsetof({{type}}, exec_context)
 {{type}}.TemporaryFileSystem,              config_parse_temporary_filesystems,          0,                                  offsetof({{type}}, exec_context)
-{{type}}.PrivateTmp,                       config_parse_private_tmp,                    0,                                  offsetof({{type}}, exec_context)
+{{type}}.PrivateTmp,                       config_parse_private_tmp,                    0,                                  offsetof({{type}}, exec_context.private_tmp)
 {{type}}.PrivateDevices,                   config_parse_bool,                           0,                                  offsetof({{type}}, exec_context.private_devices)
 {{type}}.ProtectKernelTunables,            config_parse_bool,                           0,                                  offsetof({{type}}, exec_context.protect_kernel_tunables)
 {{type}}.ProtectKernelModules,             config_parse_bool,                           0,                                  offsetof({{type}}, exec_context.protect_kernel_modules)
index 3701270ab53e1f4947f91439e4c25bfabdc9cb4d..deeeafe2b8aa51d516ea7ead24b41d48f59b70ad 100644 (file)
@@ -133,6 +133,7 @@ DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGrou
 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode, "Failed to parse keyring mode");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_proc, protect_proc, ProtectProc, "Failed to parse /proc/ protection mode");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_proc_subset, proc_subset, ProcSubset, "Failed to parse /proc/ subset mode");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_private_tmp, private_tmp, PrivateTmp, "Failed to parse private tmp value");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
@@ -5199,34 +5200,6 @@ int config_parse_temporary_filesystems(
         }
 }
 
-int config_parse_private_tmp(
-                const char* unit,
-                const char *filename,
-                unsigned line,
-                const char *section,
-                unsigned section_line,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        ExecContext *c = ASSERT_PTR(data);
-        int r;
-
-        assert(filename);
-        assert(rvalue);
-
-        r = parse_boolean(rvalue);
-        if (r < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse boolean value: %s ignoring", rvalue);
-                return 0;
-        }
-
-        c->private_tmp = r ? PRIVATE_TMP_CONNECTED : PRIVATE_TMP_OFF;
-        return 0;
-}
-
 int config_parse_bind_paths(
                 const char *unit,
                 const char *filename,
index 67ab6a15f47232b542d46d5dd9e5e0f6c1178cd0..6d3cadf05cbb13a72a181c95c70ad844d891d745 100644 (file)
@@ -3157,4 +3157,4 @@ static const char* const private_tmp_table[_PRIVATE_TMP_MAX] = {
         [PRIVATE_TMP_DISCONNECTED] = "disconnected",
 };
 
-DEFINE_STRING_TABLE_LOOKUP(private_tmp, PrivateTmp);
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(private_tmp, PrivateTmp, PRIVATE_TMP_CONNECTED);