diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml
index ec0492c26d..2645a6b217 100644
--- a/man/systemd-nspawn.xml
+++ b/man/systemd-nspawn.xml
@@ -606,7 +606,14 @@
After transitioning into the container, change to the specified user defined in the
container's user database. Like all other systemd-nspawn features, this is not a security feature and
- provides protection against accidental destructive operations only.
+ provides protection against accidental destructive operations only.
+
+ Note that if credentials are used in combination with a non-root
+ (e.g.: , or
+ ), then must be used, and
+ or must not be used, as the credentials would
+ otherwise be unreadable by the container due to missing privileges after switching to the specified
+ user.
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index f4da91797e..60894c9c86 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -2400,17 +2400,31 @@ int make_run_host(const char *root) {
}
static int setup_credentials(const char *root) {
+ bool world_readable = false;
const char *q;
int r;
if (arg_credentials.n_credentials == 0)
return 0;
+ /* If starting a single-process container as a non-root user, the uid will only be resolved after we
+ * are inside the inner child, when credential directories and files are already read-only, so they
+ * are unusable as the single process won't have access to them. We also don't have access to the
+ * uid that will actually be used from here, as we are setting credentials up from the outher child.
+ * In order to make them usable as requested by the configuration, make them world readable in that
+ * case, as by definition there are no other processes in that case besides the one being started,
+ * which is being configured to be able to access credentials, and any of its children which will
+ * inherit its privileges anyway. To ensure this, also enforce (and document) that
+ * --no-new-privileges is necessary for this combination to work. */
+ if (arg_no_new_privileges && !isempty(arg_user) && !STR_IN_SET(arg_user, "root", "0") &&
+ arg_start_mode == START_PID1)
+ world_readable = true;
+
r = make_run_host(root);
if (r < 0)
return r;
- r = userns_mkdir(root, "/run/host/credentials", 0700, 0, 0);
+ r = userns_mkdir(root, "/run/host/credentials", world_readable ? 0777 : 0700, 0, 0);
if (r < 0)
return log_error_errno(r, "Failed to create /run/host/credentials: %m");
@@ -2427,7 +2441,7 @@ static int setup_credentials(const char *root) {
if (!j)
return log_oom();
- fd = open(j, O_CREAT|O_EXCL|O_WRONLY|O_CLOEXEC|O_NOFOLLOW, 0600);
+ fd = open(j, O_CREAT|O_EXCL|O_WRONLY|O_CLOEXEC|O_NOFOLLOW, world_readable ? 0666 : 0600);
if (fd < 0)
return log_error_errno(errno, "Failed to create credential file %s: %m", j);
@@ -2435,7 +2449,7 @@ static int setup_credentials(const char *root) {
if (r < 0)
return log_error_errno(r, "Failed to write credential to file %s: %m", j);
- if (fchmod(fd, 0400) < 0)
+ if (fchmod(fd, world_readable ? 0444 : 0400) < 0)
return log_error_errno(errno, "Failed to adjust access mode of %s: %m", j);
if (arg_userns_mode != USER_NAMESPACE_NO) {
@@ -2444,7 +2458,7 @@ static int setup_credentials(const char *root) {
}
}
- if (chmod(q, 0500) < 0)
+ if (chmod(q, world_readable ? 0555 : 0500) < 0)
return log_error_errno(errno, "Failed to adjust access mode of %s: %m", q);
r = userns_lchown(q, 0, 0);
diff --git a/test/units/testsuite-13.nspawn.sh b/test/units/testsuite-13.nspawn.sh
index 69f21efd60..5b67cf8c7e 100755
--- a/test/units/testsuite-13.nspawn.sh
+++ b/test/units/testsuite-13.nspawn.sh
@@ -271,6 +271,13 @@ EOF
--load-credential=cred.path:/tmp/cred.path \
--set-credential="cred.set:hello world" \
bash -xec '[[ "$(