From e908468b5b24f1d4a4d787262cb3b8b2d8a5aad4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 28 Sep 2017 18:30:55 +0200 Subject: [PATCH] namespace: properly handle bind mounts from the host Before this patch we had an ordering problem: if we have no namespacing enabled except for two bind mounts that intend to swap /a and /b via bind mounts, then we'd execute the bind mount binding /b to /a, followed by thebind mount from /a to /b, thus having the effect that /b is now visible in both /a and /b, which was not intended. With this change, as soon as any bind mount is configured we'll put together the service mount namespace in a temporary directory instead of operating directly in the root. This solves the problem in a straightforward fashion: the source of bind mounts will always refer to the host, and thus be unaffected from the bind mounts we already created. --- src/core/namespace.c | 62 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/src/core/namespace.c b/src/core/namespace.c index 8511131..069e0ed 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -31,6 +31,7 @@ #include "dev-setup.h" #include "fd-util.h" #include "fs-util.h" +#include "label.h" #include "loop-util.h" #include "loopback-setup.h" #include "missing.h" @@ -904,6 +905,7 @@ int setup_namespace( MountEntry *m, *mounts = NULL; size_t root_hash_size = 0; bool make_slave = false; + const char *root; unsigned n_mounts; int r = 0; @@ -935,18 +937,25 @@ int setup_namespace( r = dissected_image_decrypt(dissected_image, NULL, root_hash, root_hash_size, dissect_image_flags, &decrypted_image); if (r < 0) return r; - - if (!root_directory) { - /* 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. */ - root_directory = "/run/systemd/unit-root"; - (void) mkdir(root_directory, 0700); - } } + if (root_directory) + root = root_directory; + else if (root_image || n_bind_mounts > 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. */ + + root = "/run/systemd/unit-root"; + (void) mkdir_label(root, 0700); + } else + root = NULL; + n_mounts = namespace_calculate_mounts( - root_directory, + root, ns_info, read_write_paths, read_only_paths, @@ -956,7 +965,7 @@ int setup_namespace( protect_home, protect_system); /* Set mount slave mode */ - if (root_directory || n_mounts > 0) + if (root || n_mounts > 0) make_slave = true; if (n_mounts > 0) { @@ -1025,7 +1034,7 @@ int setup_namespace( if (r < 0) goto finish; - if (namespace_info_mount_apivfs(root_directory, ns_info)) { + if (namespace_info_mount_apivfs(root, ns_info)) { r = append_static_mounts(&m, apivfs_table, ELEMENTSOF(apivfs_table), ns_info->ignore_protect_paths); if (r < 0) goto finish; @@ -1034,14 +1043,14 @@ int setup_namespace( assert(mounts + n_mounts == m); /* Prepend the root directory where that's necessary */ - r = prefix_where_needed(mounts, n_mounts, root_directory); + r = prefix_where_needed(mounts, n_mounts, root); if (r < 0) goto finish; qsort(mounts, n_mounts, sizeof(MountEntry), mount_path_compare); drop_duplicates(mounts, &n_mounts); - drop_outside_root(root_directory, mounts, &n_mounts); + drop_outside_root(root, mounts, &n_mounts); drop_inaccessible(mounts, &n_mounts); drop_nop(mounts, &n_mounts); } @@ -1061,11 +1070,12 @@ int setup_namespace( } /* Try to set up the new root directory before mounting anything there */ - if (root_directory) - (void) base_filesystem_create(root_directory, UID_INVALID, GID_INVALID); + if (root) + (void) base_filesystem_create(root, UID_INVALID, GID_INVALID); if (root_image) { - r = dissected_image_mount(dissected_image, root_directory, dissect_image_flags); + /* A root image is specified, mount it to the right place */ + r = dissected_image_mount(dissected_image, root, dissect_image_flags); if (r < 0) goto finish; @@ -1079,16 +1089,24 @@ int setup_namespace( } else if (root_directory) { - /* Turn directory into bind mount, if it isn't one yet */ - r = path_is_mount_point(root_directory, NULL, AT_SYMLINK_FOLLOW); + /* 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) goto finish; if (r == 0) { - if (mount(root_directory, root_directory, NULL, MS_BIND|MS_REC, NULL) < 0) { + if (mount(root, root, NULL, MS_BIND|MS_REC, NULL) < 0) { r = -errno; goto finish; } } + + } else if (root) { + + /* 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; + goto finish; + } } if (n_mounts > 0) { @@ -1106,7 +1124,7 @@ int setup_namespace( /* First round, add in all special mounts we need */ for (m = mounts; m < mounts + n_mounts; ++m) { - r = apply_mount(root_directory, m, tmp_dir, var_tmp_dir); + r = apply_mount(root, m, tmp_dir, var_tmp_dir); if (r < 0) goto finish; } @@ -1125,9 +1143,9 @@ int setup_namespace( } } - if (root_directory) { + if (root) { /* MS_MOVE does not work on MS_SHARED so the remount MS_SHARED will be done later */ - r = mount_move_root(root_directory); + r = mount_move_root(root); if (r < 0) goto finish; } -- 2.7.4