summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Yuan <me@yhndnzj.com>2024-06-18 16:18:56 +0200
committerMike Yuan <me@yhndnzj.com>2024-06-21 17:31:44 +0200
commit9d50d053f327e5471a514bc36487bfc3358c15d8 (patch)
treef464e0de7a484277351256c17c55d080266b7c4e
parentcore/namespace: add assertion for PRIVATE_TMP_CONNECTED (diff)
downloadsystemd-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.xml32
-rw-r--r--man/systemd.exec.xml39
-rw-r--r--src/core/dbus-execute.c22
-rw-r--r--src/core/load-fragment-gperf.gperf.in2
-rw-r--r--src/core/load-fragment.c29
-rw-r--r--src/core/namespace.c2
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);