From a07c1ed05f40c84a24f1aa9f1ad1403ed5367e36 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Sat, 8 Nov 2025 01:59:01 +0100 Subject: [PATCH 1/3] core/namespace: hide the correct credentials tree when running in user scope --- src/core/namespace.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/core/namespace.c b/src/core/namespace.c index 0e12a16592b..07a69c03a62 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -2811,13 +2811,21 @@ int setup_namespace(const NamespaceParameters *p, char **reterr_path) { return log_oom_debug(); *me = (MountEntry) { - .path_const = "/run/credentials", .mode = MOUNT_TMPFS, .read_only = true, .options_const = "mode=0755" TMPFS_LIMITS_EMPTY_OR_ALMOST, .flags = MS_NODEV|MS_STRICTATIME|MS_NOSUID|MS_NOEXEC, }; + if (p->runtime_scope == RUNTIME_SCOPE_SYSTEM) + me->path_const = "/run/credentials"; + else { + r = path_extract_directory(p->creds_path, &me->path_malloc); + if (r < 0) + return log_debug_errno(r, "Failed to extract parent directory from '%s': %m", + p->creds_path); + } + me = mount_list_extend(&ml); if (!me) return log_oom_debug(); @@ -2829,9 +2837,11 @@ int setup_namespace(const NamespaceParameters *p, char **reterr_path) { .source_const = p->creds_path, .ignore = true, }; - } else { - /* If our service has no credentials store configured, then make the whole credentials tree - * inaccessible wholesale. */ + } + + if (!p->creds_path || p->runtime_scope != RUNTIME_SCOPE_SYSTEM) { + /* If our service has no credentials store configured, or we're running in user scope, then + * make the system credentials tree inaccessible wholesale. */ MountEntry *me = mount_list_extend(&ml); if (!me) From 9876309cfe1de2cd85c02ca0b396a3e147b973ce Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Sun, 9 Nov 2025 20:14:01 +0100 Subject: [PATCH 2/3] run: make sure we send out READY=1 when --wait Let's skip bus_wait_for_jobs_one() when --wait as well, as it surpasses the start job and allows for run_context_check_started() to fire. --- src/run/run.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/run/run.c b/src/run/run.c index 47757d19d0d..96fa46bd3a5 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -2504,8 +2504,9 @@ static int start_transient_service(sd_bus *bus) { /* Optionally, wait for the start job to complete. If we are supposed to read the service's stdin * lets skip this however, because we should start that already when the start job is running, and * there's little point in waiting for the start job to complete in that case anyway, as we'll wait - * for EOF anyway, which is going to be much later. */ - if (!arg_no_block && arg_stdio == ARG_STDIO_NONE) { + * for EOF anyway, which is going to be much later. Similar applies to --wait where we're going + * to wait for the service to terminate. */ + if (!arg_no_block && !arg_wait && arg_stdio == ARG_STDIO_NONE) { r = bus_wait_for_jobs_new(bus, &w); if (r < 0) return log_error_errno(r, "Could not watch jobs: %m"); From 6a5e38c3b9e8c5ada91ac92a876ff25a36d04cb7 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Sun, 9 Nov 2025 18:22:26 +0100 Subject: [PATCH 3/3] TEST-54-CREDS: add test case for credential dir masking --- test/units/TEST-54-CREDS.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/units/TEST-54-CREDS.sh b/test/units/TEST-54-CREDS.sh index 479417dea00..5cfa19905ab 100755 --- a/test/units/TEST-54-CREDS.sh +++ b/test/units/TEST-54-CREDS.sh @@ -554,4 +554,18 @@ run0 -u testuser --pipe mkdir -p /home/testuser/.config/credstore.encrypted run0 -u testuser --pipe systemd-creds encrypt --user --name=brummbaer - /home/testuser/.config/credstore.encrypted/brummbaer < /tmp/brummbaer.data run0 -u testuser --pipe systemd-run --user --pipe -p ImportCredential=brummbaer systemd-creds cat brummbaer | cmp /tmp/brummbaer.data +# https://github.com/systemd/systemd/pull/39651 +TESTUSER_CRED_DIR="/run/user/$(id -u testuser)/credentials" + +PID="$(systemd-notify --fork -- systemd-run -M testuser@ --user --wait --unit=brummbaer.service -p LoadCredential=brummbaer sleep infinity)" +[[ -d "$TESTUSER_CRED_DIR/brummbaer.service" ]] +[[ -f "$TESTUSER_CRED_DIR/brummbaer.service/brummbaer" ]] + +systemd-run -M testuser@ --user --wait -p PrivateMounts=yes -p ImportCredential=brummbaer \ + bash -xec "[[ ! -d '$TESTUSER_CRED_DIR/brummbaer.service' ]] && [[ \$(stat -c %a /run/credentials) -eq 0 ]]" +systemd-run -M testuser@ --user --wait -p ImportCredential=brummbaer \ + test -d "$TESTUSER_CRED_DIR/brummbaer.service" + +kill "$PID" + touch /testok