]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: make DynamicUser=1 and StateDirectory= work with TemporaryFileSystem=/var/lib
authorLuca Boccassi <luca.boccassi@microsoft.com>
Tue, 27 Jul 2021 15:41:38 +0000 (16:41 +0100)
committerLuca Boccassi <luca.boccassi@microsoft.com>
Wed, 27 Oct 2021 21:45:26 +0000 (22:45 +0100)
The /var/lib/private/foo -> /var/lib/foo symlink for StateDirectory and
DynamicUser is set up on the host filesystem, before the mount namespacing
is brought up. If an empty /var/lib is used, to ensure the service does not
see other services data, the symlink is then not available despite
/var/lib/private being set up as expected.

Make a list of symlinks that need to be set up, and create them after all
the namespaced filesystems have been created, but before any eventual
read-only switch is flipped.

src/core/execute.c
src/core/namespace.c
src/core/namespace.h
src/test/test-namespace.c
src/test/test-ns.c
test/units/testsuite-34.sh

index 9d100889018da4e7311607ab6ea46e9fad2ea88c..da917e1af4fbd38d450006e455e5c6c3b1e99ed5 100644 (file)
@@ -2384,7 +2384,9 @@ static int setup_exec_directory(
                                         goto fail;
                         }
 
-                        /* And link it up from the original place */
+                        /* And link it up from the original place. Note that if a mount namespace is going to be
+                         * used, then this symlink remains on the host, and a new one for the child namespace will
+                         * be created later. */
                         r = symlink_idempotent(pp, p, true);
                         if (r < 0)
                                 goto fail;
@@ -3148,6 +3150,49 @@ finish:
         return r;
 }
 
