diff --git a/man/systemd-tmpfiles.xml b/man/systemd-tmpfiles.xml index 425ed23dd3..92ab322ba0 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/man/tmpfiles.d.xml b/man/tmpfiles.d.xml index 79d0ff6bdd..04617bc532 100644 --- a/man/tmpfiles.d.xml +++ b/man/tmpfiles.d.xml @@ -160,9 +160,10 @@ L /tmp/foobar - - - - /dev/null Type - The type consists of a single letter and optionally a plus sign (+), - exclamation mark (!), minus sign (-), equals sign - (=) and/or tilde character (~). + The type consists of a single letter and optionally one or emore modifier characters: a plus sign + (+), exclamation mark (!), minus sign (-), + equals sign (=), tilde character (~) and/or caret + (^). The following line types are understood: @@ -461,6 +462,10 @@ L /tmp/foobar - - - - /dev/null symlinks. + + + + Type Modifiers If the exclamation mark (!) is used, this line is only safe to execute during boot, and can break a running system. Lines without the exclamation mark are presumed to be safe to @@ -493,9 +498,19 @@ w- /proc/sys/vm/swappiness - - - - 10 If the tilde character (~) is used, the argument (i.e. 6th) column is Base64 decoded before use. This modifier is only supported on line types that can write file contents, i.e. f, - f+, w. This is useful for writing arbitrary binary data - (including newlines and NUL bytes) to files. Note that if this switch is used, the argument is not - subject to specifier expansion, neither before nor after Base64 decoding. + f+, w, +. This is useful for writing arbitrary + binary data (including newlines and NUL bytes) to files. Note that if this switch is used, the argument + is not subject to specifier expansion, neither before nor after Base64 decoding. + + If the caret character (^) is used, the argument (i.e. 6th) column takes a + service credential name to read the argument data from. See System and Service Credentials for details about the + credentials concept. This modifier is only supported on line types that can write file contents, + i.e. f, f+, w, w+. This is + useful for writing arbitrary files with contents sourced from elsewhere, including from VM or container + managers further up. If the specified credential is not set for the systemd-tmpfiles + service, the line is silently skipped. If ^ and ~ are combined + Base64 decoding is applied to the credential contents. Note that for all line types that result in creation of any kind of file node (i.e. f/F, diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 0c50c8e1ee..07432a1e51 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" @@ -2977,7 +2978,7 @@ static int parse_line( ItemArray *existing; OrderedHashmap *h; int r, pos; - bool append_or_force = false, boot = false, allow_failure = false, try_replace = false, unbase64 = false; + bool append_or_force = false, boot = false, allow_failure = false, try_replace = false, unbase64 = false, from_cred = false; assert(fname); assert(line >= 1); @@ -3050,6 +3051,8 @@ static int parse_line( try_replace = true; else if (action[pos] == '~' && !unbase64) unbase64 = true; + else if (action[pos] == '^' && !from_cred) + from_cred = true; else { *invalid_config = true; return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Unknown modifiers in command '%s'", action); @@ -3239,13 +3242,8 @@ static int parse_line( if (!should_include_path(i.path)) return 0; - if (unbase64) { - if (i.argument) { - r = unbase64mem(i.argument, SIZE_MAX, &i.binary_argument, &i.binary_argument_size); - if (r < 0) - return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to base64 decode specified argument '%s': %m", i.argument); - } - } else { + if (!unbase64) { + /* Do specifier expansion except if base64 mode is enabled */ r = specifier_expansion_from_arg(specifier_table, &i); if (r == -ENXIO) return log_unresolvable_specifier(fname, line); @@ -3256,6 +3254,35 @@ static int parse_line( } } + if (from_cred) { + if (!i.argument) + return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL), "Reading from credential requested, but no credential name specified."); + if (!credential_name_valid(i.argument)) + return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL), "Credential name not valid: %s", i.argument); + + r = read_credential(i.argument, &i.binary_argument, &i.binary_argument_size); + if (IN_SET(r, -ENXIO, -ENOENT)) { + /* Silently skip over lines that have no credentials passed */ + log_syntax(NULL, LOG_INFO, fname, line, 0, "Credential '%s' not specified, skipping line.", i.argument); + return 0; + } + if (r < 0) + return log_error_errno(r, "Failed to read credential '%s': %m", i.argument); + } + + /* If base64 decoding is requested, do so now */ + if (unbase64 && item_binary_argument(&i)) { + _cleanup_free_ void *data = NULL; + size_t data_size = 0; + + r = unbase64mem(item_binary_argument(&i), item_binary_argument_size(&i), &data, &data_size); + if (r < 0) + return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to base64 decode specified argument '%s': %m", i.argument); + + free_and_replace(i.binary_argument, data); + i.binary_argument_size = data_size; + } + if (!empty_or_root(arg_root)) { char *p; @@ -3594,7 +3621,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 +3768,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 +3943,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 8f66f1c7b8..5269eaa437 100755 --- a/test/TEST-54-CREDS/test.sh +++ b/test/TEST-54-CREDS/test.sh @@ -3,9 +3,29 @@ 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" -KERNEL_APPEND="${KERNEL_APPEND:-} systemd.set_credential=kernelcmdlinecred:uff systemd.set_credential=sysctl.extra:kernel.domainname=sysctltest rd.systemd.import_credentials=no" + +NSPAWN_CREDS=( + "--set-credential=mynspawncredential:strangevalue" +) +NSPAWN_ARGUMENTS="${NSPAWN_ARGUMENTS:-} ${NSPAWN_CREDS[*]}" + +QEMU_CREDS=( + "-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==" +) +QEMU_OPTIONS="${QEMU_OPTIONS:-} ${QEMU_CREDS[*]}" + +KERNEL_CREDS=( + "systemd.set_credential=kernelcmdlinecred:uff" + "systemd.set_credential=sysctl.extra:kernel.domainname=sysctltest" + "systemd.set_credential=login.motd:hello" + "systemd.set_credential=login.issue:welcome" + "rd.systemd.import_credentials=no" +) +KERNEL_APPEND="${KERNEL_APPEND:-} ${KERNEL_CREDS[*]}" # shellcheck source=test/test-functions . "${TEST_BASE_DIR:?}/test-functions" diff --git a/test/units/testsuite-54.sh b/test/units/testsuite-54.sh index a7ccdca032..dc0c5f554f 100755 --- a/test/units/testsuite-54.sh +++ b/test/units/testsuite-54.sh @@ -43,6 +43,11 @@ 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" ] + [ "$(cat /etc/motd.d/50-provision.conf)" = "hello" ] + [ "$(cat /etc/issue.d/50-provision.conf)" = "welcome" ] else echo "qemu_fw_cfg support missing in kernel. Sniff!" expected_credential="" diff --git a/tmpfiles.d/meson.build b/tmpfiles.d/meson.build index 4546169694..ca1abbf3fe 100644 --- a/tmpfiles.d/meson.build +++ b/tmpfiles.d/meson.build @@ -14,6 +14,7 @@ files = [['README', ''], ['systemd-tmp.conf', ''], ['tmp.conf', ''], ['x11.conf', ''], + ['provision.conf', ''], ] foreach pair : files diff --git a/tmpfiles.d/provision.conf b/tmpfiles.d/provision.conf new file mode 100644 index 0000000000..9a4783868f --- /dev/null +++ b/tmpfiles.d/provision.conf @@ -0,0 +1,17 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +# See tmpfiles.d(5) for details + +# Provision additional loging messages from credentials, if they are set. Note +# that these lines are NOPs if the credentials are not set or if the files +# already exist. +f^ /etc/motd.d/50-provision.conf - - - - login.motd +f^ /etc/issue.d/50-provision.conf - - - - login.issue + +# Provision a /etc/hosts file from credentials. +f^ /etc/hosts - - - - network.hosts diff --git a/units/systemd-tmpfiles-clean.service b/units/systemd-tmpfiles-clean.service index 7aee6463bd..6ae4e74ddd 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 0babe78767..ad0e54fcc4 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 bc29dbc8c9..fc6a4a0e0b 100644 --- a/units/systemd-tmpfiles-setup.service +++ b/units/systemd-tmpfiles-setup.service @@ -21,3 +21,7 @@ Type=oneshot RemainAfterExit=yes ExecStart=systemd-tmpfiles --create --remove --boot --exclude-prefix=/dev SuccessExitStatus=DATAERR CANTCREAT +LoadCredential=tmpfiles.extra +LoadCredential=login.motd +LoadCredential=login.issue +LoadCredential=network.hosts