From 8914f7e8e41c0fcd05d3d8d4fe39ce0e6addac69 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 30 Jun 2023 10:22:35 +0200 Subject: [PATCH 01/22] man: make sure credentials properly show up in directives index --- man/directives-template.xml | 9 +++++++++ man/systemd-firstboot.xml | 18 +++++++++--------- man/systemd-resolved.service.xml | 2 +- man/systemd-sysctl.service.xml | 4 ++-- man/systemd-sysusers.xml | 10 +++++----- man/systemd-tmpfiles.xml | 4 ++-- man/systemd-vconsole-setup.service.xml | 2 +- man/systemd.system-credentials.xml | 4 ++-- man/systemd.xml | 2 +- src/firstboot/firstboot.c | 2 +- 10 files changed, 33 insertions(+), 24 deletions(-) diff --git a/man/directives-template.xml b/man/directives-template.xml index 114707d2d0..0b6ee21929 100644 --- a/man/directives-template.xml +++ b/man/directives-template.xml @@ -49,6 +49,15 @@ + + System Credentials + + System credentials understood by the system and service manager and various other + components: + + + + EFI variables diff --git a/man/systemd-firstboot.xml b/man/systemd-firstboot.xml index becb5f52ac..192b91c296 100644 --- a/man/systemd-firstboot.xml +++ b/man/systemd-firstboot.xml @@ -308,10 +308,10 @@ (see systemd.exec1 for details). The following credentials are used when passed in: - + - passwd.hashed-password.root - passwd.plaintext-password.root + passwd.hashed-password.root + passwd.plaintext-password.root A hashed or plaintext version of the root password to use, in place of prompting the user. These credentials are equivalent to the same ones defined for the @@ -320,7 +320,7 @@ - passwd.shell.root + passwd.shell.root Specifies the shell binary to use for the specified account. Equivalent to the credential of the same name defined for the @@ -329,20 +329,20 @@ - firstboot.locale - firstboot.locale-messages + firstboot.locale + firstboot.locale-messages These credentials specify the locale settings to set during first boot, in place of prompting the user. - firstboot.keymap + firstboot.keymap This credential specifies the keyboard setting to set during first boot, in place of prompting the user. - Note the relationship to the vconsole.keymap credential understood by + Note the relationship to the vconsole.keymap credential understood by systemd-vconsole-setup.service8: both ultimately affect the same setting, but firstboot.keymap is written into /etc/vconsole.conf on first boot (if not already configured), and then read from @@ -352,7 +352,7 @@ - firstboot.timezone + firstboot.timezone This credential specifies the system timezone setting to set during first boot, in place of prompting the user. diff --git a/man/systemd-resolved.service.xml b/man/systemd-resolved.service.xml index 05d20bbf35..5a87f04832 100644 --- a/man/systemd-resolved.service.xml +++ b/man/systemd-resolved.service.xml @@ -407,7 +407,7 @@ search foobar.com barbar.com (see systemd.exec1 for details). The following credentials are used when passed in: - + network.dns network.search_domains diff --git a/man/systemd-sysctl.service.xml b/man/systemd-sysctl.service.xml index 4174184c15..975ffadefa 100644 --- a/man/systemd-sysctl.service.xml +++ b/man/systemd-sysctl.service.xml @@ -89,9 +89,9 @@ (see systemd.exec1 for details). The following credentials are used when passed in: - + - sysctl.extra + sysctl.extra The contents of this credential may contain additional lines to operate on. The credential contents should follow the same format as any other sysctl.d/ drop-in diff --git a/man/systemd-sysusers.xml b/man/systemd-sysusers.xml index 34d3cab5c7..49f634b180 100644 --- a/man/systemd-sysusers.xml +++ b/man/systemd-sysusers.xml @@ -143,9 +143,9 @@ (see systemd.exec1 for details). The following credentials are used when passed in: - + - passwd.hashed-password.user + passwd.hashed-password.user A UNIX hashed password string to use for the specified user, when creating an entry for it. This is particularly useful for the root user as it allows provisioning the default root password to use via a unit file drop-in or from a container manager passing in this @@ -155,7 +155,7 @@ - passwd.plaintext-password.user + passwd.plaintext-password.user Similar to passwd.hashed-password.user but expect a literal, plaintext password, which is then automatically hashed before used for the user @@ -166,13 +166,13 @@ - passwd.shell.user + passwd.shell.user Specifies the shell binary to use for the specified account when creating it. - sysusers.extra + sysusers.extra The contents of this credential may contain additional lines to operate on. The credential contents should follow the same format as any other sysusers.d/ diff --git a/man/systemd-tmpfiles.xml b/man/systemd-tmpfiles.xml index 3a9699ff4b..decd66d5c6 100644 --- a/man/systemd-tmpfiles.xml +++ b/man/systemd-tmpfiles.xml @@ -250,9 +250,9 @@ (see systemd.exec1 for details). The following credentials are used when passed in: - + - tmpfiles.extra + 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/ diff --git a/man/systemd-vconsole-setup.service.xml b/man/systemd-vconsole-setup.service.xml index f9f8327a68..e462ef8f60 100644 --- a/man/systemd-vconsole-setup.service.xml +++ b/man/systemd-vconsole-setup.service.xml @@ -57,7 +57,7 @@ (see systemd.exec1 for details). The following credentials are used when passed in: - + vconsole.keymap vconsole.keymap_toggle diff --git a/man/systemd.system-credentials.xml b/man/systemd.system-credentials.xml index f75a83cc3f..97507cf252 100644 --- a/man/systemd.system-credentials.xml +++ b/man/systemd.system-credentials.xml @@ -40,7 +40,7 @@ Well known system credentials - + firstboot.keymap @@ -52,7 +52,7 @@ firstboot.locale - firstboot.locale-message + firstboot.locale-messages The system locale to set (e.g. de_DE.UTF-8). Read by systemd-firstboot1, diff --git a/man/systemd.xml b/man/systemd.xml index 3ceac5f919..d63e70f0c8 100644 --- a/man/systemd.xml +++ b/man/systemd.xml @@ -1053,7 +1053,7 @@ The service manager when run as PID 1 reads the following system credentials: - + vmm.notify_socket diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 29fc61dd67..076e06e821 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -334,7 +334,7 @@ static int prompt_locale(int rfd) { r = read_credential("firstboot.locale-messages", (void**) &arg_locale_messages, NULL); if (r < 0) - log_debug_errno(r, "Failed to read credential firstboot.locale-message, ignoring: %m"); + log_debug_errno(r, "Failed to read credential firstboot.locale-messages, ignoring: %m"); else acquired_from_creds = true; From f76ce81b91db1dac0d0a012e1cc903639002dd0a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 4 Jul 2023 22:26:52 +0200 Subject: [PATCH 02/22] execute: fix credential dir handling for fs which support ACLs When the credential dir is backed by an fs that supports ACLs we must be more careful with adjusting the 'x' bit of the directory, as any chmod() call on the dir will reset the mask entry of the ACL entirely which we don't want. Hence, do a manual set of ACL changes, that only add/drop the 'x' bit but otherwise leave the ACL as it is. This matters if we use tmpfs rather than ramfs to store credentials. --- src/core/execute.c | 9 ++- src/shared/acl-util.c | 161 +++++++++++++++++++++++++++++++++++++++ src/shared/acl-util.h | 15 ++++ src/shared/meson.build | 2 +- src/test/test-acl-util.c | 58 ++++++++++++++ 5 files changed, 242 insertions(+), 3 deletions(-) diff --git a/src/core/execute.c b/src/core/execute.c index d850a68022..8cc1a0f76b 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -3182,6 +3182,10 @@ static int acquire_credentials( if (dfd < 0) return -errno; + r = fd_acl_make_writable(dfd); /* Add the "w" bit, if we are reusing an already set up credentials dir where it was unset */ + if (r < 0) + return r; + /* First, load credentials off disk (or acquire via AF_UNIX socket) */ HASHMAP_FOREACH(lc, context->load_credentials) { _cleanup_close_ int sub_fd = -EBADF; @@ -3313,8 +3317,9 @@ static int acquire_credentials( left -= add; } - if (fchmod(dfd, 0500) < 0) /* Now take away the "w" bit */ - return -errno; + r = fd_acl_make_read_only(dfd); /* Now take away the "w" bit */ + if (r < 0) + return r; /* After we created all keys with the right perms, also make sure the credential store as a whole is * accessible */ diff --git a/src/shared/acl-util.c b/src/shared/acl-util.c index 5c0c4e21aa..7bfe02573a 100644 --- a/src/shared/acl-util.c +++ b/src/shared/acl-util.c @@ -7,10 +7,13 @@ #include "acl-util.h" #include "alloc-util.h" +#include "errno-util.h" #include "string-util.h" #include "strv.h" #include "user-util.h" +#if HAVE_ACL + int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *ret_entry) { acl_entry_t i; int r; @@ -489,3 +492,161 @@ int fd_add_uid_acl_permission( return 0; } + +int fd_acl_make_read_only(int fd) { + _cleanup_(acl_freep) acl_t acl = NULL; + bool changed = false; + acl_entry_t i; + int r; + + assert(fd >= 0); + + /* Safely drops all W bits from all relevant ACL entries of the file, without changing entries which + * are masked by the ACL mask */ + + acl = acl_get_fd(fd); + if (!acl) { + + if (!ERRNO_IS_NOT_SUPPORTED(errno)) + return -errno; + + /* No ACLs? Then just update the regular mode_t */ + return fd_acl_make_read_only_fallback(fd); + } + + for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); + r > 0; + r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { + acl_permset_t permset; + acl_tag_t tag; + int b; + + if (acl_get_tag_type(i, &tag) < 0) + return -errno; + + /* These three control the x bits overall (as ACL_MASK affects all remaining tags) */ + if (!IN_SET(tag, ACL_USER_OBJ, ACL_MASK, ACL_OTHER)) + continue; + + if (acl_get_permset(i, &permset) < 0) + return -errno; + + b = acl_get_perm(permset, ACL_WRITE); + if (b < 0) + return -errno; + + if (b) { + if (acl_delete_perm(permset, ACL_WRITE) < 0) + return -errno; + + changed = true; + } + } + if (r < 0) + return -errno; + + if (!changed) + return 0; + + if (acl_set_fd(fd, acl) < 0) { + if (!ERRNO_IS_NOT_SUPPORTED(errno)) + return -errno; + + return fd_acl_make_read_only_fallback(fd); + } + + return 1; +} + +int fd_acl_make_writable(int fd) { + _cleanup_(acl_freep) acl_t acl = NULL; + acl_entry_t i; + int r; + + /* Safely adds the writable bit to the owner's ACL entry of this inode. (And only the owner's! – This + * not the obvious inverse of fd_acl_make_read_only() hence!) */ + + acl = acl_get_fd(fd); + if (!acl) { + if (!ERRNO_IS_NOT_SUPPORTED(errno)) + return -errno; + + /* No ACLs? Then just update the regular mode_t */ + return fd_acl_make_writable_fallback(fd); + } + + for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); + r > 0; + r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { + acl_permset_t permset; + acl_tag_t tag; + int b; + + if (acl_get_tag_type(i, &tag) < 0) + return -errno; + + if (tag != ACL_USER_OBJ) + continue; + + if (acl_get_permset(i, &permset) < 0) + return -errno; + + b = acl_get_perm(permset, ACL_WRITE); + if (b < 0) + return -errno; + + if (b) + return 0; /* Already set? Then there's nothing to do. */ + + if (acl_add_perm(permset, ACL_WRITE) < 0) + return -errno; + + break; + } + if (r < 0) + return -errno; + + if (acl_set_fd(fd, acl) < 0) { + if (!ERRNO_IS_NOT_SUPPORTED(errno)) + return -errno; + + return fd_acl_make_writable_fallback(fd); + } + + return 1; +} +#endif + +int fd_acl_make_read_only_fallback(int fd) { + struct stat st; + + assert(fd >= 0); + + if (fstat(fd, &st) < 0) + return -errno; + + if ((st.st_mode & 0222) == 0) + return 0; + + if (fchmod(fd, st.st_mode & 0555) < 0) + return -errno; + + return 1; +} + +int fd_acl_make_writable_fallback(int fd) { + struct stat st; + + assert(fd >= 0); + + if (fstat(fd, &st) < 0) + return -errno; + + if ((st.st_mode & 0200) != 0) /* already set */ + return 0; + + if (fchmod(fd, (st.st_mode & 07777) | 0200) < 0) + return -errno; + + return 1; +} diff --git a/src/shared/acl-util.h b/src/shared/acl-util.h index 978389ed1d..ef315c2f11 100644 --- a/src/shared/acl-util.h +++ b/src/shared/acl-util.h @@ -4,6 +4,9 @@ #include #include +int fd_acl_make_read_only_fallback(int fd); +int fd_acl_make_writable_fallback(int fd); + #if HAVE_ACL #include #include @@ -24,6 +27,9 @@ int parse_acl( int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *ret); int fd_add_uid_acl_permission(int fd, uid_t uid, unsigned mask); +int fd_acl_make_read_only(int fd); +int fd_acl_make_writable(int fd); + /* acl_free takes multiple argument types. * Multiple cleanup functions are necessary. */ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(acl_t, acl_free, NULL); @@ -42,4 +48,13 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(gid_t*, acl_free_gid_tp, NULL); static inline int fd_add_uid_acl_permission(int fd, uid_t uid, unsigned mask) { return -EOPNOTSUPP; } + +static inline int fd_acl_make_read_only(int fd) { + return fd_acl_make_read_only_fallback(fd); +} + +static inline int fd_acl_make_writable(int fd) { + return fd_acl_make_writable_fallback(fd); +} + #endif diff --git a/src/shared/meson.build b/src/shared/meson.build index 1e015bd38e..d643b2bd09 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -1,6 +1,7 @@ # SPDX-License-Identifier: LGPL-2.1-or-later shared_sources = files( + 'acl-util.c', 'acpi-fpdt.c', 'apparmor-util.c', 'ask-password-api.c', @@ -189,7 +190,6 @@ syscall_list_h = custom_target( if conf.get('HAVE_ACL') == 1 shared_sources += files( - 'acl-util.c', 'devnode-acl.c', ) endif diff --git a/src/test/test-acl-util.c b/src/test/test-acl-util.c index 093eaaa01b..eb9678a7d9 100644 --- a/src/test/test-acl-util.c +++ b/src/test/test-acl-util.c @@ -69,4 +69,62 @@ TEST_RET(add_acls_for_user) { return 0; } +TEST(fd_acl_make_read_only) { + _cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-empty.XXXXXX"; + _cleanup_close_ int fd = -EBADF; + const char *cmd; + struct stat st; + + fd = mkostemp_safe(fn); + assert_se(fd >= 0); + + /* make it more exciting */ + (void) fd_add_uid_acl_permission(fd, 1, ACL_READ|ACL_WRITE|ACL_EXECUTE); + + assert_se(fstat(fd, &st) >= 0); + assert_se((st.st_mode & 0200) == 0200); + + cmd = strjoina("getfacl -p ", fn); + assert_se(system(cmd) == 0); + + cmd = strjoina("stat ", fn); + assert_se(system(cmd) == 0); + + log_info("read-only"); + assert_se(fd_acl_make_read_only(fd)); + + assert_se(fstat(fd, &st) >= 0); + assert_se((st.st_mode & 0222) == 0000); + + cmd = strjoina("getfacl -p ", fn); + assert_se(system(cmd) == 0); + + cmd = strjoina("stat ", fn); + assert_se(system(cmd) == 0); + + log_info("writable"); + assert_se(fd_acl_make_writable(fd)); + + assert_se(fstat(fd, &st) >= 0); + assert_se((st.st_mode & 0222) == 0200); + + cmd = strjoina("getfacl -p ", fn); + assert_se(system(cmd) == 0); + + cmd = strjoina("stat ", fn); + assert_se(system(cmd) == 0); + + log_info("read-only"); + assert_se(fd_acl_make_read_only(fd)); + + assert_se(fstat(fd, &st) >= 0); + assert_se((st.st_mode & 0222) == 0000); + + cmd = strjoina("getfacl -p ", fn); + assert_se(system(cmd) == 0); + + cmd = strjoina("stat ", fn); + assert_se(system(cmd) == 0); +} + DEFINE_TEST_MAIN(LOG_INFO); From 0dea5b7719b6dc0e2026923fc6ad0a80a8fd1db5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 23 Jun 2023 17:49:44 +0200 Subject: [PATCH 03/22] import-creds: define a new dir where initrd configurators can pass credentials to host --- man/systemd.exec.xml | 20 +++--- src/core/import-creds.c | 140 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 148 insertions(+), 12 deletions(-) diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 5a917d8349..ccec6ec423 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -3274,18 +3274,20 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX 11) with a prefix of io.systemd.credential: or io.systemd.credential.binary:. In both cases a key/value pair separated by = is expected, in the latter case the right-hand side is Base64 decoded when - parsed (thus permitting binary data to be passed in). Example - qemu - switch: -smbios + parsed (thus permitting binary data to be passed in). Example qemu switch: -smbios type=11,value=io.systemd.credential:xx=yy, or -smbios type=11,value=io.systemd.credential.binary:rick=TmV2ZXIgR29ubmEgR2l2ZSBZb3UgVXA=. Alternatively, use the qemu fw_cfg node - opt/io.systemd.credentials/. Example qemu switch: -fw_cfg - name=opt/io.systemd.credentials/mycred,string=supersecret. They may also be specified on - the kernel command line using the systemd.set_credential= switch (see - systemd1) and from - the UEFI firmware environment via - systemd-stub7. + opt/io.systemd.credentials/. Example qemu switch: + -fw_cfg name=opt/io.systemd.credentials/mycred,string=supersecret. They may also + be passed from the UEFI firmware environment via + systemd-stub7, + from the initrd (see + systemd1), or be + specified on the kernel command line using the systemd.set_credential= switch (see + systemd1 – this is + not recommended since unprivileged userspace can read the kernel command line). If referencing an AF_UNIX stream socket to connect to, the connection will originate from an abstract namespace socket, that includes information about the unit and the diff --git a/src/core/import-creds.c b/src/core/import-creds.c index ade509be34..6b1fca6f59 100644 --- a/src/core/import-creds.c +++ b/src/core/import-creds.c @@ -611,27 +611,157 @@ static int import_credentials_smbios(ImportCredentialContext *c) { return 0; } +static int import_credentials_initrd(ImportCredentialContext *c) { + _cleanup_free_ DirectoryEntries *de = NULL; + _cleanup_close_ int source_dir_fd = -EBADF; + int r; + + assert(c); + + /* This imports credentials from /run/credentials/@initrd/ into our credentials directory and deletes + * the source directory afterwards. This is run once after the initrd → host transition. This is + * supposed to establish a well-defined avenue for initrd-based host configurators to pass + * credentials into the main system. */ + + if (in_initrd()) + return 0; + + source_dir_fd = open("/run/credentials/@initrd", O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); + if (source_dir_fd < 0) { + if (errno == ENOENT) + log_debug_errno(errno, "No credentials passed from initrd."); + else + log_warning_errno(errno, "Failed to open '/run/credentials/@initrd', ignoring: %m"); + return 0; + } + + r = readdir_all(source_dir_fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT, &de); + if (r < 0) { + log_warning_errno(r, "Failed to read '/run/credentials/@initrd' contents, ignoring: %m"); + return 0; + } + + FOREACH_ARRAY(entry, de->entries, de->n_entries) { + _cleanup_close_ int cfd = -EBADF, nfd = -EBADF; + const struct dirent *d = *entry; + struct stat st; + + if (!credential_name_valid(d->d_name)) { + log_warning("Credential '%s' has invalid name, ignoring.", d->d_name); + continue; + } + + cfd = openat(source_dir_fd, d->d_name, O_RDONLY|O_CLOEXEC); + if (cfd < 0) { + log_warning_errno(errno, "Failed to open %s, ignoring: %m", d->d_name); + continue; + } + + if (fstat(cfd, &st) < 0) { + log_warning_errno(errno, "Failed to stat %s, ignoring: %m", d->d_name); + continue; + } + + r = stat_verify_regular(&st); + if (r < 0) { + log_warning_errno(r, "Credential file %s is not a regular file, ignoring: %m", d->d_name); + continue; + } + + if (!credential_size_ok(c, d->d_name, st.st_size)) + continue; + + r = acquire_credential_directory(c); + if (r < 0) + return r; + + nfd = open_credential_file_for_write(c->target_dir_fd, SYSTEM_CREDENTIALS_DIRECTORY, d->d_name); + if (nfd == -EEXIST) + continue; + if (nfd < 0) + return nfd; + + r = copy_bytes(cfd, nfd, st.st_size, 0); + if (r < 0) { + (void) unlinkat(c->target_dir_fd, d->d_name, 0); + return log_error_errno(r, "Failed to create credential '%s': %m", d->d_name); + } + + c->size_sum += st.st_size; + c->n_credentials++; + + log_debug("Successfully copied initrd credential '%s'.", d->d_name); + + (void) unlinkat(source_dir_fd, d->d_name, 0); + } + + source_dir_fd = safe_close(source_dir_fd); + + if (rmdir("/run/credentials/@initrd") < 0) + log_warning_errno(errno, "Failed to remove /run/credentials/@initrd after import, ignoring: %m"); + + return 0; +} + static int import_credentials_trusted(void) { _cleanup_(import_credentials_context_free) ImportCredentialContext c = { .target_dir_fd = -EBADF, }; - int q, w, r; + int q, w, r, y; + + /* This is invoked during early boot when no credentials have been imported so far. (Specifically, if + * the $CREDENTIALS_DIRECTORY or $ENCRYPTED_CREDENTIALS_DIRECTORY environment variables are not set + * yet.) */ r = import_credentials_qemu(&c); w = import_credentials_smbios(&c); q = import_credentials_proc_cmdline(&c); + y = import_credentials_initrd(&c); if (c.n_credentials > 0) { int z; - log_debug("Imported %u credentials from kernel command line/smbios/fw_cfg.", c.n_credentials); + log_debug("Imported %u credentials from kernel command line/smbios/fw_cfg/initrd.", c.n_credentials); z = finalize_credentials_dir(SYSTEM_CREDENTIALS_DIRECTORY, "CREDENTIALS_DIRECTORY"); if (z < 0) return z; } - return r < 0 ? r : w < 0 ? w : q; + return r < 0 ? r : w < 0 ? w : q < 0 ? q : y; +} + +static int merge_credentials_trusted(const char *creds_dir) { + _cleanup_(import_credentials_context_free) ImportCredentialContext c = { + .target_dir_fd = -EBADF, + }; + int r; + + /* This is invoked after the initrd → host transitions, when credentials already have been imported, + * but we might want to import some more from the initrd. */ + + if (in_initrd()) + return 0; + + /* Do not try to merge initrd credentials into foreign credentials directories */ + if (!path_equal_ptr(creds_dir, SYSTEM_CREDENTIALS_DIRECTORY)) { + log_debug("Not importing initrd credentials, as foreign $CREDENTIALS_DIRECTORY has been set."); + return 0; + } + + r = import_credentials_initrd(&c); + + if (c.n_credentials > 0) { + int z; + + log_debug("Merged %u credentials from initrd.", c.n_credentials); + + z = finalize_credentials_dir(SYSTEM_CREDENTIALS_DIRECTORY, "CREDENTIALS_DIRECTORY"); + if (z < 0) + return z; + } + + return r; } static int symlink_credential_dir(const char *envvar, const char *path, const char *where) { @@ -690,6 +820,10 @@ int import_credentials(void) { r = q; } + q = merge_credentials_trusted(received_creds_dir); + if (r >= 0) + r = q; + } else { _cleanup_free_ char *v = NULL; From 49e859b7c7cb0c7f40e7e914e2f4af7d4fdc9da4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 23 Jun 2023 21:48:15 +0200 Subject: [PATCH 04/22] creds-util: add new helper read_credential_with_decryption() This is just like read_credential() but also looks into the encrypted credential directory, not just the regular one. Normally, we decrypt credentials at the moment we pass them to services. From service PoV all credentials are hence decrypted credentials. However, when we want to access credentials in a generator this logic does not apply: here we have the regular and the encrypted credentials directory. So far we didn't attempt to make use of credentials in generators hence. Let's address and add helper that looks into both directories, and talks to the TPM if necessary to decrypt the credentials. --- src/shared/creds-util.c | 74 +++++++++++++++++++++++++++++++++++++++++ src/shared/creds-util.h | 3 +- 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/src/shared/creds-util.c b/src/shared/creds-util.c index 83a4c84aa9..f54481e9b4 100644 --- a/src/shared/creds-util.c +++ b/src/shared/creds-util.c @@ -125,6 +125,80 @@ int read_credential(const char *name, void **ret, size_t *ret_size) { (char**) ret, ret_size); } +int read_credential_with_decryption(const char *name, void **ret, size_t *ret_size) { + _cleanup_(erase_and_freep) void *data = NULL; + _cleanup_free_ char *fn = NULL; + size_t sz = 0; + const char *d; + int r; + + assert(ret); + + /* Just like read_credential() but will also look for encrypted credentials. Note that services only + * receive decrypted credentials, hence use read_credential() for those. This helper here is for + * generators, i.e. code that runs outside of service context, and thus has no decrypted credentials + * yet. + * + * Note that read_credential_harder_and_warn() logs on its own, while read_credential() does not! + * (It's a lot more complex and error prone given its TPM2 connectivty, and is generally called from + * generators only where logging is OK). + * + * Error handling is also a bit different: if we can't find a credential we'll return 0 and NULL + * pointers/zero size, rather than -ENXIO/-ENOENT. */ + + if (!credential_name_valid(name)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid credential name: %s", name); + + r = read_credential(name, ret, ret_size); + if (r >= 0) + return 1; /* found */ + if (!IN_SET(r, -ENXIO, -ENOENT)) + return log_error_errno(r, "Failed read unencrypted credential '%s': %m", name); + + r = get_encrypted_credentials_dir(&d); + if (r == -ENXIO) + goto not_found; + if (r < 0) + return log_error_errno(r, "Failed to determine encrypted credentials directory: %m"); + + fn = path_join(d, name); + if (!fn) + return log_oom(); + + r = read_full_file_full( + AT_FDCWD, fn, + UINT64_MAX, SIZE_MAX, + READ_FULL_FILE_SECURE, + NULL, + (char**) data, &sz); + if (r == -ENOENT) + goto not_found; + if (r < 0) + return log_error_errno(r, "Failed to read encrypted credential data: %m"); + + r = decrypt_credential_and_warn( + name, + now(CLOCK_REALTIME), + /* tpm2_device = */ NULL, + /* tpm2_signature_path = */ NULL, + data, + sz, + ret, + ret_size); + if (r < 0) + return r; + + return 1; /* found */ + +not_found: + *ret = NULL; + + if (ret_size) + *ret_size = 0; + + return 0; /* not found */ +} + int read_credential_strings_many_internal( const char *first_name, char **first_value, ...) { diff --git a/src/shared/creds-util.h b/src/shared/creds-util.h index 1742678cb9..8fbd61e9fe 100644 --- a/src/shared/creds-util.h +++ b/src/shared/creds-util.h @@ -35,7 +35,8 @@ int get_encrypted_credentials_dir(const char **ret); #define SYSTEM_CREDENTIALS_DIRECTORY "/run/credentials/@system" #define ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY "/run/credentials/@encrypted" -int read_credential(const char *name, void **ret, size_t *ret_size); +int read_credential(const char *name, void **ret, size_t *ret_size); /* use in services! */ +int read_credential_with_decryption(const char *name, void **ret, size_t *ret_size); /* use in generators + pid1! */ int read_credential_strings_many_internal(const char *first_name, char **first_value, ...); From d021aa8ee372e2503e52f9897863e0dac505276a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 28 Jun 2023 18:10:40 +0200 Subject: [PATCH 05/22] import-creds: pick up vmm.notify_socket also from encrypted credentials Now that we have the infra in place, make PID 1 pick up encrypted credentials too. (While we are at it, split this out into its own helper) --- src/core/import-creds.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/core/import-creds.c b/src/core/import-creds.c index 6b1fca6f59..8c170d6fe5 100644 --- a/src/core/import-creds.c +++ b/src/core/import-creds.c @@ -787,6 +787,23 @@ static int symlink_credential_dir(const char *envvar, const char *path, const ch return 0; } +static int setenv_notify_socket(void) { + _cleanup_free_ char *address = NULL; + int r; + + r = read_credential_with_decryption("vmm.notify_socket", (void **)&address, /* ret_size= */ NULL); + if (r < 0) + return log_warning_errno(r, "Failed to read 'vmm.notify_socket' credential, ignoring: %m"); + + if (isempty(address)) + return 0; + + if (setenv("NOTIFY_SOCKET", address, /* replace= */ 1) < 0) + return log_warning_errno(errno, "Failed to set $NOTIFY_SOCKET environment variable, ignoring: %m"); + + return 1; +} + int import_credentials(void) { const char *received_creds_dir = NULL, *received_encrypted_creds_dir = NULL; bool envvar_set = false; @@ -847,18 +864,8 @@ int import_credentials(void) { r = q; } - if (r >= 0) { - _cleanup_free_ char *address = NULL; - - r = read_credential("vmm.notify_socket", (void **)&address, /* ret_size= */ NULL); - if (r < 0 && !IN_SET(r, -ENOENT, -ENXIO)) - log_warning_errno(r, "Failed to read 'vmm.notify_socket' credential, ignoring: %m"); - else if (r >= 0 && !isempty(address)) { - r = setenv("NOTIFY_SOCKET", address, /* replace= */ 1); - if (r < 0) - log_warning_errno(errno, "Failed to set $NOTIFY_SOCKET environment variable, ignoring: %m"); - } - } + /* Propagate vmm_notify_socket credential → $NOTIFY_SOCKET env var */ + (void) setenv_notify_socket(); return r; } From deb0d489ea1e646f1600c2cfd90c9e56f25fa041 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 28 Jun 2023 18:11:15 +0200 Subject: [PATCH 06/22] core: consult credentials for machine ID to use for host Let's hook up one more thing with credentials: the machine ID to use when none is initialized yet. This requires some reordering of initialization steps in PID 1: we need to import credentials first, and only then initialize the machine ID. --- man/systemd.system-credentials.xml | 9 +++++++++ man/systemd.xml | 10 ++++++++++ src/core/main.c | 11 ++++++----- src/shared/machine-id-setup.c | 24 ++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/man/systemd.system-credentials.xml b/man/systemd.system-credentials.xml index 97507cf252..ceb84d29b9 100644 --- a/man/systemd.system-credentials.xml +++ b/man/systemd.system-credentials.xml @@ -208,6 +208,15 @@ systemd1. + + + system.machine_id + + Takes a 128bit ID to initialize the machine ID from (if it is not set yet). Interpreted by + the service manager (PID 1). For details see + systemd1. + + diff --git a/man/systemd.xml b/man/systemd.xml index d63e70f0c8..2cffe01aff 100644 --- a/man/systemd.xml +++ b/man/systemd.xml @@ -1069,6 +1069,16 @@ notification via VSOCK when a virtual machine has finished booting. + + + system.machine_id + + Takes a 128bit hexadecimal ID to initialize /etc/machine-id from, if the + file is not set up yet. See + machine-id5 for + details. + + diff --git a/src/core/main.c b/src/core/main.c index 3f78f035d0..6290ec131c 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -2224,10 +2224,15 @@ static int initialize_runtime( return r; } + /* Pull credentials from various sources into a common credential directory (we do + * this here, before setting up the machine ID, so that we can use credential info + * for setting up the machine ID) */ + (void) import_credentials(); + (void) os_release_status(); (void) hostname_setup(true); /* Force transient machine-id on first boot. */ - machine_id_setup(NULL, /* force_transient= */ first_boot, arg_machine_id, NULL); + machine_id_setup(/* root= */ NULL, /* force_transient= */ first_boot, arg_machine_id, /* ret_machine_id */ NULL); (void) loopback_setup(); bump_unix_max_dgram_qlen(); bump_file_max_and_nr_open(); @@ -2306,10 +2311,6 @@ static int initialize_runtime( (void) bump_rlimit_nofile(saved_rlimit_nofile); (void) bump_rlimit_memlock(saved_rlimit_memlock); - /* Pull credentials from various sources into a common credential directory */ - if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM && !skip_setup) - (void) import_credentials(); - return 0; } diff --git a/src/shared/machine-id-setup.c b/src/shared/machine-id-setup.c index f27c3d768b..e059c71105 100644 --- a/src/shared/machine-id-setup.c +++ b/src/shared/machine-id-setup.c @@ -9,6 +9,7 @@ #include "alloc-util.h" #include "chase.h" +#include "creds-util.h" #include "fd-util.h" #include "id128-util.h" #include "io-util.h" @@ -27,6 +28,24 @@ #include "umask-util.h" #include "virt.h" +static int acquire_machine_id_from_credential(sd_id128_t *ret) { + _cleanup_free_ char *buf = NULL; + int r; + + r = read_credential_with_decryption("system.machine_id", (void**) &buf, /* ret_size= */ NULL); + if (r < 0) + return log_warning_errno(r, "Failed to read system.machine_id credential, ignoring: %m"); + if (r == 0) /* not found */ + return -ENXIO; + + r = sd_id128_from_string(buf, ret); + if (r < 0) + return log_warning_errno(r, "Failed to parse system.machine_id credential, ignoring: %m"); + + log_info("Initializing machine ID from credential."); + return 0; +} + static int generate_machine_id(const char *root, sd_id128_t *ret) { _cleanup_close_ int fd = -EBADF; int r; @@ -41,6 +60,11 @@ static int generate_machine_id(const char *root, sd_id128_t *ret) { } if (isempty(root) && running_in_chroot() <= 0) { + /* Let's use a system credential for the machine ID if we can */ + r = acquire_machine_id_from_credential(ret); + if (r >= 0) + return r; + /* If that didn't work, see if we are running in a container, * and a machine ID was passed in via $container_uuid the way * libvirt/LXC does it */ From 7ca59e67b1c4f45cfe4827049ee4d009f33b362b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 28 Jun 2023 22:58:07 +0200 Subject: [PATCH 07/22] import-creds: show list of imported credentials during initialization of PID 1 Let's make things easier to debug: provide an overview what has been passed, during boot. --- src/core/import-creds.c | 58 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/core/import-creds.c b/src/core/import-creds.c index 8c170d6fe5..fb2e9272bd 100644 --- a/src/core/import-creds.c +++ b/src/core/import-creds.c @@ -804,6 +804,62 @@ static int setenv_notify_socket(void) { return 1; } +static int report_credentials_per_func(const char *title, int (*get_directory_func)(const char **ret)) { + _cleanup_free_ DirectoryEntries *de = NULL; + _cleanup_close_ int dir_fd = -EBADF; + _cleanup_free_ char *ll = NULL; + const char *d = NULL; + int r, c = 0; + + assert(title); + assert(get_directory_func); + + r = get_directory_func(&d); + if (r < 0) { + if (r == -ENXIO) /* Env var not set */ + return 0; + + return log_warning_errno(r, "Failed to determine %s directory: %m", title); + } + + dir_fd = open(d, O_RDONLY|O_DIRECTORY|O_CLOEXEC); + if (dir_fd < 0) + return log_warning_errno(errno, "Failed to open credentials directory %s: %m", d); + + r = readdir_all(dir_fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT, &de); + if (r < 0) + return log_warning_errno(r, "Failed to enumerate credentials directory %s: %m", d); + + FOREACH_ARRAY(entry, de->entries, de->n_entries) { + const struct dirent *e = *entry; + + if (!credential_name_valid(e->d_name)) + continue; + + if (!strextend_with_separator(&ll, ", ", e->d_name)) + return log_oom(); + + c++; + } + + if (ll) + log_info("Received %s: %s", title, ll); + + return c; +} + +static void report_credentials(void) { + int p, q; + + p = report_credentials_per_func("regular credentials", get_credentials_dir); + q = report_credentials_per_func("untrusted credentials", get_encrypted_credentials_dir); + + log_full(p > 0 || q > 0 ? LOG_INFO : LOG_DEBUG, + "Acquired %i regular credentials, %i untrusted credentials.", + p > 0 ? p : 0, + q > 0 ? q : 0); +} + int import_credentials(void) { const char *received_creds_dir = NULL, *received_encrypted_creds_dir = NULL; bool envvar_set = false; @@ -864,6 +920,8 @@ int import_credentials(void) { r = q; } + report_credentials(); + /* Propagate vmm_notify_socket credential → $NOTIFY_SOCKET env var */ (void) setenv_notify_socket(); From 1155f44f48f8fd59c863d71b3938e34a0b2fec2a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 29 Jun 2023 12:32:44 +0200 Subject: [PATCH 08/22] execute: split out mounting of credentials fs Let's add two new helpers: mount_credentials_fs() and credentials_fs_mount_flags(). The former mounts a file system suitable for storing of unencrypted credentials at runtime (i.e. a ramfs or tmpfs). The latter determines the right mount flags to use for such a mount. Both functions mostly just take code from execute.c, but make two changes: 1. If the kernel supports it we'll use a tmpfs with the new "noswap" mount option instead of ramfs. Was added in kernel 6.4, hence is very recent, but tmpfs is so much less crappy than ramfs, hence worth it. 2. We'll set MS_NOSYMFOLLOW on the mounts if supported. These file systems should only contain regulra files, hence no need to allow symlinks. --- src/core/execute.c | 61 +++++++++++++---------------------------- src/shared/mount-util.c | 59 +++++++++++++++++++++++++++++++++++++++ src/shared/mount-util.h | 3 ++ 3 files changed, 81 insertions(+), 42 deletions(-) diff --git a/src/core/execute.c b/src/core/execute.c index 8cc1a0f76b..445983bfb6 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -3389,7 +3389,7 @@ static int setup_credentials_internal( if (r < 0) return r; - r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL); + r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ false), NULL); if (r < 0) return r; @@ -3400,57 +3400,34 @@ static int setup_credentials_internal( if (workspace_mounted < 0) { /* Nothing is mounted on the workspace yet, let's try to mount something now */ - for (int try = 0;; try++) { - if (try == 0) { - /* Try "ramfs" first, since it's not swap backed */ - r = mount_nofollow_verbose(LOG_DEBUG, "ramfs", workspace, "ramfs", MS_NODEV|MS_NOEXEC|MS_NOSUID, "mode=0700"); - if (r >= 0) { - workspace_mounted = true; - break; - } + r = mount_credentials_fs(workspace, CREDENTIALS_TOTAL_SIZE_MAX, /* ro= */ false); + if (r < 0) { + /* If that didn't work, try to make a bind mount from the final to the workspace, so that we can make it writable there. */ + r = mount_nofollow_verbose(LOG_DEBUG, final, workspace, NULL, MS_BIND|MS_REC, NULL); + if (r < 0) { + if (!ERRNO_IS_PRIVILEGE(r)) /* Propagate anything that isn't a permission problem */ + return r; - } else if (try == 1) { - _cleanup_free_ char *opts = NULL; + if (must_mount) /* If we it's not OK to use the plain directory + * fallback, propagate all errors too */ + return r; - if (asprintf(&opts, "mode=0700,nr_inodes=1024,size=%zu", (size_t) CREDENTIALS_TOTAL_SIZE_MAX) < 0) - return -ENOMEM; - - /* Fall back to "tmpfs" otherwise */ - r = mount_nofollow_verbose(LOG_DEBUG, "tmpfs", workspace, "tmpfs", MS_NODEV|MS_NOEXEC|MS_NOSUID, opts); - if (r >= 0) { - workspace_mounted = true; - break; - } + /* If we lack privileges to bind mount stuff, then let's gracefully + * proceed for compat with container envs, and just use the final dir + * as is. */ + workspace_mounted = false; } else { - /* If that didn't work, try to make a bind mount from the final to the workspace, so that we can make it writable there. */ - r = mount_nofollow_verbose(LOG_DEBUG, final, workspace, NULL, MS_BIND|MS_REC, NULL); - if (r < 0) { - if (!ERRNO_IS_PRIVILEGE(r)) /* Propagate anything that isn't a permission problem */ - return r; - - if (must_mount) /* If we it's not OK to use the plain directory - * fallback, propagate all errors too */ - return r; - - /* If we lack privileges to bind mount stuff, then let's gracefully - * proceed for compat with container envs, and just use the final dir - * as is. */ - - workspace_mounted = false; - break; - } - /* Make the new bind mount writable (i.e. drop MS_RDONLY) */ - r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL); + r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ false), NULL); if (r < 0) return r; workspace_mounted = true; - break; } - } + } else + workspace_mounted = true; } assert(!must_mount || workspace_mounted > 0); @@ -3482,7 +3459,7 @@ static int setup_credentials_internal( if (install) { /* Make workspace read-only now, so that any bind mount we make from it defaults to read-only too */ - r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL); + r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ true), NULL); if (r < 0) return r; diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c index b65416b03b..f0bf821430 100644 --- a/src/shared/mount-util.c +++ b/src/shared/mount-util.c @@ -1469,3 +1469,62 @@ int trigger_automount_at(int dir_fd, const char *path) { return 0; } + +unsigned long credentials_fs_mount_flags(bool ro) { + /* A tight set of mount flags for credentials mounts */ + return MS_NODEV|MS_NOEXEC|MS_NOSUID|ms_nosymfollow_supported()|(ro ? MS_RDONLY : 0); +} + +int mount_credentials_fs(const char *path, size_t size, bool ro) { + _cleanup_free_ char *opts = NULL; + int r, noswap_supported; + + /* Mounts a file system we can place credentials in, i.e. with tight access modes right from the + * beginning, and ideally swapping turned off. In order of preference: + * + * 1. tmpfs if it supports "noswap" + * 2. ramfs + * 3. tmpfs if it doesn't support "noswap" + */ + + noswap_supported = mount_option_supported("tmpfs", "noswap", NULL); /* Check explicitly to avoid kmsg noise */ + if (noswap_supported > 0) { + _cleanup_free_ char *noswap_opts = NULL; + + if (asprintf(&noswap_opts, "mode=0700,nr_inodes=1024,size=%zu,noswap", size) < 0) + return -ENOMEM; + + /* Best case: tmpfs with noswap (needs kernel >= 6.3) */ + + r = mount_nofollow_verbose( + LOG_DEBUG, + "tmpfs", + path, + "tmpfs", + credentials_fs_mount_flags(ro), + noswap_opts); + if (r >= 0) + return r; + } + + r = mount_nofollow_verbose( + LOG_DEBUG, + "ramfs", + path, + "ramfs", + credentials_fs_mount_flags(ro), + "mode=0700"); + if (r >= 0) + return r; + + if (asprintf(&opts, "mode=0700,nr_inodes=1024,size=%zu", size) < 0) + return -ENOMEM; + + return mount_nofollow_verbose( + LOG_DEBUG, + "tmpfs", + path, + "tmpfs", + credentials_fs_mount_flags(ro), + opts); +} diff --git a/src/shared/mount-util.h b/src/shared/mount-util.h index ceb3964a74..7ee6750044 100644 --- a/src/shared/mount-util.h +++ b/src/shared/mount-util.h @@ -141,3 +141,6 @@ int make_mount_point_inode_from_stat(const struct stat *st, const char *dest, mo int make_mount_point_inode_from_path(const char *source, const char *dest, mode_t mode); int trigger_automount_at(int dir_fd, const char *path); + +unsigned long credentials_fs_mount_flags(bool ro); +int mount_credentials_fs(const char *path, size_t size, bool ro); From bfa6d9cc64051ec7a47a9f96a75def1223d1037c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 29 Jun 2023 12:53:23 +0200 Subject: [PATCH 09/22] import-creds: unify acquire_credential_directory() + acquire_encrypted_credential_directory() Let's unify these very similar functions, and port them to the new mount_credentials_fs() call. While we are at it, if we detect that the credentials dir already is a mount point, remount it writable so that we can actually write to it. --- src/core/import-creds.c | 70 ++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 43 deletions(-) diff --git a/src/core/import-creds.c b/src/core/import-creds.c index fb2e9272bd..6bf6ded44c 100644 --- a/src/core/import-creds.c +++ b/src/core/import-creds.c @@ -70,21 +70,36 @@ static void import_credentials_context_free(ImportCredentialContext *c) { c->target_dir_fd = safe_close(c->target_dir_fd); } -static int acquire_encrypted_credential_directory(ImportCredentialContext *c) { +static int acquire_credential_directory(ImportCredentialContext *c, const char *path, bool with_mount) { int r; assert(c); + assert(path); if (c->target_dir_fd >= 0) return c->target_dir_fd; - r = mkdir_safe_label(ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY, 0700, 0, 0, MKDIR_WARN_MODE); - if (r < 0) - return log_error_errno(r, "Failed to create " ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY ": %m"); + r = path_is_mount_point(path, NULL, 0); + if (r < 0) { + if (r != -ENOENT) + return log_error_errno(r, "Failed to determine if %s is a mount point: %m", path); - c->target_dir_fd = open(ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY, O_RDONLY|O_DIRECTORY|O_CLOEXEC); + r = mkdir_safe_label(path, 0700, 0, 0, MKDIR_WARN_MODE); + if (r < 0) + return log_error_errno(r, "Failed to create %s mount point: %m", path); + + r = 0; /* Now it exists and is not a mount point */ + } + if (r > 0) + /* If already a mount point, then remount writable */ + (void) mount_nofollow_verbose(LOG_WARNING, NULL, path, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ false), NULL); + else if (with_mount) + /* If not a mount point yet, and the credentials are not encrypted, then let's try to mount a no-swap fs there */ + (void) mount_credentials_fs(path, CREDENTIALS_TOTAL_SIZE_MAX, /* ro= */ false); + + c->target_dir_fd = open(path, O_RDONLY|O_DIRECTORY|O_CLOEXEC); if (c->target_dir_fd < 0) - return log_error_errno(errno, "Failed to open " ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY ": %m"); + return log_error_errno(errno, "Failed to open %s: %m", path); return c->target_dir_fd; } @@ -137,7 +152,7 @@ static int finalize_credentials_dir(const char *dir, const char *envvar) { if (r < 0) log_warning_errno(r, "Failed to make '%s' a mount point, ignoring: %m", dir); else - (void) mount_nofollow_verbose(LOG_WARNING, NULL, dir, NULL, MS_BIND|MS_NODEV|MS_NOEXEC|MS_NOSUID|MS_RDONLY|MS_REMOUNT, NULL); + (void) mount_nofollow_verbose(LOG_WARNING, NULL, dir, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ true), NULL); if (setenv(envvar, dir, /* overwrite= */ true) < 0) return log_error_errno(errno, "Failed to set $%s environment variable: %m", envvar); @@ -227,7 +242,7 @@ static int import_credentials_boot(void) { if (!credential_size_ok(&context, n, st.st_size)) continue; - r = acquire_encrypted_credential_directory(&context); + r = acquire_credential_directory(&context, ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY, /* with_mount= */ false); if (r < 0) return r; @@ -261,37 +276,6 @@ static int import_credentials_boot(void) { return 0; } -static int acquire_credential_directory(ImportCredentialContext *c) { - int r; - - assert(c); - - if (c->target_dir_fd >= 0) - return c->target_dir_fd; - - r = path_is_mount_point(SYSTEM_CREDENTIALS_DIRECTORY, NULL, 0); - if (r < 0) { - if (r != -ENOENT) - return log_error_errno(r, "Failed to determine if " SYSTEM_CREDENTIALS_DIRECTORY " is a mount point: %m"); - - r = mkdir_safe_label(SYSTEM_CREDENTIALS_DIRECTORY, 0700, 0, 0, MKDIR_WARN_MODE); - if (r < 0) - return log_error_errno(r, "Failed to create " SYSTEM_CREDENTIALS_DIRECTORY " mount point: %m"); - - r = 0; /* Now it exists and is not a mount point */ - } - if (r == 0) - /* If not a mountpoint yet, try to mount a ramfs there (so that this stuff isn't swapped - * out), but if that doesn't work, let's just use the regular tmpfs it already is. */ - (void) mount_nofollow_verbose(LOG_WARNING, "ramfs", SYSTEM_CREDENTIALS_DIRECTORY, "ramfs", MS_NODEV|MS_NOEXEC|MS_NOSUID, "mode=0700"); - - c->target_dir_fd = open(SYSTEM_CREDENTIALS_DIRECTORY, O_RDONLY|O_DIRECTORY|O_CLOEXEC); - if (c->target_dir_fd < 0) - return log_error_errno(errno, "Failed to open " SYSTEM_CREDENTIALS_DIRECTORY ": %m"); - - return c->target_dir_fd; -} - static int proc_cmdline_callback(const char *key, const char *value, void *data) { ImportCredentialContext *c = ASSERT_PTR(data); _cleanup_free_ char *n = NULL; @@ -326,7 +310,7 @@ static int proc_cmdline_callback(const char *key, const char *value, void *data) if (!credential_size_ok(c, n, l)) return 0; - r = acquire_credential_directory(c); + r = acquire_credential_directory(c, SYSTEM_CREDENTIALS_DIRECTORY, /* with_mount= */ true); if (r < 0) return r; @@ -433,7 +417,7 @@ static int import_credentials_qemu(ImportCredentialContext *c) { continue; } - r = acquire_credential_directory(c); + r = acquire_credential_directory(c, SYSTEM_CREDENTIALS_DIRECTORY, /* with_mount= */ true); if (r < 0) return r; @@ -535,7 +519,7 @@ static int parse_smbios_strings(ImportCredentialContext *c, const char *data, si if (!credential_size_ok(c, cn, cdata_len)) continue; - r = acquire_credential_directory(c); + r = acquire_credential_directory(c, SYSTEM_CREDENTIALS_DIRECTORY, /* with_mount= */ true); if (r < 0) return r; @@ -671,7 +655,7 @@ static int import_credentials_initrd(ImportCredentialContext *c) { if (!credential_size_ok(c, d->d_name, st.st_size)) continue; - r = acquire_credential_directory(c); + r = acquire_credential_directory(c, SYSTEM_CREDENTIALS_DIRECTORY, /* with_mount= */ true); if (r < 0) return r; From 4a262d567721478e30784d247281a18de38bf2cb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 29 Jun 2023 19:03:08 +0200 Subject: [PATCH 10/22] test: add test for initrd credentials This extends the test framework a bit, and allows adding additional initrds to the qemu invocation, which we use here to place credentials in the new /run/systemd/@initrd/ credentials dir which are then passed to the host. --- test/TEST-54-CREDS/test.sh | 23 ++++++++++++++++++++++ test/test-functions | 39 +++++++++++++++++++++++++++++++++++++- test/units/testsuite-54.sh | 5 +++++ 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/test/TEST-54-CREDS/test.sh b/test/TEST-54-CREDS/test.sh index 443be8761e..68f97ce953 100755 --- a/test/TEST-54-CREDS/test.sh +++ b/test/TEST-54-CREDS/test.sh @@ -38,4 +38,27 @@ test_append_files() { generate_module_dependencies } +run_qemu_hook() { + local td="$WORKDIR"/initrd.extra."$RANDOM" + mkdir -m 755 "$td" + add_at_exit_handler "rm -rf $td" + mkdir -m 755 "$td/etc" "$td"/etc/systemd "$td"/etc/systemd/system "$td"/etc/systemd/system/initrd.target.wants + + cat > "$td"/etc/systemd/system/initrdcred.service < /run/credentials/@initrd/myinitrdcred" +EOF + ln -s ../initrdcred.service "$td"/etc/systemd/system/initrd.target.wants/initrdcred.service + + ( cd "$td" && find . | cpio -o -H newc -R root:root > "$td".cpio ) + add_at_exit_handler "rm $td.cpio" + + INITRD_EXTRA="$td.cpio" +} + do_test "$@" diff --git a/test/test-functions b/test/test-functions index 9d0ea73a42..41e1a0d57d 100644 --- a/test/test-functions +++ b/test/test-functions @@ -428,9 +428,25 @@ qemu_min_version() { printf "%s\n%s\n" "$1" "$qemu_ver" | sort -V -C } +# Pads a file to multiple of 4 bytes +pad4_file() { + local size + size=$(stat -c "%s" "$1") + local padded + padded=$((((size + 3) / 4) * 4)) + truncate -s "$padded" "$1" +} + # Return 0 if qemu did run (then you must check the result state/logs for actual # success), or 1 if qemu is not available. run_qemu() { + if declare -F run_qemu_hook >/dev/null; then + if ! run_qemu_hook "${workspace}"; then + derror "check_qemu_hook() returned with EC > 0" + ret=4 + fi + fi + # If the test provided its own initrd, use it (e.g. TEST-24) if [[ -z "$INITRD" && -f "${TESTDIR:?}/initrd.img" ]]; then INITRD="$TESTDIR/initrd.img" @@ -577,7 +593,28 @@ run_qemu() { fi if [[ -n "$INITRD" ]]; then - qemu_options+=(-initrd "$INITRD") + if [[ -n "$INITRD_EXTRA" ]]; then + # An addition initrd has been specified, let's combine it with the main one. + local t="$WORKDIR"/initrd.combined."$RANDOM" + + # First, show contents of additional initrd + echo "Additional initrd contents:" + cpio -tv < "$INITRD_EXTRA" + + # Copy the main initrd + zstd -d -c -f "$INITRD" > "$t" + add_at_exit_handler "rm $t" + # Kernel requires this to be padded to multiple of 4 bytes with zeroes + pad4_file "$t" + + # Copy the additional initrd + cat "$INITRD_EXTRA" >> "$t" + pad4_file "$t" + + qemu_options+=(-initrd "$t") + else + qemu_options+=(-initrd "$INITRD") + fi fi # Let's use KVM if possible diff --git a/test/units/testsuite-54.sh b/test/units/testsuite-54.sh index 89466a5e33..ca7e11fc46 100755 --- a/test/units/testsuite-54.sh +++ b/test/units/testsuite-54.sh @@ -301,6 +301,11 @@ systemd-run -p DynamicUser=yes -p 'LoadCredential=os:/etc/os-release' \ --pipe \ true | cmp /etc/os-release +if ! systemd-detect-virt -q -c ; then + # Validate that the credential we inserted via the initrd logic arrived + test "$(systemd-creds cat --system myinitrdcred)" = "guatemala" +fi + systemd-analyze log-level info echo OK >/testok From 6ac62485cff1a15de684394c9f628afad75c4819 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 23 Jun 2023 21:51:12 +0200 Subject: [PATCH 11/22] fstab-generator: optional read addtional fstab lines from credentials Fixes: #27260 --- man/systemd-fstab-generator.xml | 15 +++++++++++ man/systemd.system-credentials.xml | 9 +++++++ src/fstab-generator/fstab-generator.c | 39 +++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/man/systemd-fstab-generator.xml b/man/systemd-fstab-generator.xml index e21115f173..46f2390234 100644 --- a/man/systemd-fstab-generator.xml +++ b/man/systemd-fstab-generator.xml @@ -269,6 +269,21 @@ systemd.swap=/dev/sda2:x-systemd.makefs + + System Credentials + + + + fstab.extra + + This credential may contain addition mounts to establish, in the same format as + fstab5, with + one mount per line. It is read in addition to /etc/fstab. + + + + See Also diff --git a/man/systemd.system-credentials.xml b/man/systemd.system-credentials.xml index ceb84d29b9..6fd69ead30 100644 --- a/man/systemd.system-credentials.xml +++ b/man/systemd.system-credentials.xml @@ -186,6 +186,15 @@ + + fstab.extra + + + Additional mounts to establish at boot. For details, see + systemd-fstab-generator8. + + + vconsole.keymap vconsole.keymap_toggle diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index 23358ae8a2..14a46c4c4e 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -8,6 +8,7 @@ #include "bus-error.h" #include "bus-locator.h" #include "chase.h" +#include "creds-util.h" #include "efi-loader.h" #include "env-util.h" #include "fd-util.h" @@ -1281,6 +1282,40 @@ static int add_mounts_from_cmdline(void) { return ret; } +static int add_mounts_from_creds(void) { + _cleanup_free_ void *b = NULL; + struct mntent *me; + int r, ret = 0; + size_t bs; + + r = read_credential_with_decryption( + in_initrd() ? "fstab.extra.initrd" : "fstab.extra", + &b, &bs); + if (r <= 0) + return r; + + _cleanup_fclose_ FILE *f = NULL; + f = fmemopen_unlocked(b, bs, "r"); + if (!f) + return log_oom(); + + while ((me = getmntent(f))) { + r = parse_fstab_one( + "/run/credentials", + me->mnt_fsname, + me->mnt_dir, + me->mnt_type, + me->mnt_opts, + me->mnt_passno, + /* initrd = */ false, + /* use_swap_enabled = */ true); + if (r < 0 && ret >= 0) + ret = r; + } + + return ret; +} + static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { int r; @@ -1513,6 +1548,10 @@ static int run_generator(void) { if (r < 0 && ret >= 0) ret = r; + r = add_mounts_from_creds(); + if (r < 0 && ret >= 0) + ret = r; + return ret; } From 3a065dfc29aa061906ab9dd886093581410b666a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 23 Jun 2023 22:49:55 +0200 Subject: [PATCH 12/22] fstab-generator: add more parameter name comments --- src/fstab-generator/fstab-generator.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index 14a46c4c4e..cdcbb0deec 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -1501,7 +1501,7 @@ static int run_generator(void) { (void) determine_usr(); if (arg_sysroot_check) { - r = parse_fstab(true); + r = parse_fstab(/* initrd= */ true); if (r == 0) log_debug("Nothing interesting found, not doing daemon-reload."); if (r > 0) @@ -1531,13 +1531,13 @@ static int run_generator(void) { /* Honour /etc/fstab only when that's enabled */ if (arg_fstab_enabled) { /* Parse the local /etc/fstab, possibly from the initrd */ - r = parse_fstab(false); + r = parse_fstab(/* initrd= */ false); if (r < 0 && ret >= 0) ret = r; /* If running in the initrd also parse the /etc/fstab from the host */ if (in_initrd()) - r = parse_fstab(true); + r = parse_fstab(/* initrd= */ true); else r = generator_enable_remount_fs_service(arg_dest); if (r < 0 && ret >= 0) From 51235f2fe63fa5d45ac75b193b942cf3419ca6dd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 29 Jun 2023 21:52:57 +0200 Subject: [PATCH 13/22] test: add simple fstab credential test --- test/TEST-54-CREDS/test.sh | 1 + test/units/testsuite-54.sh | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/test/TEST-54-CREDS/test.sh b/test/TEST-54-CREDS/test.sh index 68f97ce953..1d14e0a8c9 100755 --- a/test/TEST-54-CREDS/test.sh +++ b/test/TEST-54-CREDS/test.sh @@ -15,6 +15,7 @@ QEMU_CREDS=( "-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==" + "-smbios type=11,value=io.systemd.credential.binary:fstab.extra=aW5qZWN0ZWQgL2luamVjdGVkIHRtcGZzIFgtbW91bnQubWtkaXIgMCAwCg==" ) QEMU_OPTIONS="${QEMU_OPTIONS:-} ${QEMU_CREDS[*]}" diff --git a/test/units/testsuite-54.sh b/test/units/testsuite-54.sh index ca7e11fc46..bcc7313d6f 100755 --- a/test/units/testsuite-54.sh +++ b/test/units/testsuite-54.sh @@ -304,6 +304,10 @@ systemd-run -p DynamicUser=yes -p 'LoadCredential=os:/etc/os-release' \ if ! systemd-detect-virt -q -c ; then # Validate that the credential we inserted via the initrd logic arrived test "$(systemd-creds cat --system myinitrdcred)" = "guatemala" + + # Check that the fstab credential logic worked + test -d /injected + grep -q /injected /proc/self/mountinfo fi systemd-analyze log-level info From cdd133b3dd31661419470242a7275a60b2ab71d7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 23 Jun 2023 22:50:34 +0200 Subject: [PATCH 14/22] getty-generator: allow configuring additional gettys via credentials --- man/systemd-getty-generator.xml | 18 +++++++++ man/systemd.system-credentials.xml | 8 ++++ src/getty-generator/getty-generator.c | 57 ++++++++++++++++++++++++++- 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/man/systemd-getty-generator.xml b/man/systemd-getty-generator.xml index a31ed660bb..d2e05dc82f 100644 --- a/man/systemd-getty-generator.xml +++ b/man/systemd-getty-generator.xml @@ -85,11 +85,29 @@ + + System Credentials + + + + getty.ttys.serial + getty.ttys.container + + These system credentials may be used to spawn additional login prompts on selected + TTYs. The two credentials should contain a newline-separated list of TTY names to spawn instances of + serial-getty@.service (in case of getty.ttys.serial) and + container-getty@.service (in case of getty.ttys.container) + on. + + + + See Also systemd1, kernel-command-line7, + systemd.system-credentials7, agetty8 diff --git a/man/systemd.system-credentials.xml b/man/systemd.system-credentials.xml index 6fd69ead30..0e64b45df0 100644 --- a/man/systemd.system-credentials.xml +++ b/man/systemd.system-credentials.xml @@ -207,6 +207,14 @@ + + getty.ttys.serial + getty.ttys.container + + Used for spawning additional login prompts, see + systemd-getty-generator8 for details. + + vmm.notify_socket diff --git a/src/getty-generator/getty-generator.c b/src/getty-generator/getty-generator.c index d255e90db5..e327b375cd 100644 --- a/src/getty-generator/getty-generator.c +++ b/src/getty-generator/getty-generator.c @@ -5,6 +5,7 @@ #include #include "alloc-util.h" +#include "creds-util.h" #include "errno-util.h" #include "fd-util.h" #include "fileio.h" @@ -13,8 +14,8 @@ #include "mkdir-label.h" #include "parse-util.h" #include "path-util.h" -#include "process-util.h" #include "proc-cmdline.h" +#include "process-util.h" #include "strv.h" #include "terminal-util.h" #include "unit-name.h" @@ -141,6 +142,56 @@ static int run_container(void) { } } +static int add_credential_gettys(void) { + static const struct { + const char *credential_name; + int (*func)(const char *tty); + } table[] = { + { "getty.ttys.serial", add_serial_getty }, + { "getty.ttys.container", add_container_getty }, + }; + int r; + + FOREACH_ARRAY(t, table, ELEMENTSOF(table)) { + _cleanup_free_ char *b = NULL; + size_t sz = 0; + + r = read_credential_with_decryption(t->credential_name, (void*) &b, &sz); + if (r < 0) + return r; + if (r == 0) + continue; + + _cleanup_fclose_ FILE *f = NULL; + f = fmemopen_unlocked(b, sz, "r"); + if (!f) + return log_oom(); + + for (;;) { + _cleanup_free_ char *tty = NULL; + char *s; + + r = read_line(f, PATH_MAX, &tty); + if (r == 0) + break; + if (r < 0) { + log_error_errno(r, "Failed to parse credential %s: %m", t->credential_name); + break; + } + + s = strstrip(tty); + if (startswith(s, "#")) + continue; + + r = t->func(s); + if (r < 0) + return r; + } + } + + return 0; +} + static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { int r; @@ -183,6 +234,10 @@ static int run(const char *dest, const char *dest_early, const char *dest_late) return 0; } + r = add_credential_gettys(); + if (r < 0) + return r; + if (detect_container() > 0) /* Add console shell and look at $container_ttys, but don't do add any * further magic if we are in a container. */ From fd2de366e12373b1baf3217e42e75274b144ac5c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 23 Jun 2023 22:50:55 +0200 Subject: [PATCH 15/22] getty-generator: minor modernizations --- src/getty-generator/getty-generator.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/getty-generator/getty-generator.c b/src/getty-generator/getty-generator.c index e327b375cd..24c35ae987 100644 --- a/src/getty-generator/getty-generator.c +++ b/src/getty-generator/getty-generator.c @@ -25,8 +25,7 @@ static const char *arg_dest = NULL; static bool arg_enabled = true; static int add_symlink(const char *fservice, const char *tservice) { - char *from, *to; - int r; + const char *from, *to; assert(fservice); assert(tservice); @@ -36,8 +35,7 @@ static int add_symlink(const char *fservice, const char *tservice) { (void) mkdir_parents_label(to, 0755); - r = symlink(from, to); - if (r < 0) { + if (symlink(from, to) < 0) { /* In case console=hvc0 is passed this will very likely result in EEXIST */ if (errno == EEXIST) return 0; @@ -281,7 +279,7 @@ static int run(const char *dest, const char *dest_early, const char *dest_late) p = path_join("/sys/class/tty", j); if (!p) - return -ENOMEM; + return log_oom(); if (access(p, F_OK) < 0) continue; From 53888c3393c8189c383a776284d80d002c30a507 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 29 Jun 2023 22:53:26 +0200 Subject: [PATCH 16/22] test: verify that the getty generator with creds works --- test/TEST-54-CREDS/test.sh | 1 + test/units/testsuite-54.sh | 3 +++ 2 files changed, 4 insertions(+) diff --git a/test/TEST-54-CREDS/test.sh b/test/TEST-54-CREDS/test.sh index 1d14e0a8c9..5588242230 100755 --- a/test/TEST-54-CREDS/test.sh +++ b/test/TEST-54-CREDS/test.sh @@ -16,6 +16,7 @@ QEMU_CREDS=( "-smbios type=11,value=io.systemd.credential.binary:sysusers.extra=dSBjcmVkdGVzdHVzZXIK" "-smbios type=11,value=io.systemd.credential.binary:tmpfiles.extra=ZiAvdG1wL3NvdXJjZWRmcm9tY3JlZGVudGlhbCAtIC0gLSAtIHRtcGZpbGVzc2VjcmV0Cg==" "-smbios type=11,value=io.systemd.credential.binary:fstab.extra=aW5qZWN0ZWQgL2luamVjdGVkIHRtcGZzIFgtbW91bnQubWtkaXIgMCAwCg==" + "-smbios type=11,value=io.systemd.credential:getty.ttys.container=idontexist" ) QEMU_OPTIONS="${QEMU_OPTIONS:-} ${QEMU_CREDS[*]}" diff --git a/test/units/testsuite-54.sh b/test/units/testsuite-54.sh index bcc7313d6f..8ba327a3b1 100755 --- a/test/units/testsuite-54.sh +++ b/test/units/testsuite-54.sh @@ -308,6 +308,9 @@ if ! systemd-detect-virt -q -c ; then # Check that the fstab credential logic worked test -d /injected grep -q /injected /proc/self/mountinfo + + # Make sure the getty generator processed the credentials properly + systemctl -P Wants show getty.target | grep -q container-getty@idontexist.service fi systemd-analyze log-level info From 3ed075cf6158b2fadd904aba0abfe9bfe020dbf5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 30 Jun 2023 10:23:10 +0200 Subject: [PATCH 17/22] generator: run various generators only run on the host, not in initrd These 5 generators only make sense on the host,not in the initrd, hence if they end up in the initrd anyway, make them exit quickly. --- src/boot/bless-boot-generator.c | 4 ++-- src/getty-generator/getty-generator.c | 6 ++++++ src/rc-local-generator/rc-local-generator.c | 6 ++++++ src/system-update-generator/system-update-generator.c | 6 ++++++ src/sysv-generator/sysv-generator.c | 6 ++++++ 5 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/boot/bless-boot-generator.c b/src/boot/bless-boot-generator.c index 5120b9622e..38b2c3ad7c 100644 --- a/src/boot/bless-boot-generator.c +++ b/src/boot/bless-boot-generator.c @@ -18,9 +18,9 @@ static int run(const char *dest, const char *dest_early, const char *dest_late) { - if (in_initrd() > 0) { + if (in_initrd()) { log_debug("Skipping generator, running in the initrd."); - return 0; + return EXIT_SUCCESS; } if (detect_container() > 0) { diff --git a/src/getty-generator/getty-generator.c b/src/getty-generator/getty-generator.c index 24c35ae987..4379b4b648 100644 --- a/src/getty-generator/getty-generator.c +++ b/src/getty-generator/getty-generator.c @@ -10,6 +10,7 @@ #include "fd-util.h" #include "fileio.h" #include "generator.h" +#include "initrd-util.h" #include "log.h" #include "mkdir-label.h" #include "parse-util.h" @@ -212,6 +213,11 @@ static int run(const char *dest, const char *dest_early, const char *dest_late) assert_se(arg_dest = dest); + if (in_initrd()) { + log_debug("Skipping generator, running in the initrd."); + return EXIT_SUCCESS; + } + r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0); if (r < 0) log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); diff --git a/src/rc-local-generator/rc-local-generator.c b/src/rc-local-generator/rc-local-generator.c index d8c06b1d2d..89cc5fadb6 100644 --- a/src/rc-local-generator/rc-local-generator.c +++ b/src/rc-local-generator/rc-local-generator.c @@ -5,6 +5,7 @@ #include #include "generator.h" +#include "initrd-util.h" #include "log.h" #include "mkdir-label.h" #include "string-util.h" @@ -58,6 +59,11 @@ static int run(const char *dest, const char *dest_early, const char *dest_late) assert_se(arg_dest = dest); + if (in_initrd()) { + log_debug("Skipping generator, running in the initrd."); + return EXIT_SUCCESS; + } + if (check_executable(RC_LOCAL_PATH) >= 0) { log_debug("Automatically adding rc-local.service."); diff --git a/src/system-update-generator/system-update-generator.c b/src/system-update-generator/system-update-generator.c index 83b06902ab..a1782d5c05 100644 --- a/src/system-update-generator/system-update-generator.c +++ b/src/system-update-generator/system-update-generator.c @@ -5,6 +5,7 @@ #include "fs-util.h" #include "generator.h" +#include "initrd-util.h" #include "log.h" #include "path-util.h" #include "proc-cmdline.h" @@ -61,6 +62,11 @@ static int run(const char *dest, const char *dest_early, const char *dest_late) assert_se(arg_dest = dest_early); + if (in_initrd()) { + log_debug("Skipping generator, running in the initrd."); + return EXIT_SUCCESS; + } + r = generate_symlink(); if (r <= 0) return r; diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c index 632f4a1aac..30f82d5736 100644 --- a/src/sysv-generator/sysv-generator.c +++ b/src/sysv-generator/sysv-generator.c @@ -14,6 +14,7 @@ #include "generator.h" #include "hashmap.h" #include "hexdecoct.h" +#include "initrd-util.h" #include "install.h" #include "log.h" #include "main-func.h" @@ -899,6 +900,11 @@ static int run(const char *dest, const char *dest_early, const char *dest_late) SysvStub *service; int r; + if (in_initrd()) { + log_debug("Skipping generator, running in the initrd."); + return EXIT_SUCCESS; + } + assert_se(arg_dest = dest_late); r = lookup_paths_init_or_warn(&lp, RUNTIME_SCOPE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, NULL); From df5f51c3fe3be1734d48d6a866f7a63ee59bfc56 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 30 Jun 2023 11:44:46 +0200 Subject: [PATCH 18/22] doc: document inird credentials + and how to consume credentials in generators (as well as various other fixes) --- docs/CREDENTIALS.md | 98 +++++++++++++++++++++++++++++++++------------ 1 file changed, 73 insertions(+), 25 deletions(-) diff --git a/docs/CREDENTIALS.md b/docs/CREDENTIALS.md index 50100cc5b6..da5152c164 100644 --- a/docs/CREDENTIALS.md +++ b/docs/CREDENTIALS.md @@ -51,9 +51,9 @@ purpose. Specifically, the following features are provided: allow it, via `ramfs`.) 7. Credentials may be acquired from a hosting VM hypervisor (SMBIOS OEM strings - or qemu `fw_cfg`), a hosting container manager, the kernel command line, or - from the UEFI environment and the EFI System Partition (via - `systemd-stub`). Such system credentials may then be propagated into + or qemu `fw_cfg`), a hosting container manager, the kernel command line, + from the initrd, or from the UEFI environment via the EFI System Partition + (via `systemd-stub`). Such system credentials may then be propagated into individual services as needed. 8. Credentials are an effective way to pass parameters into services that run @@ -72,19 +72,19 @@ Within unit files, there are four settings to configure service credentials. 1. `LoadCredential=` may be used to load a credential from disk, from an `AF_UNIX` socket, or propagate them from a system credential. -2. `ImportCredential=` may be used to load one or more (encrypted) credentials - from disk or from the credential stores. +2. `ImportCredential=` may be used to load one or more (optionally encrypted) + credentials from disk or from the credential stores. -2. `SetCredential=` may be used to set a credential to a literal string encoded +3. `SetCredential=` may be used to set a credential to a literal string encoded in the unit file. Because unit files are world-readable (both on disk and via D-Bus), this should only be used for credentials that aren't sensitive, e.g. public keys or certificates, but not private keys. -3. `LoadCredentialEncrypted=` is similar to `LoadCredential=` but will load an +4. `LoadCredentialEncrypted=` is similar to `LoadCredential=` but will load an encrypted credential, and decrypt it before passing it to the service. For details on credential encryption, see below. -4. `SetCredentialEncrypted=` is similar to `SetCredential=` but expects an +5. `SetCredentialEncrypted=` is similar to `SetCredential=` but expects an encrypted credential to be specified literally. Unlike `SetCredential=` it is thus safe to be used even for sensitive information, because even though unit files are world readable, the ciphertext included in them cannot be @@ -153,6 +153,33 @@ credentials directory. For daemons that allow passing credentials via a path supplied as environment variable, use the `%d` specifier in the `Environment=` setting to build valid paths to specific credentials. +Encrypted credentials are automatically decrypted/authenticated during service +activation, so that service code only receives plaintext credentials. + +## Programming Interface from Generator Code + +[Generators](https://www.freedesktop.org/software/systemd/man/systemd.generator.html) +may generate native unit files from external configuration or system +parameters, such as system credentials. Note that they run outside of service +context, and hence will not receive encrypted credentials in plaintext +form. Specifically, credentials passed into the system in encrypted form will +be placed as they are in a directory referenced by the +`$ENCRYPTED_CREDENTIALS_DIRECTORY` environment variable, and those passed in +plaintext form will be placed in `$CREDENTIALS_DIRECTORY`. Use a command such +as `systemd-creds --system cat …` to access both forms of credentials, and +decrypt them if needed (see +[systemd-creds(1)](https://www.freedesktop.org/software/systemd/man/systemd-creds.html) +for details. + +Note that generators typically run very early during boot (similar to initrd +code), earlier than the `/var/` file system is necessarily mounted (which is +where the system's credential encryption secret is located). Thus it's a good +idea to encrypt credentials with `systemd-creds encrypt --with-key=auto-initrd` +if they shall be consumed by a generator, to ensure they are locked to the TPM2 +only, not the credentials secret stored below `/var/`. + +For further details about encrypted credentials, see below. + ## Tools The @@ -194,6 +221,12 @@ cannot be decrypted without access to the TPM2 chip and the aforementioned key file `/var/lib/systemd/credential.secret`. Moreover, credentials cannot be prepared on a machine other than the local one. +Decryption generally takes place at the moment of service activation. This +means credentials passed to the system can be either encrypted or plaintext and +remain that way all the way while they are propagated to their consumers, until +the moment of service activation when they are decrypted and authenticated, so +that the service only sees plaintext credentials. + The `systemd-creds` tool provides the commands `encrypt` and `decrypt` to encrypt and decrypt/authenticate credentials. Example: @@ -247,7 +280,7 @@ via `systemd` credentials. In particular, it might make sense to boot a system with a set of credentials that are then propagated to individual services where they are ultimately consumed. -`systemd` supports four ways to pass credentials to systems: +`systemd` supports five ways to pass credentials to systems: 1. A container manager may set the `$CREDENTIALS_DIRECTORY` environment variable for systemd running as PID 1 in the container, the same way as @@ -255,8 +288,7 @@ services where they are ultimately consumed. invokes. [`systemd-nspawn(1)`](https://www.freedesktop.org/software/systemd/man/systemd-nspawn.html#Credentials)'s `--set-credential=` and `--load-credential=` switches implement this, in order to pass arbitrary credentials from host to container payload. Also see - the [Container Interface](CONTAINER_INTERFACE.md) - documentation. + the [Container Interface](CONTAINER_INTERFACE.md) documentation. 2. Quite similar, VMs can be passed credentials via SMBIOS OEM strings (example qemu command line switch `-smbios @@ -269,16 +301,17 @@ services where they are ultimately consumed. three of these specific switches would set credential `foo` to `bar`.) Passing credentials via the SMBIOS mechanism is typically preferable over `fw_cfg` since it is faster and less specific to the chosen VMM - implementation. Moreover, `fw_cfg` has a 55 character limitation - on names passed that way. So some settings may not fit. + implementation. Moreover, `fw_cfg` has a 55 character limitation on names + passed that way. So some settings may not fit. -3. Credentials can also be passed into a system via the kernel command line, - via the `systemd.set-credential=` kernel command line option. Note though - that any data specified here is visible to any userspace application via - `/proc/cmdline`. This is hence typically not useful to pass sensitive - information. +3. Credentials may be passed from the initrd to the host during the initrd → + host transition. Provisioning systems that run in the initrd may use this to + install credentials on the system. All files placed in + `/run/credentials/@initrd/` are imported into the set of file system + credentials during the transition. The files (and their directory) are + removed once this is completed. -4. Credentials may also be passed from the UEFI environment to userspace, if +5. Credentials may also be passed from the UEFI environment to userspace, if the [`systemd-stub`](https://www.freedesktop.org/software/systemd/man/systemd-stub.html) UEFI kernel stub is used. This allows placing encrypted credentials in the @@ -288,6 +321,12 @@ services where they are ultimately consumed. initrds, as userspace can place credentials next to these EFI kernels, and be sure they can be accessed securely from initrd context. +4. Credentials can also be passed into a system via the kernel command line, + via the `systemd.set-credential=` kernel command line option. Note though + that any data specified here is visible to all userspace applications (even + unprivileged ones) via `/proc/cmdline`. Typically, this is hence not useful + to pass sensitive information, and should be avoided. + Credentials passed to the system may be enumerated/displayed via `systemd-creds --system`. They may also be propagated down to services, via the `LoadCredential=` setting. Example: @@ -359,6 +398,9 @@ Various services shipped with `systemd` consume credentials for tweaking behavio will look for the credentials `tmpfiles.extra` with arbitrary tmpfiles.d lines. Can be encoded in base64 to allow easily passing it on the command line. +* Further well-known credentials are documented in + [`systemd.system-credentials(7)`](https://www.freedesktop.org/software/systemd/man/systemd.system-credentials.html). + In future more services are likely to gain support for consuming credentials. Example: @@ -427,6 +469,11 @@ READY=1 From *service* perspective the runtime path to find loaded credentials in is provided in the `$CREDENTIALS_DIRECTORY` environment variable. +From *generator* perspective the runtime path to find credentials passed into +the system in plaintext form in is provided in `$CREDENTIALS_DIRECTORY`, and +those passed into the system in encrypted form is provided in +`$ENCRYPTED_CREDENTIALS_DIRECTORY`. + At runtime, credentials passed to the *system* are placed in `/run/credentials/@system/` (for regular credentials, such as those passed from a container manager or via qemu) and `/run/credentials/@encrypted/` (for @@ -434,13 +481,14 @@ credentials that must be decrypted/validated before use, such as those from `systemd-stub`). The `ImportCredential=` setting (and the `LoadCredential=` and -`LoadCredentialEncrypted=` settings when configured with a relative source path) -will search for the source file to read the credential from automatically. Primarily, -these credentials are searched among the credentials passed into the system. If -not found there, they are searched in `/etc/credstore/`, `/run/credstore/`, +`LoadCredentialEncrypted=` settings when configured with a relative source +path) will search for the source file to read the credential from +automatically. Primarily, these credentials are searched among the credentials +passed into the system. If not found there, they are searched in +`/etc/credstore/`, `/run/credstore/`, `/usr/lib/credstore/`. `LoadCredentialEncrypted=` will also search -`/etc/credstore.encrypted/` and similar directories. `ImportCredential` will search -both the non-encrypted and encrypted directories. These directories are +`/etc/credstore.encrypted/` and similar directories. `ImportCredential=` will +search both the non-encrypted and encrypted directories. These directories are hence a great place to store credentials to load on the system. ## Conditionalizing Services From 771c76294a42a4b53c0966b0b0fd1470c770ddc2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 30 Jun 2023 11:45:59 +0200 Subject: [PATCH 19/22] man: document how credentials are passed into generators --- man/systemd.generator.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/man/systemd.generator.xml b/man/systemd.generator.xml index 5e39c5877f..7b2501aa77 100644 --- a/man/systemd.generator.xml +++ b/man/systemd.generator.xml @@ -190,6 +190,20 @@ ConditionArchitecture= in systemd.unit5. + + + $CREDENTIALS_DIRECTORY + $ENCRYPTED_CREDENTIALS_DIRECTORY + + If set, refers to the directory system credentials have been placed in. Credentials + passed into the system in plaintext form will be placed in $CREDENTIALS_DIRECTORY, + and those passed in in encrypted form will be placed in + $ENCRYPTED_CREDENTIALS_DIRECTORY. Use the + systemd-creds1 + command to automatically decrypt/authenticate credentials passed in, if needed. Specifically, use the + systemd-creds --system cat command. + + From 49850c1ee34ce047940e4b0fa537a11365b05f5b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 30 Jun 2023 11:46:16 +0200 Subject: [PATCH 20/22] man: document where PID 1 imports credentials from --- man/systemd.xml | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/man/systemd.xml b/man/systemd.xml index 2cffe01aff..754aadbb6a 100644 --- a/man/systemd.xml +++ b/man/systemd.xml @@ -1051,7 +1051,40 @@ System credentials - The service manager when run as PID 1 reads the following system credentials: + During initialization the service manager will import credentials from various sources into the + system's set of credentials, which can then be propagated into services and consumed by + generators: + + + When the service manager first initializes it will read system credentials from SMBIOS + Type 11 vendor strings + io.systemd.credential:name=value, + and + io.systemd.credential.binary:name=value. + + At the same time it will import credentials from QEMU fw_cfg. (Note + that the SMBIOS mechanism is generally preferred, because it is faster and generic.) + + Credentials may be passed via the kernel command line, using the + systemd.set-credential= parameter, see above. + + Credentials may be passed from the UEFI environment via + systemd-stub7. + + When the service manager is invoked during the initrd → host transition it will import + all files in /run/credentials/@initrd/ as system credentials. + + + Invoke + systemd-creds1 as + follows to see the list of credentials passed into the system: + + # systemd-creds --system list + + For further information see System and Service + Credentials documentation. + + The service manager when run as PID 1 consumes the following system credentials: From de70ecb328d16dedcdea4c99cf9ff9d55491f120 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 4 Jul 2023 11:46:37 +0200 Subject: [PATCH 21/22] import-creds: add support for binary credentials specified on the kernel cmdline --- docs/CREDENTIALS.md | 9 +++++---- man/kernel-command-line.xml | 1 + man/systemd.exec.xml | 3 ++- man/systemd.xml | 7 +++++-- src/core/import-creds.c | 26 ++++++++++++++++++++++---- test/TEST-54-CREDS/test.sh | 1 + test/units/testsuite-54.sh | 1 + 7 files changed, 37 insertions(+), 11 deletions(-) diff --git a/docs/CREDENTIALS.md b/docs/CREDENTIALS.md index da5152c164..9d06c45f1f 100644 --- a/docs/CREDENTIALS.md +++ b/docs/CREDENTIALS.md @@ -322,10 +322,11 @@ services where they are ultimately consumed. be sure they can be accessed securely from initrd context. 4. Credentials can also be passed into a system via the kernel command line, - via the `systemd.set-credential=` kernel command line option. Note though - that any data specified here is visible to all userspace applications (even - unprivileged ones) via `/proc/cmdline`. Typically, this is hence not useful - to pass sensitive information, and should be avoided. + via the `systemd.set_credential=` and `systemd.set_credential_binary=` + kernel command line options (the latter takes Base64 encoded binary + data). Note though that any data specified here is visible to all userspace + applications (even unprivileged ones) via `/proc/cmdline`. Typically, this + is hence not useful to pass sensitive information, and should be avoided. Credentials passed to the system may be enumerated/displayed via `systemd-creds --system`. They may also be propagated down to services, via the diff --git a/man/kernel-command-line.xml b/man/kernel-command-line.xml index 93ed4f6acb..43247fe534 100644 --- a/man/kernel-command-line.xml +++ b/man/kernel-command-line.xml @@ -72,6 +72,7 @@ systemd.setenv= systemd.machine_id= systemd.set_credential= + systemd.set_credential_binary= systemd.import_credentials= systemd.reload_limit_interval_sec= systemd.reload_limit_burst= diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index ccec6ec423..e077407367 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -3285,7 +3285,8 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX systemd-stub7, from the initrd (see systemd1), or be - specified on the kernel command line using the systemd.set_credential= switch (see + specified on the kernel command line using the systemd.set_credential= and + systemd.set_credential_binary= switches (see systemd1 – this is not recommended since unprivileged userspace can read the kernel command line). diff --git a/man/systemd.xml b/man/systemd.xml index 754aadbb6a..21c98efecd 100644 --- a/man/systemd.xml +++ b/man/systemd.xml @@ -932,12 +932,15 @@ systemd.set_credential= + systemd.set_credential_binary= Sets a system credential, which can then be propagated to system services using the ImportCredential= or LoadCredential= setting, see systemd.exec5 for - details. Takes a pair of credential name and value, separated by a colon. Note that the kernel - command line is typically accessible by unprivileged programs in + details. Takes a pair of credential name and value, separated by a colon. The + systemd.set_credential= parameter expects the credential value in literal text + form, the systemd.set_credential_binary= parameter takes binary data encoded in + Base64. Note that the kernel command line is typically accessible by unprivileged programs in /proc/cmdline. Thus, this mechanism is not suitable for transferring sensitive data. Use it only for data that is not sensitive (e.g. public keys/certificates, rather than private keys), or in testing/debugging environments. diff --git a/src/core/import-creds.c b/src/core/import-creds.c index 6bf6ded44c..40cbf10dad 100644 --- a/src/core/import-creds.c +++ b/src/core/import-creds.c @@ -278,15 +278,21 @@ static int import_credentials_boot(void) { static int proc_cmdline_callback(const char *key, const char *value, void *data) { ImportCredentialContext *c = ASSERT_PTR(data); + _cleanup_free_ void *binary = NULL; _cleanup_free_ char *n = NULL; _cleanup_close_ int nfd = -EBADF; - const char *colon; + const char *colon, *d; + bool base64; size_t l; int r; assert(key); - if (!proc_cmdline_key_streq(key, "systemd.set_credential")) + if (proc_cmdline_key_streq(key, "systemd.set_credential")) + base64 = false; + else if (proc_cmdline_key_streq(key, "systemd.set_credential_binary")) + base64 = true; + else return 0; colon = value ? strchr(value, ':') : NULL; @@ -305,7 +311,19 @@ static int proc_cmdline_callback(const char *key, const char *value, void *data) } colon++; - l = strlen(colon); + + if (base64) { + r = unbase64mem(colon, SIZE_MAX, &binary, &l); + if (r < 0) { + log_warning_errno(r, "Failed to decode binary credential '%s' data, ignoring: %m", n); + return 0; + } + + d = binary; + } else { + d = colon; + l = strlen(colon); + } if (!credential_size_ok(c, n, l)) return 0; @@ -320,7 +338,7 @@ static int proc_cmdline_callback(const char *key, const char *value, void *data) if (nfd < 0) return nfd; - r = loop_write(nfd, colon, l, /* do_poll= */ false); + r = loop_write(nfd, d, l, /* do_poll= */ false); if (r < 0) { (void) unlinkat(c->target_dir_fd, n, 0); return log_error_errno(r, "Failed to write credential: %m"); diff --git a/test/TEST-54-CREDS/test.sh b/test/TEST-54-CREDS/test.sh index 5588242230..c0a9d7a53d 100755 --- a/test/TEST-54-CREDS/test.sh +++ b/test/TEST-54-CREDS/test.sh @@ -25,6 +25,7 @@ KERNEL_CREDS=( "systemd.set_credential=sysctl.extra:kernel.domainname=sysctltest" "systemd.set_credential=login.motd:hello" "systemd.set_credential=login.issue:welcome" + "systemd.set_credential_binary=waldi:d29vb29mZmZ3dWZmZnd1ZmYK" "rd.systemd.import_credentials=no" ) KERNEL_APPEND="${KERNEL_APPEND:-} ${KERNEL_CREDS[*]}" diff --git a/test/units/testsuite-54.sh b/test/units/testsuite-54.sh index 8ba327a3b1..6c4e006500 100755 --- a/test/units/testsuite-54.sh +++ b/test/units/testsuite-54.sh @@ -176,6 +176,7 @@ if systemd-detect-virt -q -c ; then elif [ -d /sys/firmware/qemu_fw_cfg/by_name ]; then # Verify that passing creds through kernel cmdline works [ "$(systemd-creds --system cat kernelcmdlinecred)" = "uff" ] + [ "$(systemd-creds --system cat waldi)" = "woooofffwufffwuff" ] # And that it also works via SMBIOS [ "$(systemd-creds --system cat smbioscredential)" = "magicdata" ] From aafd429ca751a8608a85c04cdcc608af3c75a406 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 23 Jun 2023 22:50:02 +0200 Subject: [PATCH 22/22] update TODO --- TODO | 36 +++++++----------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/TODO b/TODO index a3bf2c894b..307f25e618 100644 --- a/TODO +++ b/TODO @@ -131,12 +131,6 @@ Deprecations and removals: Features: -* use kernel 6.3's "noswap" parameter in tmpfs in place of ramfs for storing - credentials. - -* import-creds: allocate a non-swap-backed fs for /run/credentials/@system, - like we do for services. - * new "systemd-pcrlock" component for dealing with PCR4. Design idea: 1. define /{etc,usr,var/lib}/pcrlock.d//.pcrlock 2. these files contain list of hashes that will be measured when component is @@ -225,12 +219,10 @@ Features: support .microcode in PE add-ons, so that a microcode update can be shipped independently of any kernel. -* add clean mechanism concept for passing env/creds from initrd to host on - switch root, so that cloud-init and similar have a clean, sane method to pass - along the stuff they picked up, without patching any dirs. Maybe add - SwitchRootEx() as new bus call that takes these as argument. When adding - SwitchRootEx() we should maybe also add a flags param that allows disabling - and enabling whether serialization is requested during switch root. +* Maybe add SwitchRootEx() as new bus call that takes env vars to set for new + PID 1 as argument. When adding SwitchRootEx() we should maybe also add a + flags param that allows disabling and enabling whether serialization is + requested during switch root. * introduce a .acpitable section for early ACPI table override @@ -249,10 +241,6 @@ Features: scenarios. Maybe insist sealing is done additionally against some keypair in the TPM to which access is updated on each boot, for the next, or so? -* open up creds for uses in generators, and document clearly that encrypted - creds are only supported if strictly tpm bound, but not when using the host - secret (as that is only available if /var/ is around. - * logind: when logging in, always take an fd to the home dir, to keep the dir busy, so that autofs release can never happen. (this is generally a good idea, and specifically works around the fact the autofs ignores busy by mount @@ -819,10 +807,9 @@ Features: * Process credentials in: • networkd/udevd: add a way to define additional .link, .network, .netdev files via the credentials logic. - • fstab-generator: allow defining additional fstab-like mounts via - credentials (similar: crypttab-generator, verity-generator, - integrity-generator) - • getty-generator: allow defining additional getty instances via a credential + • crypttab-generator: allow defining additional crypttab-like volumes via + credentials (similar: verity-generator, integrity-generator). Use + fstab-generator logic as inspiration. • run-generator: allow defining additional commands to run via a credential • resolved: allow defining additional /etc/hosts entries via a credential (it might make sense to then synthesize a new combined /etc/hosts file in /run @@ -837,9 +824,6 @@ Features: systemd.homed.register or so with JSON user records to automatically register if not registered yet. Usecase: deploy a system, and add an account one can directly log into. - • initialize machine ID from systemd credential picked up from the ESP via - sd-stub, so that machine ID is stable even on systems where unified kernels - are used, and hence kernel cmdline cannot be modified locally • in gpt-auto-generator: check partition uuids against such uuids supplied via sd-stub credentials. That way, we can support parallel OS installations with pre-built kernels. @@ -948,11 +932,6 @@ Features: https://0pointer.net/blog/testing-my-system-code-in-usr-without-modifying-usr.html https://0pointer.net/blog/running-an-container-off-the-host-usr.html -* add a clear concept how the initrd can make up credentials on their own to - pass to the system when transitioning into the host OS. usecase: things like - cloud-init/ignitation and similar can parameterize the host with data they - acquire. - * sd-event: compat wd reuse in inotify code: keep a set of removed watch descriptors, and clear this set piecemeal when we see the IN_IGNORED event for it, or when read() returns EAGAIN or on IN_Q_OVERFLOW. Then, whenever we @@ -969,7 +948,6 @@ Features: - kernel-install should be able to pick up initrd sysexts automatically and place them next to EFI kernel, for sd-stub to pick them up. - systemd-fstab-generator should look for rootfs device to mount in creds - - pid 1 should look for machine ID in creds - systemd-resume-generator should look for resume partition uuid in creds - sd-stub: automatically pick up microcode from ESP (/loader/microcode/*) and synthesize initrd from it, and measure it. Signing is not necessary, as