From d2d6c096f6373a76f3b303a7a116e7cfe7139c4d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Nov 2016 22:21:40 +0100 Subject: core: add ability to define arbitrary bind mounts for services This adds two new settings BindPaths= and BindReadOnlyPaths=. They allow defining arbitrary bind mounts specific to particular services. This is particularly useful for services with RootDirectory= set as this permits making specific bits of the host directory available to chrooted services. The two new settings follow the concepts nspawn already possess in --bind= and --bind-ro=, as well as the .nspawn settings Bind= and BindReadOnly= (and these latter options should probably be renamed to BindPaths= and BindReadOnlyPaths= too). Fixes: #3439 --- src/core/namespace.c | 127 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 116 insertions(+), 11 deletions(-) (limited to 'src/core/namespace.c') diff --git a/src/core/namespace.c b/src/core/namespace.c index 746ebc45b0..834883267c 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -50,6 +50,8 @@ typedef enum MountMode { /* This is ordered by priority! */ INACCESSIBLE, + BIND_MOUNT, + BIND_MOUNT_RECURSIVE, READONLY, PRIVATE_TMP, PRIVATE_VAR_TMP, @@ -64,6 +66,8 @@ typedef struct MountEntry { bool has_prefix:1; /* Already is prefixed by the root dir? */ bool read_only:1; /* Shall this mount point be read-only? */ char *path_malloc; /* Use this instead of 'path' if we had to allocate memory */ + const char *source_const; /* The source path, for bind mounts */ + char *source_malloc; } MountEntry; /* @@ -166,6 +170,12 @@ static bool mount_entry_read_only(const MountEntry *p) { return p->read_only || IN_SET(p->mode, READONLY, INACCESSIBLE); } +static const char *mount_entry_source(const MountEntry *p) { + assert(p); + + return p->source_malloc ?: p->source_const; +} + static int append_access_mounts(MountEntry **p, char **strv, MountMode mode) { char **i; @@ -201,6 +211,25 @@ static int append_access_mounts(MountEntry **p, char **strv, MountMode mode) { return 0; } +static int append_bind_mounts(MountEntry **p, const BindMount *binds, unsigned n) { + unsigned i; + + assert(p); + + for (i = 0; i < n; i++) { + const BindMount *b = binds + i; + + *((*p)++) = (MountEntry) { + .path_const = b->destination, + .mode = b->recursive ? BIND_MOUNT_RECURSIVE : BIND_MOUNT, + .read_only = b->read_only, + .source_const = b->source, + }; + } + + return 0; +} + static int append_static_mounts(MountEntry **p, const MountEntry *mounts, unsigned n, bool ignore_protect) { unsigned i; @@ -568,27 +597,33 @@ fail: return r; } -static int mount_entry_chase(MountEntry *m, const char *root_directory) { +static int mount_entry_chase( + const char *root_directory, + MountEntry *m, + const char *path, + char **location) { + char *chased; int r; assert(m); /* Since mount() will always follow symlinks and we need to take the different root directory into account we - * chase the symlinks on our own first. */ + * chase the symlinks on our own first. This is called for the destination path, as well as the source path (if + * that applies). The result is stored in "location". */ - r = chase_symlinks(mount_entry_path(m), root_directory, 0, &chased); + r = chase_symlinks(path, root_directory, 0, &chased); if (r == -ENOENT && m->ignore) { - log_debug_errno(r, "Path %s does not exist, ignoring.", mount_entry_path(m)); + log_debug_errno(r, "Path %s does not exist, ignoring.", path); return 0; } if (r < 0) - return log_debug_errno(r, "Failed to follow symlinks on %s: %m", mount_entry_path(m)); + return log_debug_errno(r, "Failed to follow symlinks on %s: %m", path); - log_debug("Followed symlinks %s → %s.", mount_entry_path(m), chased); + log_debug("Followed symlinks %s → %s.", path, chased); - free(m->path_malloc); - m->path_malloc = chased; + free(*location); + *location = chased; return 1; } @@ -600,11 +635,12 @@ static int apply_mount( const char *var_tmp_dir) { const char *what; + bool rbind = true; int r; assert(m); - r = mount_entry_chase(m, root_directory); + r = mount_entry_chase(root_directory, m, mount_entry_path(m), &m->path_malloc); if (r <= 0) return r; @@ -633,7 +669,6 @@ static int apply_mount( case READONLY: case READWRITE: - r = path_is_mount_point(mount_entry_path(m), root_directory, 0); if (r < 0) return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m", mount_entry_path(m)); @@ -643,6 +678,19 @@ static int apply_mount( what = mount_entry_path(m); break; + case BIND_MOUNT: + rbind = false; + /* fallthrough */ + + case BIND_MOUNT_RECURSIVE: + /* Also chase the source mount */ + r = mount_entry_chase(root_directory, m, mount_entry_source(m), &m->source_malloc); + if (r <= 0) + return r; + + what = mount_entry_source(m); + break; + case PRIVATE_TMP: what = tmp_dir; break; @@ -660,7 +708,7 @@ static int apply_mount( assert(what); - if (mount(what, mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL) < 0) + if (mount(what, mount_entry_path(m), NULL, MS_BIND|(rbind ? MS_REC : 0), NULL) < 0) return log_debug_errno(errno, "Failed to mount %s to %s: %m", what, mount_entry_path(m)); log_debug("Successfully mounted %s to %s", what, mount_entry_path(m)); @@ -695,6 +743,8 @@ static unsigned namespace_calculate_mounts( char** read_write_paths, char** read_only_paths, char** inaccessible_paths, + const BindMount *bind_mounts, + unsigned n_bind_mounts, const char* tmp_dir, const char* var_tmp_dir, ProtectHome protect_home, @@ -719,6 +769,7 @@ static unsigned namespace_calculate_mounts( strv_length(read_write_paths) + strv_length(read_only_paths) + strv_length(inaccessible_paths) + + n_bind_mounts + ns_info->private_dev + (ns_info->protect_kernel_tunables ? ELEMENTSOF(protect_kernel_tunables_table) : 0) + (ns_info->protect_control_groups ? 1 : 0) + @@ -732,6 +783,8 @@ int setup_namespace( char** read_write_paths, char** read_only_paths, char** inaccessible_paths, + const BindMount *bind_mounts, + unsigned n_bind_mounts, const char* tmp_dir, const char* var_tmp_dir, ProtectHome protect_home, @@ -751,6 +804,7 @@ int setup_namespace( read_write_paths, read_only_paths, inaccessible_paths, + bind_mounts, n_bind_mounts, tmp_dir, var_tmp_dir, protect_home, protect_system); @@ -772,6 +826,10 @@ int setup_namespace( if (r < 0) goto finish; + r = append_bind_mounts(&m, bind_mounts, n_bind_mounts); + if (r < 0) + goto finish; + if (tmp_dir) { *(m++) = (MountEntry) { .path_const = "/tmp", @@ -911,6 +969,53 @@ finish: return r; } +void bind_mount_free_many(BindMount *b, unsigned n) { + unsigned i; + + assert(b || n == 0); + + for (i = 0; i < n; i++) { + free(b[i].source); + free(b[i].destination); + } + + free(b); +} + +int bind_mount_add(BindMount **b, unsigned *n, const BindMount *item) { + _cleanup_free_ char *s = NULL, *d = NULL; + BindMount *c; + + assert(b); + assert(n); + assert(item); + + s = strdup(item->source); + if (!s) + return -ENOMEM; + + d = strdup(item->destination); + if (!d) + return -ENOMEM; + + c = realloc_multiply(*b, sizeof(BindMount), *n + 1); + if (!c) + return -ENOMEM; + + *b = c; + + c[(*n) ++] = (BindMount) { + .source = s, + .destination = d, + .read_only = item->read_only, + .recursive = item->recursive, + .ignore_enoent = item->ignore_enoent, + }; + + s = d = NULL; + return 0; +} + static int setup_one_tmp_dir(const char *id, const char *prefix, char **path) { _cleanup_free_ char *x = NULL; char bid[SD_ID128_STRING_MAX]; -- cgit v1.2.3-54-g00ecf