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.
This commit is contained in:
Lennart Poettering
2017-10-02 17:41:43 +02:00
parent 645767d6b5
commit e908468b5b
+40 -22
View File
@@ -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;
}