mirror of
https://github.com/systemd/systemd.git
synced 2026-08-07 08:30:57 +00:00
core: make DynamicUser=1 and StateDirectory= work with TemporaryFileSystem=/var/lib
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.
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -166,6 +166,7 @@ static void test_protect_kernel_logs(void) {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL, 0,
|
||||
NULL, 0,
|
||||
NULL, 0,
|
||||
|
||||
@@ -83,6 +83,7 @@ int main(int argc, char *argv[]) {
|
||||
(char **) writable,
|
||||
(char **) readonly,
|
||||
(char **) inaccessible,
|
||||
NULL,
|
||||
(char **) exec,
|
||||
(char **) no_exec,
|
||||
NULL,
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user