/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-***/
#include <errno.h>
#include <sched.h>
} MountEntry;
/* If MountAPIVFS= is used, let's mount /sys and /proc into the it, but only as a fallback if the user hasn't mounted
- * something there already. These mounts are hence overriden by any other explicitly configured mounts. */
+ * something there already. These mounts are hence overridden by any other explicitly configured mounts. */
static const MountEntry apivfs_table[] = {
{ "/proc", PROCFS, false },
{ "/dev", BIND_DEV, false },
needs_prefix = true;
}
- if (!path_is_absolute(e))
+ if (!path_is_absolute(e)) {
+ log_debug("Path is not absolute: %s", e);
return -EINVAL;
+ }
*((*p)++) = (MountEntry) {
.path_const = e,
unsigned long flags = MS_NODEV|MS_STRICTATIME;
bool ro = false;
- if (!path_is_absolute(t->path))
+ if (!path_is_absolute(t->path)) {
+ log_debug("Path is not absolute: %s", t->path);
return -EINVAL;
+ }
if (!isempty(t->options)) {
str = strjoin("mode=0755,", t->options);
r = mount_option_mangle(str, MS_NODEV|MS_STRICTATIME, &flags, &o);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to parse mount option '%s': %m", str);
- ro = !!(flags & MS_RDONLY);
+ ro = flags & MS_RDONLY;
if (ro)
flags ^= MS_RDONLY;
}
}
static int make_read_only(const MountEntry *m, char **blacklist, FILE *proc_self_mountinfo) {
+ bool submounts = false;
int r = 0;
assert(m);
/* Make superblock readonly */
if (mount(NULL, mount_entry_path(m), NULL, MS_REMOUNT | MS_RDONLY | m->flags, mount_entry_options(m)) < 0)
r = -errno;
- } else
+ } else {
+ submounts = true;
r = bind_remount_recursive_with_mountinfo(mount_entry_path(m), true, blacklist, proc_self_mountinfo);
+ }
} else if (m->mode == PRIVATE_DEV) {
/* Superblock can be readonly but the submounts can't */
if (mount(NULL, mount_entry_path(m), NULL, MS_REMOUNT|DEV_MOUNT_OPTIONS|MS_RDONLY, NULL) < 0)
if (r == -ENOENT && m->ignore)
r = 0;
- return r;
+ if (r < 0)
+ return log_debug_errno(r, "Failed to re-mount '%s'%s read-only: %m", mount_entry_path(m),
+ submounts ? " and its submounts" : "");
+
+ return 0;
}
-static bool namespace_info_mount_apivfs(const char *root_directory, const NamespaceInfo *ns_info) {
+static bool namespace_info_mount_apivfs(const NamespaceInfo *ns_info) {
assert(ns_info);
/*
* ProtectControlGroups= and ProtectKernelTunables= imply MountAPIVFS=,
* since to protect the API VFS mounts, they need to be around in the
- * first place... and RootDirectory= or RootImage= need to be set.
+ * first place...
*/
- /* root_directory should point to a mount point */
- return root_directory &&
- (ns_info->mount_apivfs ||
- ns_info->protect_control_groups ||
- ns_info->protect_kernel_tunables);
+ return ns_info->mount_apivfs ||
+ ns_info->protect_control_groups ||
+ ns_info->protect_kernel_tunables;
}
static size_t namespace_calculate_mounts(
- const char* root_directory,
const NamespaceInfo *ns_info,
char** read_write_paths,
char** read_only_paths,
(ns_info->protect_control_groups ? 1 : 0) +
(ns_info->protect_kernel_modules ? ELEMENTSOF(protect_kernel_modules_table) : 0) +
protect_home_cnt + protect_system_cnt +
- (namespace_info_mount_apivfs(root_directory, ns_info) ? ELEMENTSOF(apivfs_table) : 0);
+ (namespace_info_mount_apivfs(ns_info) ? ELEMENTSOF(apivfs_table) : 0);
}
static void normalize_mounts(const char *root_directory, MountEntry *mounts, size_t *n_mounts) {
+ assert(root_directory);
assert(n_mounts);
assert(mounts || *n_mounts == 0);
_cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
_cleanup_free_ void *root_hash = NULL;
MountEntry *m, *mounts = NULL;
- size_t root_hash_size = 0;
- bool make_slave = false;
- const char *root;
- size_t n_mounts;
+ size_t n_mounts, root_hash_size = 0;
bool require_prefix = false;
+ const char *root;
int r = 0;
assert(ns_info);
dissect_image_flags & DISSECT_IMAGE_READ_ONLY ? O_RDONLY : O_RDWR,
&loop_device);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to create loop device for root image: %m");
r = root_hash_load(root_image, &root_hash, &root_hash_size);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to load root hash: %m");
r = dissect_image(loop_device->fd, root_hash, root_hash_size, dissect_image_flags, &dissected_image);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to dissect image: %m");
r = dissected_image_decrypt(dissected_image, NULL, root_hash, root_hash_size, dissect_image_flags, &decrypted_image);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to decrypt dissected image: %m");
}
if (root_directory)
root = root_directory;
- else if (root_image || n_bind_mounts > 0 || n_temporary_filesystems > 0) {
-
- /* If we are booting from an image, create a mount point for the image, if it's still missing. We use
- * the same mount point for all images, which is safe, since they all live in their own namespaces
- * after all, and hence won't see each other. We also use such a root directory whenever there are bind
- * mounts configured, so that their source mounts are never obstructed by mounts we already applied
- * while we are applying them. */
+ else {
+ /* Always create the mount namespace in a temporary directory, instead of operating
+ * directly in the root. The temporary directory prevents any mounts from being
+ * potentially obscured my other mounts we already applied.
+ * We use the same mount point for all images, which is safe, since they all live
+ * in their own namespaces after all, and hence won't see each other. */
root = "/run/systemd/unit-root";
(void) mkdir_label(root, 0700);
require_prefix = true;
- } else
- root = NULL;
+ }
n_mounts = namespace_calculate_mounts(
- root,
ns_info,
read_write_paths,
read_only_paths,
tmp_dir, var_tmp_dir,
protect_home, protect_system);
- /* Set mount slave mode */
- if (root || n_mounts > 0)
- make_slave = true;
-
if (n_mounts > 0) {
m = mounts = (MountEntry *) alloca0(n_mounts * sizeof(MountEntry));
r = append_access_mounts(&m, read_write_paths, READWRITE, require_prefix);
if (r < 0)
goto finish;
- if (namespace_info_mount_apivfs(root, ns_info)) {
+ if (namespace_info_mount_apivfs(ns_info)) {
r = append_static_mounts(&m, apivfs_table, ELEMENTSOF(apivfs_table), ns_info->ignore_protect_paths);
if (r < 0)
goto finish;
if (r < 0)
goto finish;
- normalize_mounts(root_directory, mounts, &n_mounts);
+ normalize_mounts(root, mounts, &n_mounts);
}
+ /* All above is just preparation, figuring out what to do. Let's now actually start doing something. */
+
if (unshare(CLONE_NEWNS) < 0) {
- r = -errno;
+ r = log_debug_errno(errno, "Failed to unshare the mount namespace: %m");
+ if (IN_SET(r, -EACCES, -EPERM, -EOPNOTSUPP, -ENOSYS))
+ /* If the kernel doesn't support namespaces, or when there's a MAC or seccomp filter in place
+ * that doesn't allow us to create namespaces (or a missing cap), then propagate a recognizable
+ * error back, which the caller can use to detect this case (and only this) and optionally
+ * continue without namespacing applied. */
+ r = -ENOANO;
+
goto finish;
}
- if (make_slave) {
- /* Remount / as SLAVE so that nothing now mounted in the namespace
- shows up in the parent */
- if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) {
- r = -errno;
- goto finish;
- }
+ /* Remount / as SLAVE so that nothing now mounted in the namespace
+ * shows up in the parent */
+ if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) {
+ r = log_debug_errno(errno, "Failed to remount '/' as SLAVE: %m");
+ goto finish;
}
if (root_image) {
/* A root image is specified, mount it to the right place */
r = dissected_image_mount(dissected_image, root, UID_INVALID, dissect_image_flags);
- if (r < 0)
+ if (r < 0) {
+ log_debug_errno(r, "Failed to mount root image: %m");
goto finish;
+ }
if (decrypted_image) {
r = decrypted_image_relinquish(decrypted_image);
- if (r < 0)
+ if (r < 0) {
+ log_debug_errno(r, "Failed to relinquish decrypted image: %m");
goto finish;
+ }
}
loop_device_relinquish(loop_device);
/* A root directory is specified. Turn its directory into bind mount, if it isn't one yet. */
r = path_is_mount_point(root, NULL, AT_SYMLINK_FOLLOW);
- if (r < 0)
+ if (r < 0) {
+ log_debug_errno(r, "Failed to detect that %s is a mount point or not: %m", root);
goto finish;
+ }
if (r == 0) {
if (mount(root, root, NULL, MS_BIND|MS_REC, NULL) < 0) {
- r = -errno;
+ r = log_debug_errno(errno, "Failed to bind mount '%s': %m", root);
goto finish;
}
}
- } else if (root) {
+ } else {
/* Let's mount the main root directory to the root directory to use */
if (mount("/", root, NULL, MS_BIND|MS_REC, NULL) < 0) {
- r = -errno;
+ r = log_debug_errno(errno, "Failed to bind mount '/' on '%s': %m", root);
goto finish;
}
}
* For example, this is the case with the option: 'InaccessiblePaths=/proc' */
proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
if (!proc_self_mountinfo) {
- r = -errno;
+ r = log_debug_errno(errno, "Failed to open /proc/self/mountinfo: %m");
goto finish;
}
if (!again)
break;
- normalize_mounts(root_directory, mounts, &n_mounts);
+ normalize_mounts(root, mounts, &n_mounts);
}
/* Create a blacklist we can pass to bind_mount_recursive() */
}
}
- if (root) {
- /* MS_MOVE does not work on MS_SHARED so the remount MS_SHARED will be done later */
- r = mount_move_root(root);
- if (r < 0)
- goto finish;
+ /* MS_MOVE does not work on MS_SHARED so the remount MS_SHARED will be done later */
+ r = mount_move_root(root);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to mount root with MS_MOVE: %m");
+ goto finish;
}
/* Remount / as the desired mode. Note that this will not
* reestablish propagation from our side to the host, since
* what's disconnected is disconnected. */
if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) {
- r = -errno;
+ r = log_debug_errno(errno, "Failed to remount '/' with desired mount flags: %m");
goto finish;
}
[PROTECT_HOME_TMPFS] = "tmpfs",
};
-DEFINE_STRING_TABLE_LOOKUP(protect_home, ProtectHome);
-
-ProtectHome parse_protect_home_or_bool(const char *s) {
- int r;
-
- r = parse_boolean(s);
- if (r > 0)
- return PROTECT_HOME_YES;
- if (r == 0)
- return PROTECT_HOME_NO;
-
- return protect_home_from_string(s);
-}
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(protect_home, ProtectHome, PROTECT_HOME_YES);
static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = {
[PROTECT_SYSTEM_NO] = "no",
[PROTECT_SYSTEM_STRICT] = "strict",
};
-DEFINE_STRING_TABLE_LOOKUP(protect_system, ProtectSystem);
-
-ProtectSystem parse_protect_system_or_bool(const char *s) {
- int r;
-
- r = parse_boolean(s);
- if (r > 0)
- return PROTECT_SYSTEM_YES;
- if (r == 0)
- return PROTECT_SYSTEM_NO;
-
- return protect_system_from_string(s);
-}
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(protect_system, ProtectSystem, PROTECT_SYSTEM_YES);
static const char* const namespace_type_table[] = {
[NAMESPACE_MOUNT] = "mnt",