|
|
142bf4 |
From 10e421f70889a672f984785393051e0471c0aeb2 Mon Sep 17 00:00:00 2001
|
|
|
b80fe6 |
From: Daan De Meyer <daan.j.demeyer@gmail.com>
|
|
|
b80fe6 |
Date: Mon, 9 Sep 2024 12:25:28 +0200
|
|
|
b80fe6 |
Subject: [PATCH] core: Add support for PrivateUsers=identity
|
|
|
b80fe6 |
|
|
|
b80fe6 |
This configures an indentity mapping similar to
|
|
|
b80fe6 |
systemd-nspawn --private-users=identity.
|
|
|
142bf4 |
|
|
|
142bf4 |
(cherry picked from commit fa693fdc7e17618958c505af4b2f39ecd1c3363e)
|
|
|
b80fe6 |
---
|
|
|
142bf4 |
man/org.freedesktop.systemd1.xml | 24 ++++++++++
|
|
|
b80fe6 |
man/systemd.exec.xml | 35 ++++++++++-----
|
|
|
b80fe6 |
src/core/dbus-execute.c | 57 ++++++++++++++++++++++--
|
|
|
142bf4 |
src/core/exec-invoke.c | 56 +++++++++++++++--------
|
|
|
b80fe6 |
src/core/execute-serialize.c | 9 ++--
|
|
|
b80fe6 |
src/core/execute.c | 2 +-
|
|
|
b80fe6 |
src/core/execute.h | 2 +-
|
|
|
b80fe6 |
src/core/load-fragment-gperf.gperf.in | 2 +-
|
|
|
b80fe6 |
src/core/load-fragment.c | 1 +
|
|
|
b80fe6 |
src/core/load-fragment.h | 1 +
|
|
|
b80fe6 |
src/core/namespace.c | 8 ++++
|
|
|
b80fe6 |
src/core/namespace.h | 11 +++++
|
|
|
b80fe6 |
src/shared/bus-unit-util.c | 1 +
|
|
|
b80fe6 |
test/units/TEST-07-PID1.private-users.sh | 12 +++++
|
|
|
142bf4 |
14 files changed, 179 insertions(+), 42 deletions(-)
|
|
|
b80fe6 |
create mode 100755 test/units/TEST-07-PID1.private-users.sh
|
|
|
b80fe6 |
|
|
|
b80fe6 |
diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
|
|
|
142bf4 |
index b0b45097e30a3..ce0757cf9cfa2 100644
|
|
|
b80fe6 |
--- a/man/org.freedesktop.systemd1.xml
|
|
|
b80fe6 |
+++ b/man/org.freedesktop.systemd1.xml
|
|
|
142bf4 |
@@ -3221,6 +3221,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
|
|
b80fe6 |
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
|
|
b80fe6 |
readonly b PrivateUsers = ...;
|
|
|
b80fe6 |
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
|
|
b80fe6 |
+ readonly s PrivateUsersEx = '...';
|
|
|
b80fe6 |
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
|
|
b80fe6 |
readonly b PrivateMounts = ...;
|
|
|
b80fe6 |
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
|
|
b80fe6 |
readonly b PrivateIPC = ...;
|
|
|
142bf4 |
@@ -3832,6 +3834,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
|
|
b80fe6 |
|
|
|
b80fe6 |
|
|
|
b80fe6 |
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
|
|
|
b80fe6 |
|
|
|
b80fe6 |
|
|
|
142bf4 |
@@ -4516,6 +4520,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
|
|
b80fe6 |
|
|
|
b80fe6 |
<variablelist class="dbus-property" generated="True" extra-ref="PrivateUsers"/>
|
|
|
b80fe6 |
|
|
|
b80fe6 |
+ <variablelist class="dbus-property" generated="True" extra-ref="PrivateUsersEx"/>
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
<variablelist class="dbus-property" generated="True" extra-ref="PrivateMounts"/>
|
|
|
b80fe6 |
|
|
|
b80fe6 |
<variablelist class="dbus-property" generated="True" extra-ref="PrivateIPC"/>
|
|
|
142bf4 |
@@ -5338,6 +5344,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
|
|
b80fe6 |
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
|
|
b80fe6 |
readonly b PrivateUsers = ...;
|
|
|
b80fe6 |
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
|
|
b80fe6 |
+ readonly s PrivateUsersEx = '...';
|
|
|
b80fe6 |
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
|
|
b80fe6 |
readonly b PrivateMounts = ...;
|
|
|
b80fe6 |
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
|
|
b80fe6 |
readonly b PrivateIPC = ...;
|
|
|
142bf4 |
@@ -5961,6 +5969,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
|
|
b80fe6 |
|
|
|
b80fe6 |
|
|
|
b80fe6 |
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
|
|
|
b80fe6 |
|
|
|
b80fe6 |
|
|
|
142bf4 |
@@ -6625,6 +6635,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
|
|
b80fe6 |
|
|
|
b80fe6 |
<variablelist class="dbus-property" generated="True" extra-ref="PrivateUsers"/>
|
|
|
b80fe6 |
|
|
|
b80fe6 |
+ <variablelist class="dbus-property" generated="True" extra-ref="PrivateUsersEx"/>
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
<variablelist class="dbus-property" generated="True" extra-ref="PrivateMounts"/>
|
|
|
b80fe6 |
|
|
|
b80fe6 |
<variablelist class="dbus-property" generated="True" extra-ref="PrivateIPC"/>
|
|
|
142bf4 |
@@ -7311,6 +7323,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
|
|
b80fe6 |
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
|
|
b80fe6 |
readonly b PrivateUsers = ...;
|
|
|
b80fe6 |
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
|
|
b80fe6 |
+ readonly s PrivateUsersEx = '...';
|
|
|
b80fe6 |
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
|
|
b80fe6 |
readonly b PrivateMounts = ...;
|
|
|
b80fe6 |
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
|
|
b80fe6 |
readonly b PrivateIPC = ...;
|
|
|
142bf4 |
@@ -7860,6 +7874,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
|
|
b80fe6 |
|
|
|
b80fe6 |
|
|
|
b80fe6 |
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
|
|
|
b80fe6 |
|
|
|
b80fe6 |
|
|
|
142bf4 |
@@ -8436,6 +8452,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
|
|
b80fe6 |
|
|
|
b80fe6 |
<variablelist class="dbus-property" generated="True" extra-ref="PrivateUsers"/>
|
|
|
b80fe6 |
|
|
|
b80fe6 |
+ <variablelist class="dbus-property" generated="True" extra-ref="PrivateUsersEx"/>
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
<variablelist class="dbus-property" generated="True" extra-ref="PrivateMounts"/>
|
|
|
b80fe6 |
|
|
|
b80fe6 |
<variablelist class="dbus-property" generated="True" extra-ref="PrivateIPC"/>
|
|
|
142bf4 |
@@ -9245,6 +9263,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
|
|
b80fe6 |
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
|
|
b80fe6 |
readonly b PrivateUsers = ...;
|
|
|
b80fe6 |
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
|
|
b80fe6 |
+ readonly s PrivateUsersEx = '...';
|
|
|
b80fe6 |
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
|
|
b80fe6 |
readonly b PrivateMounts = ...;
|
|
|
b80fe6 |
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
|
|
b80fe6 |
readonly b PrivateIPC = ...;
|
|
|
142bf4 |
@@ -9780,6 +9800,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
|
|
b80fe6 |
|
|
|
b80fe6 |
|
|
|
b80fe6 |
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
|
|
|
b80fe6 |
|
|
|
b80fe6 |
|
|
|
142bf4 |
@@ -10342,6 +10364,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
|
|
b80fe6 |
|
|
|
b80fe6 |
<variablelist class="dbus-property" generated="True" extra-ref="PrivateUsers"/>
|
|
|
b80fe6 |
|
|
|
b80fe6 |
+ <variablelist class="dbus-property" generated="True" extra-ref="PrivateUsersEx"/>
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
<variablelist class="dbus-property" generated="True" extra-ref="PrivateMounts"/>
|
|
|
b80fe6 |
|
|
|
b80fe6 |
<variablelist class="dbus-property" generated="True" extra-ref="PrivateIPC"/>
|
|
|
b80fe6 |
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
|
|
|
142bf4 |
index 21527f756d669..3cdb7ff70a09b 100644
|
|
|
b80fe6 |
--- a/man/systemd.exec.xml
|
|
|
b80fe6 |
+++ b/man/systemd.exec.xml
|
|
|
142bf4 |
@@ -1941,18 +1941,29 @@ BindReadOnlyPaths=/var/lib/systemd</programlisting>
|
|
|
b80fe6 |
<varlistentry>
|
|
|
b80fe6 |
<term><varname>PrivateUsers=</varname></term>
|
|
|
b80fe6 |
|
|
|
b80fe6 |
- <listitem><para>Takes a boolean argument. If true, sets up a new user namespace for the executed processes and
|
|
|
b80fe6 |
- configures a minimal user and group mapping, that maps the <literal>root</literal> user and group as well as
|
|
|
b80fe6 |
- the unit's own user and group to themselves and everything else to the <literal>nobody</literal> user and
|
|
|
b80fe6 |
- group. This is useful to securely detach the user and group databases used by the unit from the rest of the
|
|
|
b80fe6 |
- system, and thus to create an effective sandbox environment. All files, directories, processes, IPC objects and
|
|
|
b80fe6 |
- other resources owned by users/groups not equaling <literal>root</literal> or the unit's own will stay visible
|
|
|
b80fe6 |
- from within the unit but appear owned by the <literal>nobody</literal> user and group. If this mode is enabled,
|
|
|
b80fe6 |
- all unit processes are run without privileges in the host user namespace (regardless if the unit's own
|
|
|
b80fe6 |
- user/group is <literal>root</literal> or not). Specifically this means that the process will have zero process
|
|
|
b80fe6 |
- capabilities on the host's user namespace, but full capabilities within the service's user namespace. Settings
|
|
|
b80fe6 |
- such as <varname>CapabilityBoundingSet=</varname> will affect only the latter, and there's no way to acquire
|
|
|
b80fe6 |
- additional capabilities in the host's user namespace. Defaults to off.</para>
|
|
|
b80fe6 |
+ <listitem><para>Takes a boolean argument or one of <literal>self</literal> or
|
|
|
b80fe6 |
+ <literal>identity</literal>. Defaults to off. If enabled, sets up a new user namespace for the
|
|
|
b80fe6 |
+ executed processes and configures a user and group mapping. If set to a true value or
|
|
|
b80fe6 |
+ <literal>self</literal>, a minimal user and group mapping is configured that maps the
|
|
|
b80fe6 |
+ <literal>root</literal> user and group as well as the unit's own user and group to themselves and
|
|
|
b80fe6 |
+ everything else to the <literal>nobody</literal> user and group. This is useful to securely detach
|
|
|
b80fe6 |
+ the user and group databases used by the unit from the rest of the system, and thus to create an
|
|
|
b80fe6 |
+ effective sandbox environment. All files, directories, processes, IPC objects and other resources
|
|
|
b80fe6 |
+ owned by users/groups not equaling <literal>root</literal> or the unit's own will stay visible from
|
|
|
b80fe6 |
+ within the unit but appear owned by the <literal>nobody</literal> user and group. </para>
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
+ <para>If the parameter is <literal>identity</literal>, user namespacing is set up with an identity
|
|
|
b80fe6 |
+ mapping for the first 65536 UIDs/GIDs. Any UIDs/GIDs above 65536 will be mapped to the
|
|
|
b80fe6 |
+ <literal>nobody</literal> user and group, respectively. While this does not provide UID/GID isolation,
|
|
|
b80fe6 |
+ since all UIDs/GIDs are chosen identically it does provide process capability isolation, and hence is
|
|
|
b80fe6 |
+ often a good choice if proper user namespacing with distinct UID maps is not appropriate.</para>
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
+ <para>If this mode is enabled, all unit processes are run without privileges in the host user
|
|
|
b80fe6 |
+ namespace (regardless if the unit's own user/group is <literal>root</literal> or not). Specifically
|
|
|
b80fe6 |
+ this means that the process will have zero process capabilities on the host's user namespace, but
|
|
|
b80fe6 |
+ full capabilities within the service's user namespace. Settings such as
|
|
|
b80fe6 |
+ <varname>CapabilityBoundingSet=</varname> will affect only the latter, and there's no way to acquire
|
|
|
b80fe6 |
+ additional capabilities in the host's user namespace.</para>
|
|
|
b80fe6 |
|
|
|
b80fe6 |
<para>When this setting is set up by a per-user instance of the service manager, the mapping of the
|
|
|
b80fe6 |
<literal>root</literal> user and group to itself is omitted (unless the user manager is root).
|
|
|
b80fe6 |
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
|
|
|
142bf4 |
index b0d9402e53ca7..1aeba38df60a6 100644
|
|
|
b80fe6 |
--- a/src/core/dbus-execute.c
|
|
|
b80fe6 |
+++ b/src/core/dbus-execute.c
|
|
|
142bf4 |
@@ -58,6 +58,7 @@ static BUS_DEFINE_PROPERTY_GET(property_get_mount_apivfs, "b", ExecContext, exec
|
|
|
142bf4 |
static BUS_DEFINE_PROPERTY_GET2(property_get_ioprio_class, "i", ExecContext, exec_context_get_effective_ioprio, ioprio_prio_class);
|
|
|
b80fe6 |
static BUS_DEFINE_PROPERTY_GET2(property_get_ioprio_priority, "i", ExecContext, exec_context_get_effective_ioprio, ioprio_prio_data);
|
|
|
b80fe6 |
static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_empty_string, "s", NULL);
|
|
|
b80fe6 |
+static BUS_DEFINE_PROPERTY_GET_REF(property_get_private_users_ex, "s", PrivateUsers, private_users_to_string);
|
|
|
b80fe6 |
static BUS_DEFINE_PROPERTY_GET_REF(property_get_syslog_level, "i", int, LOG_PRI);
|
|
|
b80fe6 |
static BUS_DEFINE_PROPERTY_GET_REF(property_get_syslog_facility, "i", int, LOG_FAC);
|
|
|
b80fe6 |
static BUS_DEFINE_PROPERTY_GET(property_get_cpu_affinity_from_numa, "b", ExecContext, exec_context_get_cpu_affinity_from_numa);
|
|
|
142bf4 |
@@ -943,6 +944,21 @@ static int property_get_image_policy(
|
|
|
142bf4 |
return sd_bus_message_append(reply, "s", s);
|
|
|
b80fe6 |
}
|
|
|
b80fe6 |
|
|
|
b80fe6 |
+static int property_get_private_users(
|
|
|
b80fe6 |
+ sd_bus *bus,
|
|
|
b80fe6 |
+ const char *path,
|
|
|
b80fe6 |
+ const char *interface,
|
|
|
b80fe6 |
+ const char *property,
|
|
|
b80fe6 |
+ sd_bus_message *reply,
|
|
|
b80fe6 |
+ void *userdata,
|
|
|
b80fe6 |
+ sd_bus_error *error) {
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
+ PrivateUsers *p = ASSERT_PTR(userdata);
|
|
|
b80fe6 |
+ int b = *p != PRIVATE_USERS_OFF;
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
+ return sd_bus_message_append_basic(reply, 'b', &b);
|
|
|
b80fe6 |
+}
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
const sd_bus_vtable bus_exec_vtable[] = {
|
|
|
b80fe6 |
SD_BUS_VTABLE_START(0),
|
|
|
b80fe6 |
SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST),
|
|
|
142bf4 |
@@ -1063,7 +1079,8 @@ const sd_bus_vtable bus_exec_vtable[] = {
|
|
|
b80fe6 |
SD_BUS_PROPERTY("ProtectKernelLogs", "b", bus_property_get_bool, offsetof(ExecContext, protect_kernel_logs), SD_BUS_VTABLE_PROPERTY_CONST),
|
|
|
b80fe6 |
SD_BUS_PROPERTY("ProtectControlGroups", "b", bus_property_get_bool, offsetof(ExecContext, protect_control_groups), SD_BUS_VTABLE_PROPERTY_CONST),
|
|
|
b80fe6 |
SD_BUS_PROPERTY("PrivateNetwork", "b", bus_property_get_bool, offsetof(ExecContext, private_network), SD_BUS_VTABLE_PROPERTY_CONST),
|
|
|
b80fe6 |
- SD_BUS_PROPERTY("PrivateUsers", "b", bus_property_get_bool, offsetof(ExecContext, private_users), SD_BUS_VTABLE_PROPERTY_CONST),
|
|
|
b80fe6 |
+ SD_BUS_PROPERTY("PrivateUsers", "b", property_get_private_users, offsetof(ExecContext, private_users), SD_BUS_VTABLE_PROPERTY_CONST),
|
|
|
b80fe6 |
+ SD_BUS_PROPERTY("PrivateUsersEx", "s", property_get_private_users_ex, offsetof(ExecContext, private_users), SD_BUS_VTABLE_PROPERTY_CONST),
|
|
|
b80fe6 |
SD_BUS_PROPERTY("PrivateMounts", "b", bus_property_get_tristate, offsetof(ExecContext, private_mounts), SD_BUS_VTABLE_PROPERTY_CONST),
|
|
|
b80fe6 |
SD_BUS_PROPERTY("PrivateIPC", "b", bus_property_get_bool, offsetof(ExecContext, private_ipc), SD_BUS_VTABLE_PROPERTY_CONST),
|
|
|
b80fe6 |
SD_BUS_PROPERTY("ProtectHome", "s", property_get_protect_home, offsetof(ExecContext, protect_home), SD_BUS_VTABLE_PROPERTY_CONST),
|
|
|
142bf4 |
@@ -1738,6 +1755,41 @@ int bus_exec_context_set_transient_property(
|
|
|
142bf4 |
if (streq(name, "PrivateTmp"))
|
|
|
142bf4 |
return bus_set_transient_bool(u, name, &c->private_tmp, message, flags, error);
|
|
|
b80fe6 |
|
|
|
b80fe6 |
+ if (streq(name, "PrivateUsers")) {
|
|
|
b80fe6 |
+ int v;
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
+ r = sd_bus_message_read(message, "b", &v);
|
|
|
b80fe6 |
+ if (r < 0)
|
|
|
b80fe6 |
+ return r;
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
|
|
b80fe6 |
+ c->private_users = v ? PRIVATE_USERS_SELF : PRIVATE_USERS_OFF;
|
|
|
b80fe6 |
+ (void) unit_write_settingf(u, flags, name, "%s=%s", name, yes_no(v));
|
|
|
b80fe6 |
+ }
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
+ return 1;
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
+ } else if (streq(name, "PrivateUsersEx")) {
|
|
|
b80fe6 |
+ const char *s;
|
|
|
b80fe6 |
+ PrivateUsers t;
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
+ r = sd_bus_message_read(message, "s", &s);
|
|
|
b80fe6 |
+ if (r < 0)
|
|
|
b80fe6 |
+ return r;
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
+ t = private_users_from_string(s);
|
|
|
b80fe6 |
+ if (t < 0)
|
|
|
b80fe6 |
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s setting: %s", name, s);
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
|
|
b80fe6 |
+ c->private_users = t;
|
|
|
b80fe6 |
+ (void) unit_write_settingf(u, flags, name, "PrivateUsers=%s",
|
|
|
b80fe6 |
+ private_users_to_string(c->private_users));
|
|
|
b80fe6 |
+ }
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
+ return 1;
|
|
|
b80fe6 |
+ }
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
if (streq(name, "PrivateDevices"))
|
|
|
b80fe6 |
return bus_set_transient_bool(u, name, &c->private_devices, message, flags, error);
|
|
|
b80fe6 |
|
|
|
142bf4 |
@@ -1753,9 +1805,6 @@ int bus_exec_context_set_transient_property(
|
|
|
b80fe6 |
if (streq(name, "PrivateIPC"))
|
|
|
b80fe6 |
return bus_set_transient_bool(u, name, &c->private_ipc, message, flags, error);
|
|
|
b80fe6 |
|
|
|
b80fe6 |
- if (streq(name, "PrivateUsers"))
|
|
|
b80fe6 |
- return bus_set_transient_bool(u, name, &c->private_users, message, flags, error);
|
|
|
b80fe6 |
-
|
|
|
b80fe6 |
if (streq(name, "NoNewPrivileges"))
|
|
|
b80fe6 |
return bus_set_transient_bool(u, name, &c->no_new_privileges, message, flags, error);
|
|
|
b80fe6 |
|
|
|
b80fe6 |
diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c
|
|
|
142bf4 |
index 5850a595f0475..1f4a9923df7e0 100644
|
|
|
b80fe6 |
--- a/src/core/exec-invoke.c
|
|
|
b80fe6 |
+++ b/src/core/exec-invoke.c
|
|
|
142bf4 |
@@ -2080,7 +2080,7 @@ static int build_pass_environment(const ExecContext *c, char ***ret) {
|
|
|
b80fe6 |
return 0;
|
|
|
b80fe6 |
}
|
|
|
b80fe6 |
|
|
|
b80fe6 |
-static int setup_private_users(uid_t ouid, gid_t ogid, uid_t uid, gid_t gid) {
|
|
|
b80fe6 |
+static int setup_private_users(PrivateUsers private_users, uid_t ouid, gid_t ogid, uid_t uid, gid_t gid) {
|
|
|
b80fe6 |
_cleanup_free_ char *uid_map = NULL, *gid_map = NULL;
|
|
|
b80fe6 |
_cleanup_close_pair_ int errno_pipe[2] = EBADF_PAIR;
|
|
|
b80fe6 |
_cleanup_close_ int unshare_ready_fd = -EBADF;
|
|
|
142bf4 |
@@ -2099,33 +2099,48 @@ static int setup_private_users(uid_t ouid, gid_t ogid, uid_t uid, gid_t gid) {
|
|
|
b80fe6 |
* For unprivileged users (i.e. without capabilities), the root to root mapping is excluded. As such, it
|
|
|
b80fe6 |
* does not need CAP_SETUID to write the single line mapping to itself. */
|
|
|
b80fe6 |
|
|
|
b80fe6 |
+ if (private_users == PRIVATE_USERS_OFF)
|
|
|
b80fe6 |
+ return 0;
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
+ if (private_users == PRIVATE_USERS_IDENTITY) {
|
|
|
b80fe6 |
+ uid_map = strdup("0 0 65536\n");
|
|
|
b80fe6 |
+ if (!uid_map)
|
|
|
b80fe6 |
+ return -ENOMEM;
|
|
|
b80fe6 |
/* Can only set up multiple mappings with CAP_SETUID. */
|
|
|
b80fe6 |
- if (have_effective_cap(CAP_SETUID) > 0 && uid != ouid && uid_is_valid(uid))
|
|
|
b80fe6 |
+ } else if (have_effective_cap(CAP_SETUID) > 0 && uid != ouid && uid_is_valid(uid)) {
|
|
|
b80fe6 |
r = asprintf(&uid_map,
|
|
|
b80fe6 |
UID_FMT " " UID_FMT " 1\n" /* Map $OUID → $OUID */
|
|
|
b80fe6 |
UID_FMT " " UID_FMT " 1\n", /* Map $UID → $UID */
|
|
|
b80fe6 |
ouid, ouid, uid, uid);
|
|
|
b80fe6 |
- else
|
|
|
b80fe6 |
+ if (r < 0)
|
|
|
b80fe6 |
+ return -ENOMEM;
|
|
|
b80fe6 |
+ } else {
|
|
|
b80fe6 |
r = asprintf(&uid_map,
|
|
|
b80fe6 |
UID_FMT " " UID_FMT " 1\n", /* Map $OUID → $OUID */
|
|
|
b80fe6 |
ouid, ouid);
|
|
|
b80fe6 |
+ if (r < 0)
|
|
|
b80fe6 |
+ return -ENOMEM;
|
|
|
b80fe6 |
+ }
|
|
|
b80fe6 |
|
|
|
142bf4 |
- if (r < 0)
|
|
|
142bf4 |
- return -ENOMEM;
|
|
|
142bf4 |
-
|
|
|
b80fe6 |
+ if (private_users == PRIVATE_USERS_IDENTITY) {
|
|
|
b80fe6 |
+ gid_map = strdup("0 0 65536\n");
|
|
|
b80fe6 |
+ if (!gid_map)
|
|
|
b80fe6 |
+ return -ENOMEM;
|
|
|
b80fe6 |
/* Can only set up multiple mappings with CAP_SETGID. */
|
|
|
b80fe6 |
- if (have_effective_cap(CAP_SETGID) > 0 && gid != ogid && gid_is_valid(gid))
|
|
|
b80fe6 |
+ } else if (have_effective_cap(CAP_SETGID) > 0 && gid != ogid && gid_is_valid(gid)) {
|
|
|
b80fe6 |
r = asprintf(&gid_map,
|
|
|
b80fe6 |
GID_FMT " " GID_FMT " 1\n" /* Map $OGID → $OGID */
|
|
|
b80fe6 |
GID_FMT " " GID_FMT " 1\n", /* Map $GID → $GID */
|
|
|
b80fe6 |
ogid, ogid, gid, gid);
|
|
|
b80fe6 |
- else
|
|
|
b80fe6 |
+ if (r < 0)
|
|
|
b80fe6 |
+ return -ENOMEM;
|
|
|
b80fe6 |
+ } else {
|
|
|
b80fe6 |
r = asprintf(&gid_map,
|
|
|
b80fe6 |
GID_FMT " " GID_FMT " 1\n", /* Map $OGID -> $OGID */
|
|
|
b80fe6 |
ogid, ogid);
|
|
|
142bf4 |
-
|
|
|
b80fe6 |
- if (r < 0)
|
|
|
b80fe6 |
- return -ENOMEM;
|
|
|
b80fe6 |
+ if (r < 0)
|
|
|
b80fe6 |
+ return -ENOMEM;
|
|
|
b80fe6 |
+ }
|
|
|
b80fe6 |
|
|
|
b80fe6 |
/* Create a communication channel so that the parent can tell the child when it finished creating the user
|
|
|
b80fe6 |
* namespace. */
|
|
|
142bf4 |
@@ -2236,7 +2251,7 @@ static int setup_private_users(uid_t ouid, gid_t ogid, uid_t uid, gid_t gid) {
|
|
|
b80fe6 |
if (r != EXIT_SUCCESS) /* If something strange happened with the child, let's consider this fatal, too */
|
|
|
b80fe6 |
return -EIO;
|
|
|
b80fe6 |
|
|
|
b80fe6 |
- return 0;
|
|
|
b80fe6 |
+ return 1;
|
|
|
b80fe6 |
}
|
|
|
b80fe6 |
|
|
|
b80fe6 |
static int create_many_symlinks(const char *root, const char *source, char **symlinks) {
|
|
|
142bf4 |
@@ -3865,7 +3880,7 @@ static bool exec_context_need_unprivileged_private_users(
|
|
|
b80fe6 |
if (params->runtime_scope != RUNTIME_SCOPE_USER)
|
|
|
b80fe6 |
return false;
|
|
|
b80fe6 |
|
|
|
b80fe6 |
- return context->private_users ||
|
|
|
b80fe6 |
+ return context->private_users != PRIVATE_USERS_OFF ||
|
|
|
142bf4 |
context->private_tmp ||
|
|
|
b80fe6 |
context->private_devices ||
|
|
|
b80fe6 |
context->private_network ||
|
|
|
142bf4 |
@@ -4720,18 +4735,23 @@ int exec_invoke(
|
|
|
b80fe6 |
/* If we're unprivileged, set up the user namespace first to enable use of the other namespaces.
|
|
|
b80fe6 |
* Users with CAP_SYS_ADMIN can set up user namespaces last because they will be able to
|
|
|
b80fe6 |
* set up all of the other namespaces (i.e. network, mount, UTS) without a user namespace. */
|
|
|
b80fe6 |
+ PrivateUsers pu = context->private_users;
|
|
|
b80fe6 |
+ if (pu == PRIVATE_USERS_OFF)
|
|
|
b80fe6 |
+ pu = PRIVATE_USERS_SELF;
|
|
|
b80fe6 |
|
|
|
b80fe6 |
- r = setup_private_users(saved_uid, saved_gid, uid, gid);
|
|
|
b80fe6 |
+ r = setup_private_users(pu, saved_uid, saved_gid, uid, gid);
|
|
|
b80fe6 |
/* If it was requested explicitly and we can't set it up, fail early. Otherwise, continue and let
|
|
|
b80fe6 |
* the actual requested operations fail (or silently continue). */
|
|
|
b80fe6 |
- if (r < 0 && context->private_users) {
|
|
|
b80fe6 |
+ if (r < 0 && context->private_users != PRIVATE_USERS_OFF) {
|
|
|
b80fe6 |
*exit_status = EXIT_USER;
|
|
|
b80fe6 |
return log_exec_error_errno(context, params, r, "Failed to set up user namespacing for unprivileged user: %m");
|
|
|
b80fe6 |
}
|
|
|
b80fe6 |
if (r < 0)
|
|
|
b80fe6 |
log_exec_info_errno(context, params, r, "Failed to set up user namespacing for unprivileged user, ignoring: %m");
|
|
|
b80fe6 |
- else
|
|
|
b80fe6 |
+ else {
|
|
|
b80fe6 |
+ assert(r > 0);
|
|
|
b80fe6 |
userns_set_up = true;
|
|
|
b80fe6 |
+ }
|
|
|
b80fe6 |
}
|
|
|
b80fe6 |
|
|
|
b80fe6 |
if (exec_needs_network_namespace(context) && runtime && runtime->shared && runtime->shared->netns_storage_socket[0] >= 0) {
|
|
|
142bf4 |
@@ -4844,8 +4864,8 @@ int exec_invoke(
|
|
|
b80fe6 |
* case of mount namespaces being less privileged when the mount point list is copied from a
|
|
|
b80fe6 |
* different user namespace). */
|
|
|
b80fe6 |
|
|
|
b80fe6 |
- if (needs_sandboxing && context->private_users && !userns_set_up) {
|
|
|
b80fe6 |
- r = setup_private_users(saved_uid, saved_gid, uid, gid);
|
|
|
b80fe6 |
+ if (needs_sandboxing && !userns_set_up) {
|
|
|
b80fe6 |
+ r = setup_private_users(context->private_users, saved_uid, saved_gid, uid, gid);
|
|
|
b80fe6 |
if (r < 0) {
|
|
|
b80fe6 |
*exit_status = EXIT_USER;
|
|
|
b80fe6 |
return log_exec_error_errno(context, params, r, "Failed to set up user namespacing: %m");
|
|
|
b80fe6 |
diff --git a/src/core/execute-serialize.c b/src/core/execute-serialize.c
|
|
|
142bf4 |
index 41b31e9ae3f21..867a74419da98 100644
|
|
|
b80fe6 |
--- a/src/core/execute-serialize.c
|
|
|
b80fe6 |
+++ b/src/core/execute-serialize.c
|
|
|
142bf4 |
@@ -1880,7 +1880,7 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
|
|
|
b80fe6 |
if (r < 0)
|
|
|
b80fe6 |
return r;
|
|
|
b80fe6 |
|
|
|
b80fe6 |
- r = serialize_bool_elide(f, "exec-context-private-users", c->private_users);
|
|
|
b80fe6 |
+ r = serialize_item(f, "exec-context-private-users", private_users_to_string(c->private_users));
|
|
|
b80fe6 |
if (r < 0)
|
|
|
b80fe6 |
return r;
|
|
|
b80fe6 |
|
|
|
142bf4 |
@@ -2772,10 +2772,9 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
|
|
|
b80fe6 |
return r;
|
|
|
b80fe6 |
c->private_network = r;
|
|
|
b80fe6 |
} else if ((val = startswith(l, "exec-context-private-users="))) {
|
|
|
b80fe6 |
- r = parse_boolean(val);
|
|
|
b80fe6 |
- if (r < 0)
|
|
|
b80fe6 |
- return r;
|
|
|
b80fe6 |
- c->private_users = r;
|
|
|
b80fe6 |
+ c->private_users = private_users_from_string(val);
|
|
|
b80fe6 |
+ if (c->private_users < 0)
|
|
|
b80fe6 |
+ return -EINVAL;
|
|
|
b80fe6 |
} else if ((val = startswith(l, "exec-context-private-ipc="))) {
|
|
|
b80fe6 |
r = parse_boolean(val);
|
|
|
b80fe6 |
if (r < 0)
|
|
|
b80fe6 |
diff --git a/src/core/execute.c b/src/core/execute.c
|
|
|
142bf4 |
index f74665fcd7b9a..c2241803b763a 100644
|
|
|
b80fe6 |
--- a/src/core/execute.c
|
|
|
b80fe6 |
+++ b/src/core/execute.c
|
|
|
142bf4 |
@@ -973,7 +973,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
|
|
|
b80fe6 |
prefix, yes_no(c->protect_clock),
|
|
|
b80fe6 |
prefix, yes_no(c->protect_control_groups),
|
|
|
b80fe6 |
prefix, yes_no(c->private_network),
|
|
|
b80fe6 |
- prefix, yes_no(c->private_users),
|
|
|
b80fe6 |
+ prefix, private_users_to_string(c->private_users),
|
|
|
b80fe6 |
prefix, protect_home_to_string(c->protect_home),
|
|
|
b80fe6 |
prefix, protect_system_to_string(c->protect_system),
|
|
|
b80fe6 |
prefix, yes_no(exec_context_get_effective_mount_apivfs(c)),
|
|
|
b80fe6 |
diff --git a/src/core/execute.h b/src/core/execute.h
|
|
|
142bf4 |
index 107ae2524387c..603feac0d6027 100644
|
|
|
b80fe6 |
--- a/src/core/execute.h
|
|
|
b80fe6 |
+++ b/src/core/execute.h
|
|
|
142bf4 |
@@ -317,7 +317,7 @@ struct ExecContext {
|
|
|
142bf4 |
bool private_tmp;
|
|
|
b80fe6 |
bool private_network;
|
|
|
b80fe6 |
bool private_devices;
|
|
|
b80fe6 |
- bool private_users;
|
|
|
b80fe6 |
+ PrivateUsers private_users;
|
|
|
b80fe6 |
bool private_ipc;
|
|
|
b80fe6 |
bool protect_kernel_tunables;
|
|
|
b80fe6 |
bool protect_kernel_modules;
|
|
|
b80fe6 |
diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in
|
|
|
142bf4 |
index df219d86dfeaa..213b36ec05eee 100644
|
|
|
b80fe6 |
--- a/src/core/load-fragment-gperf.gperf.in
|
|
|
b80fe6 |
+++ b/src/core/load-fragment-gperf.gperf.in
|
|
|
b80fe6 |
@@ -130,7 +130,7 @@
|
|
|
b80fe6 |
{{type}}.IPCNamespacePath, config_parse_unit_path_printf, 0, offsetof({{type}}, exec_context.ipc_namespace_path)
|
|
|
b80fe6 |
{{type}}.LogNamespace, config_parse_log_namespace, 0, offsetof({{type}}, exec_context)
|
|
|
b80fe6 |
{{type}}.PrivateNetwork, config_parse_bool, 0, offsetof({{type}}, exec_context.private_network)
|
|
|
b80fe6 |
-{{type}}.PrivateUsers, config_parse_bool, 0, offsetof({{type}}, exec_context.private_users)
|
|
|
b80fe6 |
+{{type}}.PrivateUsers, config_parse_private_users, 0, offsetof({{type}}, exec_context.private_users)
|
|
|
b80fe6 |
{{type}}.PrivateMounts, config_parse_tristate, 0, offsetof({{type}}, exec_context.private_mounts)
|
|
|
b80fe6 |
{{type}}.PrivateIPC, config_parse_bool, 0, offsetof({{type}}, exec_context.private_ipc)
|
|
|
b80fe6 |
{{type}}.ProtectSystem, config_parse_protect_system, 0, offsetof({{type}}, exec_context.protect_system)
|
|
|
b80fe6 |
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
|
|
|
142bf4 |
index a1a116a439774..23b5a66742da7 100644
|
|
|
b80fe6 |
--- a/src/core/load-fragment.c
|
|
|
b80fe6 |
+++ b/src/core/load-fragment.c
|
|
|
142bf4 |
@@ -132,6 +132,7 @@ DEFINE_CONFIG_PARSE_ENUM(config_parse_collect_mode, collect_mode, CollectMode, "
|
|
|
142bf4 |
DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
|
|
|
142bf4 |
DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode, "Failed to parse keyring mode");
|
|
|
142bf4 |
DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_proc, protect_proc, ProtectProc, "Failed to parse /proc/ protection mode");
|
|
|
142bf4 |
+DEFINE_CONFIG_PARSE_ENUM(config_parse_private_users, private_users, PrivateUsers, "Failed to parse private users mode");
|
|
|
142bf4 |
DEFINE_CONFIG_PARSE_ENUM(config_parse_proc_subset, proc_subset, ProcSubset, "Failed to parse /proc/ subset mode");
|
|
|
142bf4 |
DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode");
|
|
|
142bf4 |
DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
|
|
|
b80fe6 |
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
|
|
|
142bf4 |
index 005b91510608f..b6741c9207ab0 100644
|
|
|
b80fe6 |
--- a/src/core/load-fragment.h
|
|
|
b80fe6 |
+++ b/src/core/load-fragment.h
|
|
|
142bf4 |
@@ -113,6 +113,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_import_credential);
|
|
|
142bf4 |
CONFIG_PARSER_PROTOTYPE(config_parse_set_status);
|
|
|
b80fe6 |
CONFIG_PARSER_PROTOTYPE(config_parse_namespace_path_strv);
|
|
|
b80fe6 |
CONFIG_PARSER_PROTOTYPE(config_parse_temporary_filesystems);
|
|
|
b80fe6 |
+CONFIG_PARSER_PROTOTYPE(config_parse_private_users);
|
|
|
b80fe6 |
CONFIG_PARSER_PROTOTYPE(config_parse_cpu_quota);
|
|
|
b80fe6 |
CONFIG_PARSER_PROTOTYPE(config_parse_allowed_cpuset);
|
|
|
b80fe6 |
CONFIG_PARSER_PROTOTYPE(config_parse_protect_home);
|
|
|
b80fe6 |
diff --git a/src/core/namespace.c b/src/core/namespace.c
|
|
|
142bf4 |
index b92bb012eee31..052a4452c12b7 100644
|
|
|
b80fe6 |
--- a/src/core/namespace.c
|
|
|
b80fe6 |
+++ b/src/core/namespace.c
|
|
|
142bf4 |
@@ -3080,3 +3080,11 @@ static const char* const proc_subset_table[_PROC_SUBSET_MAX] = {
|
|
|
b80fe6 |
};
|
|
|
b80fe6 |
|
|
|
142bf4 |
DEFINE_STRING_TABLE_LOOKUP(proc_subset, ProcSubset);
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
+static const char* const private_users_table[_PRIVATE_USERS_MAX] = {
|
|
|
b80fe6 |
+ [PRIVATE_USERS_OFF] = "off",
|
|
|
b80fe6 |
+ [PRIVATE_USERS_SELF] = "self",
|
|
|
b80fe6 |
+ [PRIVATE_USERS_IDENTITY] = "identity",
|
|
|
b80fe6 |
+};
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(private_users, PrivateUsers, PRIVATE_USERS_SELF);
|
|
|
b80fe6 |
diff --git a/src/core/namespace.h b/src/core/namespace.h
|
|
|
142bf4 |
index 921716bf3ec93..d10d7f440ad8b 100644
|
|
|
b80fe6 |
--- a/src/core/namespace.h
|
|
|
b80fe6 |
+++ b/src/core/namespace.h
|
|
|
142bf4 |
@@ -53,6 +53,14 @@ typedef enum ProcSubset {
|
|
|
142bf4 |
_PROC_SUBSET_INVALID = -EINVAL,
|
|
|
142bf4 |
} ProcSubset;
|
|
|
b80fe6 |
|
|
|
b80fe6 |
+typedef enum PrivateUsers {
|
|
|
b80fe6 |
+ PRIVATE_USERS_OFF,
|
|
|
b80fe6 |
+ PRIVATE_USERS_SELF,
|
|
|
b80fe6 |
+ PRIVATE_USERS_IDENTITY,
|
|
|
b80fe6 |
+ _PRIVATE_USERS_MAX,
|
|
|
b80fe6 |
+ _PRIVATE_USERS_INVALID = -EINVAL,
|
|
|
b80fe6 |
+} PrivateUsers;
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
struct BindMount {
|
|
|
b80fe6 |
char *source;
|
|
|
b80fe6 |
char *destination;
|
|
|
142bf4 |
@@ -184,6 +192,9 @@ ProtectProc protect_proc_from_string(const char *s) _pure_;
|
|
|
142bf4 |
const char* proc_subset_to_string(ProcSubset i) _const_;
|
|
|
142bf4 |
ProcSubset proc_subset_from_string(const char *s) _pure_;
|
|
|
b80fe6 |
|
|
|
b80fe6 |
+const char* private_users_to_string(PrivateUsers i) _const_;
|
|
|
b80fe6 |
+PrivateUsers private_users_from_string(const char *s) _pure_;
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
void bind_mount_free_many(BindMount *b, size_t n);
|
|
|
b80fe6 |
int bind_mount_add(BindMount **b, size_t *n, const BindMount *item);
|
|
|
b80fe6 |
|
|
|
b80fe6 |
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
|
|
|
142bf4 |
index da83422524fa4..ff24ac5dd40dc 100644
|
|
|
b80fe6 |
--- a/src/shared/bus-unit-util.c
|
|
|
b80fe6 |
+++ b/src/shared/bus-unit-util.c
|
|
|
142bf4 |
@@ -1037,6 +1037,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
|
|
|
142bf4 |
"SyslogIdentifier",
|
|
|
b80fe6 |
"ProtectSystem",
|
|
|
b80fe6 |
"ProtectHome",
|
|
|
b80fe6 |
+ "PrivateUsersEx",
|
|
|
b80fe6 |
"SELinuxContext",
|
|
|
b80fe6 |
"RootImage",
|
|
|
b80fe6 |
"RootVerity",
|
|
|
b80fe6 |
diff --git a/test/units/TEST-07-PID1.private-users.sh b/test/units/TEST-07-PID1.private-users.sh
|
|
|
b80fe6 |
new file mode 100755
|
|
|
b80fe6 |
index 0000000000000..2475b5d365d59
|
|
|
b80fe6 |
--- /dev/null
|
|
|
b80fe6 |
+++ b/test/units/TEST-07-PID1.private-users.sh
|
|
|
b80fe6 |
@@ -0,0 +1,12 @@
|
|
|
b80fe6 |
+#!/usr/bin/env bash
|
|
|
b80fe6 |
+# SPDX-License-Identifier: LGPL-2.1-or-later
|
|
|
b80fe6 |
+# shellcheck disable=SC2016
|
|
|
b80fe6 |
+set -eux
|
|
|
b80fe6 |
+set -o pipefail
|
|
|
b80fe6 |
+
|
|
|
b80fe6 |
+systemd-run -p PrivateUsers=yes --wait bash -c 'test "$(cat /proc/self/uid_map)" == " 0 0 1"'
|
|
|
b80fe6 |
+systemd-run -p PrivateUsers=yes --wait bash -c 'test "$(cat /proc/self/gid_map)" == " 0 0 1"'
|
|
|
b80fe6 |
+systemd-run -p PrivateUsersEx=self --wait bash -c 'test "$(cat /proc/self/uid_map)" == " 0 0 1"'
|
|
|
b80fe6 |
+systemd-run -p PrivateUsersEx=self --wait bash -c 'test "$(cat /proc/self/gid_map)" == " 0 0 1"'
|
|
|
b80fe6 |
+systemd-run -p PrivateUsersEx=identity --wait bash -c 'test "$(cat /proc/self/uid_map)" == " 0 0 65536"'
|
|
|
b80fe6 |
+systemd-run -p PrivateUsersEx=identity --wait bash -c 'test "$(cat /proc/self/gid_map)" == " 0 0 65536"'
|