@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b RootEphemeral = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly s RootMStack = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as ExtensionDirectories = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(sba(ss)) ExtensionImages = [...];
<variablelist class="dbus-property" generated="True" extra-ref="RootEphemeral"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="RootMStack"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="ExtensionDirectories"/>
<variablelist class="dbus-property" generated="True" extra-ref="ExtensionImages"/>
<varname>MountImages</varname>
<varname>ExtensionImages</varname>
<varname>ExtensionDirectories</varname>
+ <varname>RootMStack</varname>
see systemd.exec(5) for their meaning.</para>
<para><varname>MemoryAvailable</varname> takes into account unit's and parents' <literal>MemoryMax</literal>
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b RootEphemeral = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly s RootMStack = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as ExtensionDirectories = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(sba(ss)) ExtensionImages = [...];
<variablelist class="dbus-property" generated="True" extra-ref="RootEphemeral"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="RootMStack"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="ExtensionDirectories"/>
<variablelist class="dbus-property" generated="True" extra-ref="ExtensionImages"/>
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b RootEphemeral = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly s RootMStack = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as ExtensionDirectories = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(sba(ss)) ExtensionImages = [...];
<variablelist class="dbus-property" generated="True" extra-ref="RootEphemeral"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="RootMStack"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="ExtensionDirectories"/>
<variablelist class="dbus-property" generated="True" extra-ref="ExtensionImages"/>
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b RootEphemeral = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly s RootMStack = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as ExtensionDirectories = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(sba(ss)) ExtensionImages = [...];
<variablelist class="dbus-property" generated="True" extra-ref="RootEphemeral"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="RootMStack"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="ExtensionDirectories"/>
<variablelist class="dbus-property" generated="True" extra-ref="ExtensionImages"/>
<varname>ManagedOOMKills</varname>,
<varname>ExecReloadPost</varname>, and
<varname>ExecReloadPostEx</varname> were added in version 259.</para>
- <para><varname>BindNetworkInterface</varname>,
- <varname>MemoryTHP</varname>, and
- <varname>RefreshOnReload</varname> were added in version 260.</para>
+ <para><varname>BindNetworkInterface</varname>, <varname>MemoryTHP</varname>,
+ <varname>RefreshOnReload</varname>, and <varname>RootMStack</varname> were added in version 260.</para>
</refsect2>
<refsect2>
<title>Socket Unit Objects</title>
<para><varname>UserNamespacePath</varname>,
<varname>OOMKills</varname>, and
<varname>ManagedOOMKills</varname> were added in 259.</para>
- <para><varname>BindNetworkInterface</varname>, and
- <varname>MemoryTHP</varname> were added in version 260.</para>
+ <para><varname>BindNetworkInterface</varname> <varname>MemoryTHP</varname>, and
+ <varname>RootMStack</varname> were added in version 260.</para>
</refsect2>
<refsect2>
<title>Mount Unit Objects</title>
<para><varname>UserNamespacePath</varname>,
<varname>OOMKills</varname>, and
<varname>ManagedOOMKills</varname> were added in 259.</para>
- <para><varname>BindNetworkInterface</varname>, and
- <varname>MemoryTHP</varname> were added in version 260.</para>
+ <para><varname>BindNetworkInterface</varname> <varname>MemoryTHP</varname>, and
+ <varname>RootMStack</varname> were added in version 260.</para>
</refsect2>
<refsect2>
<title>Swap Unit Objects</title>
<para><varname>UserNamespacePath</varname>,
<varname>OOMKills</varname>, and
<varname>ManagedOOMKills</varname> were added in 259.</para>
- <para><varname>BindNetworkInterface</varname>, and
- <varname>MemoryTHP</varname> were added in version 260.</para>
+ <para><varname>BindNetworkInterface</varname>, <varname>MemoryTHP</varname>, and
+ <varname>RootMStack</varname> were added in version 260.</para>
</refsect2>
<refsect2>
<title>Slice Unit Objects</title>
SD_BUS_PROPERTY("RootHashSignaturePath", "s", NULL, offsetof(ExecContext, root_hash_sig_path), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootVerity", "s", NULL, offsetof(ExecContext, root_verity), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootEphemeral", "b", bus_property_get_bool, offsetof(ExecContext, root_ephemeral), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RootMStack", "s", NULL, offsetof(ExecContext, root_mstack), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ExtensionDirectories", "as", NULL, offsetof(ExecContext, extension_directories), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ExtensionImages", "a(sba(ss))", property_get_extension_images, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("MountImages", "a(ssba(ss))", property_get_mount_images, 0, SD_BUS_VTABLE_PROPERTY_CONST),
if (streq(name, "RootImage"))
return bus_set_transient_path(u, name, &c->root_image, message, flags, reterr_error);
+ if (streq(name, "RootMStack"))
+ return bus_set_transient_path(u, name, &c->root_mstack, message, flags, reterr_error);
+
if (streq(name, "RootImageOptions")) {
_cleanup_(mount_options_free_allp) MountOptions *options = NULL;
_cleanup_free_ char *format_str = NULL;
if (r < 0)
return r;
+ r = serialize_item_escaped(f, "exec-context-root-mstack", c->root_mstack);
+ if (r < 0)
+ return r;
+
r = serialize_item_format(f, "exec-context-umask", "%04o", c->umask);
if (r < 0)
return r;
if (r < 0)
return r;
c->root_ephemeral = r;
+ } else if ((val = startswith(l, "exec-context-root-mstack="))) {
+ ssize_t k;
+ char *p;
+
+ k = cunescape(val, 0, &p);
+ if (k < 0)
+ return k;
+ free_and_replace(c->root_mstack, p);
} else if ((val = startswith(l, "exec-context-umask="))) {
r = parse_mode(val, &c->umask);
if (r < 0)
assert(context);
- if (context->root_image)
+ if (context->root_image ||
+ context->root_mstack)
return true;
if (context->root_directory_as_fd)
iovec_done(&c->root_hash_sig);
c->root_hash_sig_path = mfree(c->root_hash_sig_path);
c->root_verity = mfree(c->root_verity);
+ c->root_mstack = mfree(c->root_mstack);
c->tty_path = mfree(c->tty_path);
c->syslog_identifier = mfree(c->syslog_identifier);
c->user = mfree(c->user);
if (c->root_verity)
fprintf(f, "%sRootVerity: %s\n", prefix, c->root_verity);
+ if (c->root_mstack)
+ fprintf(f, "%sRootMStack: %s\n", prefix, c->root_mstack);
+
STRV_FOREACH(e, c->environment)
fprintf(f, "%sEnvironment: %s\n", prefix, *e);
bool exec_context_with_rootfs(const ExecContext *c) {
assert(c);
- /* Checks if RootDirectory=, RootImage= or RootDirectoryFileDescriptor= are used */
+ /* Checks if RootDirectory=, RootImage=, RootMStack= or RootDirectoryFileDescriptor= are used */
- return !empty_or_root(c->root_directory) || c->root_image || c->root_directory_as_fd;
+ return !empty_or_root(c->root_directory) || c->root_image || c->root_directory_as_fd || c->root_mstack;
}
bool exec_context_with_rootfs_strict(const ExecContext *c) {
* true in more cases: when a root directory is explicitly configured, even if it's our usual
* root. */
- return c->root_directory || c->root_image || c->root_directory_as_fd;
+ return c->root_directory || c->root_image || c->root_directory_as_fd || c->root_mstack;
}
int exec_context_has_vpicked_extensions(const ExecContext *context) {
char **unset_environment;
struct rlimit *rlimit[_RLIMIT_MAX];
- char *working_directory, *root_directory, *root_image, *root_verity, *root_hash_path, *root_hash_sig_path;
+ char *working_directory;
+ char *root_directory;
+ char *root_image, *root_verity, *root_hash_path, *root_hash_sig_path;
struct iovec root_hash, root_hash_sig;
MountOptions *root_image_options;
+ char *root_mstack;
bool root_ephemeral;
bool working_directory_missing_ok:1;
bool working_directory_home:1;
{{type}}.RootHashSignature, config_parse_exec_root_hash_sig, 0, offsetof({{type}}, exec_context)
{{type}}.RootVerity, config_parse_unit_path_printf, true, offsetof({{type}}, exec_context.root_verity)
{{type}}.RootEphemeral, config_parse_bool, 0, offsetof({{type}}, exec_context.root_ephemeral)
+{{type}}.RootMStack, config_parse_unit_path_printf, true, offsetof({{type}}, exec_context.root_mstack)
{{type}}.ExtensionDirectories, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.extension_directories)
{{type}}.ExtensionImages, config_parse_extension_images, 0, offsetof({{type}}, exec_context)
{{type}}.ExtensionImagePolicy, config_parse_image_policy, 0, offsetof({{type}}, exec_context.extension_image_policy)
#include "mkdir-label.h"
#include "mount-util.h"
#include "mountpoint-util.h"
+#include "mstack.h"
#include "namespace.h"
#include "namespace-util.h"
#include "nsflags.h"
return 0;
}
+static bool namespace_with_rootfs(const NamespaceParameters *p) {
+ /* Returns true, if we have a root dir, root image or too mstack, and hence the root mount is
+ * changed */
+
+ return p->root_image || p->root_directory || p->root_directory_fd >= 0 || p->root_mstack;
+}
+
static int mount_private_dev(const MountEntry *m, const NamespaceParameters *p) {
static const char devnodes[] =
"/dev/null\0"
/* We assume /run/systemd/journal/ is available if not changing root, which isn't entirely accurate
* but shouldn't matter, as either way the user would get ENOENT when accessing /dev/log */
- if ((!p->root_image && !p->root_directory && p->root_directory_fd < 0) || p->bind_log_sockets) {
+ if (!namespace_with_rootfs(p) || p->bind_log_sockets) {
const char *devlog = strjoina(temporary_mount, "/dev/log");
if (symlink("/run/systemd/journal/dev-log", devlog) < 0)
log_debug_errno(errno,
return false;
}
+static bool namespace_read_only(const NamespaceParameters *p) {
+ assert(p);
+
+ return root_read_only(p->read_only_paths,
+ p->protect_system) &&
+ home_read_only(p->read_only_paths, p->inaccessible_paths, p->empty_directories,
+ p->bind_mounts, p->n_bind_mounts, p->temporary_filesystems, p->n_temporary_filesystems,
+ p->protect_home) &&
+ strv_isempty(p->read_write_paths);
+}
+
int setup_namespace(const NamespaceParameters *p, char **reterr_path) {
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
_cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
+ _cleanup_(mstack_freep) MStack *mstack = NULL;
_cleanup_strv_free_ char **hierarchies = NULL;
_cleanup_(mount_list_done) MountList ml = {};
_cleanup_close_ int userns_fd = -EBADF;
DISSECT_IMAGE_PIN_PARTITION_DEVICES |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY |
DISSECT_IMAGE_VERITY_SHARE;
+ MStackFlags mstack_flags = 0;
int r;
assert(p);
if (p->root_image) {
/* Make the whole image read-only if we can determine that we only access it in a read-only fashion. */
- if (root_read_only(p->read_only_paths,
- p->protect_system) &&
- home_read_only(p->read_only_paths, p->inaccessible_paths, p->empty_directories,
- p->bind_mounts, p->n_bind_mounts, p->temporary_filesystems, p->n_temporary_filesystems,
- p->protect_home) &&
- strv_isempty(p->read_write_paths))
+ if (namespace_read_only(p))
dissect_image_flags |= DISSECT_IMAGE_READ_ONLY;
SET_FLAG(dissect_image_flags, DISSECT_IMAGE_NO_PARTITION_TABLE, p->verity && p->verity->data_path);
return r;
}
}
+
+ } else if (p->root_mstack) {
+ if (namespace_read_only(p))
+ mstack_flags |= MSTACK_RDONLY;
+
+ r = mstack_load(p->root_mstack, /* dir_fd= */ -EBADF, &mstack);
+ if (r < 0)
+ return r;
+
+ if (p->runtime_scope != RUNTIME_SCOPE_SYSTEM) {
+ userns_fd = namespace_open_by_type(NAMESPACE_USER);
+ if (userns_fd < 0)
+ return log_debug_errno(userns_fd, "Failed to open our own user namespace: %m");
+ }
+
+ r = mstack_open_images(mstack, userns_fd, p->root_image_policy, /* image_filter= */ NULL, mstack_flags);
+ if (r < 0)
+ return r;
}
if (p->root_directory)
return r;
}
+ } else if (p->root_mstack) {
+ r = mstack_make_mounts(mstack, root, mstack_flags);
+ if (r < 0)
+ return r;
+
+ r = mstack_bind_mounts(mstack, root, /* where_fd= */ -EBADF, mstack_flags, /* ret_root_fd= */ NULL);
+ if (r < 0)
+ return r;
+
} else {
/* Let's mount the main root directory to the root directory to use */
r = mount_nofollow_verbose(LOG_DEBUG, "/", root, NULL, MS_BIND|MS_REC, NULL);
}
/* Try to set up the new root directory before mounting anything else there. */
- if (p->root_image || p->root_directory || p->root_directory_fd >= 0)
+ if (namespace_with_rootfs(p))
(void) base_filesystem_create(root, UID_INVALID, GID_INVALID);
/* Now make the magic happen */
int root_directory_fd;
const char *root_directory;
const char *root_image;
+ const char *root_mstack;
const MountOptions *root_image_options;
const ImagePolicy *root_image_policy;
if (s->exec_context.root_image ||
s->exec_context.n_extension_images > 0 ||
- !strv_isempty(s->exec_context.extension_directories)) /* We cannot chase paths through images */
- return log_unit_debug_errno(UNIT(s), SYNTHETIC_ERRNO(ENODATA), "Service with RootImage=, ExtensionImages= or ExtensionDirectories= set, cannot determine socket SELinux label before activation, ignoring.");
+ !strv_isempty(s->exec_context.extension_directories) ||
+ s->exec_context.root_mstack) /* We cannot chase paths through images */
+ return log_unit_debug_errno(UNIT(s), SYNTHETIC_ERRNO(ENODATA), "Service with RootImage=, ExtensionImages=, ExtensionDirectories= or RootMStack= set cannot determine socket SELinux label before activation, ignoring.");
ExecCommand *c = s->exec_command[SERVICE_EXEC_START];
if (!c)
return r;
}
+ if (c->root_mstack) {
+ r = unit_add_mounts_for(u, c->root_mstack, UNIT_DEPENDENCY_FILE, UNIT_MOUNT_WANTS);
+ if (r < 0)
+ return r;
+ }
+
for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) {
if (!u->manager->prefix[dt])
continue;
return r;
}
- if (c->root_image) {
+ if (c->root_image || c->root_mstack) {
/* We need to wait for /dev/loopX to appear when doing RootImage=, hence let's add an
- * implicit dependency on udev */
+ * implicit dependency on udev. (And for RootMStack= we might need it) */
r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_UDEVD_SERVICE, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
/* Only add these if needed, as they imply that everything else is blocked. */
if (cgroup_context_has_device_policy(cc)) {
- if (ec->root_image || ec->mount_images) {
+ if (ec->root_image || ec->mount_images || ec->root_mstack) {
- /* When RootImage= or MountImages= is specified, the following devices are touched. */
+ /* When RootImage= or MountImages= is specified, the following devices are
+ * touched. For RootMStack= there's the possibility the are touched. */
FOREACH_STRING(p, "/dev/loop-control", "/dev/mapper/control") {
r = cgroup_context_add_device_allow(cc, p, CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE);
if (r < 0)
JSON_BUILD_PAIR_CALLBACK_NON_NULL("WorkingDirectory", working_directory_build_json, c),
JSON_BUILD_PAIR_STRING_NON_EMPTY("RootDirectory", c->root_directory),
JSON_BUILD_PAIR_STRING_NON_EMPTY("RootImage", c->root_image),
+ JSON_BUILD_PAIR_STRING_NON_EMPTY("RootMStack", c->root_mstack),
JSON_BUILD_PAIR_CALLBACK_NON_NULL("RootImageOptions", root_image_options_build_json, c->root_image_options),
SD_JSON_BUILD_PAIR_BOOLEAN("RootEphemeral", c->root_ephemeral),
JSON_BUILD_PAIR_BASE64_NON_EMPTY("RootHash", c->root_hash.iov_base, c->root_hash.iov_len),
SD_VARLINK_DEFINE_FIELD(RootDirectory, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man"PROJECT_VERSION_STR"systemd.exec.html#RootImage="),
SD_VARLINK_DEFINE_FIELD(RootImage, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
+ SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man"PROJECT_VERSION_STR"systemd.exec.html#RootMStack="),
+ SD_VARLINK_DEFINE_FIELD(RootMStack, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man"PROJECT_VERSION_STR"systemd.exec.html#RootImageOptions="),
SD_VARLINK_DEFINE_FIELD_BY_TYPE(RootImageOptions, PartitionMountOptions, SD_VARLINK_ARRAY|SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man"PROJECT_VERSION_STR"systemd.exec.html#RootEphemeral="),