diff --git a/man/systemd-tmpfiles.xml b/man/systemd-tmpfiles.xml
index 425ed23dd35..92ab322ba0e 100644
--- a/man/systemd-tmpfiles.xml
+++ b/man/systemd-tmpfiles.xml
@@ -230,6 +230,31 @@
systemd-tmpfiles --remove --create
+
+ Credentials
+
+ systemd-tmpfiles supports the service credentials logic as implemented by
+ LoadCredential=/SetCredential= (see
+ systemd.exec1 for
+ details). The following credentials are used when passed in:
+
+
+
+ tmpfiles.extra
+
+ The contents of this credential may contain additional lines to operate on. The
+ credential contents should follow the same format as any other tmpfiles.d/
+ drop-in configuration file. If this credential is passed it is processed after all of the drop-in
+ files read from the file system. The lines in the credential can hence augment existing lines of the
+ OS, but not override them.
+
+
+
+ Note that by default the systemd-tmpfiles-setup.service unit file (and related
+ unit files) is set up to inherit the tmpfiles.extra credential from the service
+ manager.
+
+
Environment
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index 0c50c8e1ee3..e2451f1b958 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -25,6 +25,7 @@
#include "chattr-util.h"
#include "conf-files.h"
#include "copy.h"
+#include "creds-util.h"
#include "def.h"
#include "devnum-util.h"
#include "dirent-util.h"
@@ -3594,7 +3595,12 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoent, bool *invalid_config) {
+static int read_config_file(
+ char **config_dirs,
+ const char *fn,
+ bool ignore_enoent,
+ bool *invalid_config) {
+
_cleanup_(hashmap_freep) Hashmap *uid_cache = NULL, *gid_cache = NULL;
_cleanup_fclose_ FILE *_f = NULL;
_cleanup_free_ char *pp = NULL;
@@ -3736,6 +3742,25 @@ static int read_config_files(char **config_dirs, char **args, bool *invalid_conf
return 0;
}
+static int read_credential_lines(bool *invalid_config) {
+ _cleanup_free_ char *j = NULL;
+ const char *d;
+ int r;
+
+ r = get_credentials_dir(&d);
+ if (r == -ENXIO)
+ return 0;
+ if (r < 0)
+ return log_error_errno(r, "Failed to get credentials directory: %m");
+
+ j = path_join(d, "tmpfiles.extra");
+ if (!j)
+ return log_oom();
+
+ (void) read_config_file(/* config_dirs= */ NULL, j, /* ignore_enoent= */ true, invalid_config);
+ return 0;
+}
+
static int link_parent(ItemArray *a) {
const char *path;
char *prefix;
@@ -3892,6 +3917,10 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
+ r = read_credential_lines(&invalid_config);
+ if (r < 0)
+ return r;
+
/* Let's now link up all child/parent relationships */
ORDERED_HASHMAP_FOREACH(a, items) {
r = link_parent(a);
diff --git a/test/TEST-54-CREDS/test.sh b/test/TEST-54-CREDS/test.sh
index 8f66f1c7b84..ac6777be311 100755
--- a/test/TEST-54-CREDS/test.sh
+++ b/test/TEST-54-CREDS/test.sh
@@ -4,7 +4,7 @@ set -e
TEST_DESCRIPTION="test credentials"
NSPAWN_ARGUMENTS="${NSPAWN_ARGUMENTS:-} --set-credential=mynspawncredential:strangevalue"
-QEMU_OPTIONS="${QEMU_OPTIONS:-} -fw_cfg name=opt/io.systemd.credentials/myqemucredential,string=othervalue -smbios type=11,value=io.systemd.credential:smbioscredential=magicdata -smbios type=11,value=io.systemd.credential.binary:binarysmbioscredential=bWFnaWNiaW5hcnlkYXRh -smbios type=11,value=io.systemd.credential.binary:sysusers.extra=dSBjcmVkdGVzdHVzZXIK"
+QEMU_OPTIONS="${QEMU_OPTIONS:-} -fw_cfg name=opt/io.systemd.credentials/myqemucredential,string=othervalue -smbios type=11,value=io.systemd.credential:smbioscredential=magicdata -smbios type=11,value=io.systemd.credential.binary:binarysmbioscredential=bWFnaWNiaW5hcnlkYXRh -smbios type=11,value=io.systemd.credential.binary:sysusers.extra=dSBjcmVkdGVzdHVzZXIK -smbios type=11,value=io.systemd.credential.binary:tmpfiles.extra=ZiAvdG1wL3NvdXJjZWRmcm9tY3JlZGVudGlhbCAtIC0gLSAtIHRtcGZpbGVzc2VjcmV0Cg=="
KERNEL_APPEND="${KERNEL_APPEND:-} systemd.set_credential=kernelcmdlinecred:uff systemd.set_credential=sysctl.extra:kernel.domainname=sysctltest rd.systemd.import_credentials=no"
# shellcheck source=test/test-functions
diff --git a/test/units/testsuite-54.sh b/test/units/testsuite-54.sh
index a7ccdca032f..8eff47028d7 100755
--- a/test/units/testsuite-54.sh
+++ b/test/units/testsuite-54.sh
@@ -43,6 +43,9 @@ elif [ -d /sys/firmware/qemu_fw_cfg/by_name ]; then
# Verify that creating a user via sysusers via the kernel cmdline worked
grep -q ^credtestuser: /etc/passwd
+
+ # Verify that writing a file via tmpfiles worked
+ [ "$(cat /tmp/sourcedfromcredential)" = "tmpfilessecret" ]
else
echo "qemu_fw_cfg support missing in kernel. Sniff!"
expected_credential=""
diff --git a/units/systemd-tmpfiles-clean.service b/units/systemd-tmpfiles-clean.service
index 7aee6463bd5..6ae4e74ddd0 100644
--- a/units/systemd-tmpfiles-clean.service
+++ b/units/systemd-tmpfiles-clean.service
@@ -20,3 +20,4 @@ Type=oneshot
ExecStart=systemd-tmpfiles --clean
SuccessExitStatus=DATAERR
IOSchedulingClass=idle
+LoadCredential=tmpfiles.extra
diff --git a/units/systemd-tmpfiles-setup-dev.service b/units/systemd-tmpfiles-setup-dev.service
index 0babe78767a..ad0e54fcc41 100644
--- a/units/systemd-tmpfiles-setup-dev.service
+++ b/units/systemd-tmpfiles-setup-dev.service
@@ -20,3 +20,4 @@ Type=oneshot
RemainAfterExit=yes
ExecStart=systemd-tmpfiles --prefix=/dev --create --boot
SuccessExitStatus=DATAERR CANTCREAT
+LoadCredential=tmpfiles.extra
diff --git a/units/systemd-tmpfiles-setup.service b/units/systemd-tmpfiles-setup.service
index bc29dbc8c9c..6c1ee91a403 100644
--- a/units/systemd-tmpfiles-setup.service
+++ b/units/systemd-tmpfiles-setup.service
@@ -21,3 +21,4 @@ Type=oneshot
RemainAfterExit=yes
ExecStart=systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev
SuccessExitStatus=DATAERR CANTCREAT
+LoadCredential=tmpfiles.extra