diff options
author | Mike Yuan <me@yhndnzj.com> | 2024-06-18 16:18:56 +0200 |
---|---|---|
committer | Mike Yuan <me@yhndnzj.com> | 2024-06-21 17:31:44 +0200 |
commit | 9d50d053f327e5471a514bc36487bfc3358c15d8 (patch) | |
tree | f464e0de7a484277351256c17c55d080266b7c4e | |
parent | core/namespace: add assertion for PRIVATE_TMP_CONNECTED (diff) | |
download | systemd-9d50d053f327e5471a514bc36487bfc3358c15d8.tar.xz systemd-9d50d053f327e5471a514bc36487bfc3358c15d8.zip |
core: expose PrivateTmp=disconnected
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.
-rw-r--r-- | man/org.freedesktop.systemd1.xml | 32 | ||||
-rw-r--r-- | man/systemd.exec.xml | 39 | ||||
-rw-r--r-- | src/core/dbus-execute.c | 22 | ||||
-rw-r--r-- | src/core/load-fragment-gperf.gperf.in | 2 | ||||
-rw-r--r-- | src/core/load-fragment.c | 29 | ||||
-rw-r--r-- | src/core/namespace.c | 2 |
6 files changed, 78 insertions, 48 deletions
diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml index a806ca9c38..31e6194bec 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -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> diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 9e621b9aa3..7a2fc76b65 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -675,8 +675,8 @@ 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> diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index dde15e18fc..0346ee1478 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -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), @@ -1770,6 +1772,26 @@ int bus_exec_context_set_transient_property( } 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; } if (streq(name, "PrivateDevices")) diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in index 46c2198ac7..e04450d869 100644 --- a/src/core/load-fragment-gperf.gperf.in +++ b/src/core/load-fragment-gperf.gperf.in @@ -119,7 +119,7 @@ {{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) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 3701270ab5..deeeafe2b8 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -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, diff --git a/src/core/namespace.c b/src/core/namespace.c index 67ab6a15f4..6d3cadf05c 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -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); |