+/* ret_symlinks will contain a list of pairs src:dest that describes
+ * the symlinks to create later on. For example, the symlinks needed
+ * to safely give private directories to DynamicUser=1 users. */
+static int compile_symlinks(
+                const ExecContext *context,
+                const ExecParameters *params,
+                char ***ret_symlinks) {
+
+        _cleanup_strv_free_ char **symlinks = NULL;
+        int r;
+
+        assert(context);
+        assert(params);
+        assert(ret_symlinks);
+
+        for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) {
+                char **src;
+
+                if (!exec_directory_is_private(context, dt))
+                        continue;
+
+                STRV_FOREACH(src, context->directories[dt].paths) {
+                        _cleanup_free_ char *private_path = NULL, *path = NULL;
+
+                        private_path = path_join(params->prefix[dt], "private", *src);
+                        if (!private_path)
+                                return -ENOMEM;
+
+                        path = path_join(params->prefix[dt], *src);
+                        if (!path)
+                                return -ENOMEM;
+
+                        r = strv_consume_pair(&symlinks, TAKE_PTR(private_path), TAKE_PTR(path));
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        *ret_symlinks = TAKE_PTR(symlinks);
+
+        return 0;
+}
+
 static bool insist_on_sandboxing(
                 const ExecContext *context,
                 const char *root_dir,
@@ -3194,7 +3239,7 @@ static int apply_mount_namespace(
                 const ExecRuntime *runtime,
                 char **error_path) {
 
-        _cleanup_strv_free_ char **empty_directories = NULL;
+        _cleanup_strv_free_ char **empty_directories = NULL, **symlinks = NULL;
         const char *tmp_dir = NULL, *var_tmp_dir = NULL;
         const char *root_dir = NULL, *root_image = NULL;
         _cleanup_free_ char *creds_path = NULL, *incoming_dir = NULL, *propagate_dir = NULL;
@@ -3217,6 +3262,12 @@ static int apply_mount_namespace(
         if (r < 0)
                 return r;
 
+        /* Symlinks for exec dirs are set up after other mounts, before they are
+         * made read-only. */
+        r = compile_symlinks(context, params, &symlinks);
+        if (r < 0)
+                return r;
+
         needs_sandboxing = (params->flags & EXEC_APPLY_SANDBOXING) && !(command_flags & EXEC_COMMAND_FULLY_PRIVILEGED);
         if (needs_sandboxing) {
                 /* The runtime struct only contains the parent of the private /tmp,
@@ -3300,6 +3351,7 @@ static int apply_mount_namespace(
                             needs_sandboxing ? context->exec_paths : NULL,
                             needs_sandboxing ? context->no_exec_paths : NULL,
                             empty_directories,
+                            symlinks,
                             bind_mounts,
                             n_bind_mounts,
                             context->temporary_filesystems,
index 48f11f99066fa2996fda9d6d846da1ba720669ee..68704dff063dba27013ae19b7ba5a577c0f4b635 100644 (file)
@@ -1586,11 +1586,36 @@ static void normalize_mounts(const char *root_directory, MountEntry *mounts, siz
         drop_nop(mounts, n_mounts);
 }
 
+static int create_symlinks_from_tuples(const char *root, char **strv_symlinks) {
+        char **src, **dst;
+        int r;
+
+        STRV_FOREACH_PAIR(src, dst, strv_symlinks) {
+                _cleanup_free_ char *src_abs = NULL, *dst_abs = NULL;
+
+                src_abs = path_join(root, *src);
+                dst_abs = path_join(root, *dst);
+                if (!src_abs || !dst_abs)
+                        return -ENOMEM;
+
+                r = mkdir_parents_label(dst_abs, 0755);
+                if (r < 0)
+                        return r;
+
+                r = symlink_idempotent(src_abs, dst_abs, true);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
 static int apply_mounts(
                 const char *root,
                 const NamespaceInfo *ns_info,
                 MountEntry *mounts,
                 size_t *n_mounts,
+                char **exec_dir_symlinks,
                 char **error_path) {
 
         _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
@@ -1657,6 +1682,14 @@ static int apply_mounts(
                 normalize_mounts(root, mounts, n_mounts);
         }
 
+        /* Now that all filesystems have been set up, but before the
+         * read-only switches are flipped, create the exec dirs symlinks.
+         * Note that when /var/lib is not empty/tmpfs, these symlinks will already
+         * exist, which means this will be a no-op. */
+        r = create_symlinks_from_tuples(root, exec_dir_symlinks);
+        if (r < 0)
+                return r;
+
         /* Create a deny list we can pass to bind_mount_recursive() */
         deny_list = new(char*, (*n_mounts)+1);
         if (!deny_list)
@@ -1820,6 +1853,7 @@ int setup_namespace(
                 char** exec_paths,
                 char** no_exec_paths,
                 char** empty_directories,
+                char** exec_dir_symlinks,
                 const BindMount *bind_mounts,
                 size_t n_bind_mounts,
                 const TemporaryFileSystem *temporary_filesystems,
@@ -2261,7 +2295,7 @@ int setup_namespace(
                 (void) base_filesystem_create(root, UID_INVALID, GID_INVALID);
 
         /* Now make the magic happen */
-        r = apply_mounts(root, ns_info, mounts, &n_mounts, error_path);
+        r = apply_mounts(root, ns_info, mounts, &n_mounts, exec_dir_symlinks, error_path);
         if (r < 0)
                 goto finish;
 
index c9373a4adb1057ae63692d33242a1664f4e5a33f..62f05d7585fb9b508b16fc170a6467c73843a2d6 100644 (file)
@@ -121,6 +121,7 @@ int setup_namespace(
                 char **exec_paths,
                 char **no_exec_paths,
                 char **empty_directories,
+                char **exec_dir_symlinks,
                 const BindMount *bind_mounts,
                 size_t n_bind_mounts,
                 const TemporaryFileSystem *temporary_filesystems,
index 796382f3cf0713f2ebe3b7363aa552132aa47174..dd66379b5c9d9f085c04d1511d6984365d3f51e7 100644 (file)
@@ -166,6 +166,7 @@ static void test_protect_kernel_logs(void) {
                                     NULL,
                                     NULL,
                                     NULL,
+                                    NULL,
                                     NULL, 0,
                                     NULL, 0,
                                     NULL, 0,
index ae666a3019a9aedd4dff7336719257b62211b75a..b03eabb59bbc60adb9beafd26e39ca0c1ec8e485 100644 (file)
@@ -83,6 +83,7 @@ int main(int argc, char *argv[]) {
                             (char **) writable,
                             (char **) readonly,
                             (char **) inaccessible,
+                            NULL,
                             (char **) exec,
                             (char **) no_exec,
                             NULL,
index 97244ac4571f2edd535f479e87cc05ea9ab394ae..eca16bc78d90242a82a95d8b4e6e28efd6d7c324 100755 (executable)
@@ -10,6 +10,7 @@ systemd-analyze log-target console
 
 systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz touch /var/lib/zzz/test
 systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test
+systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz -p TemporaryFileSystem=/var/lib test -f /var/lib/zzz/test
 systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing \
     && { echo 'unexpected success'; exit 1; }
 
@@ -22,6 +23,7 @@ test ! -f /var/lib/zzz/test-missing
 # Convert to DynamicUser=1
 
 systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz test -f /var/lib/zzz/test
+systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz -p TemporaryFileSystem=/var/lib test -f /var/lib/zzz/test
 systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing \
     && { echo 'unexpected success'; exit 1; }
 
@@ -34,6 +36,7 @@ test ! -f /var/lib/zzz/test-missing
 # Convert back
 
 systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test
+systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz -p TemporaryFileSystem=/var/lib test -f /var/lib/zzz/test
 systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing \
     && { echo 'unexpected success'; exit 1; }