From 58982cf0cb42e43157a48e6e32f18b831d84f5d0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Nov 2023 10:57:20 +0100 Subject: [PATCH 1/6] creds-util: add helper for opening the credentials directory --- src/shared/creds-util.c | 11 +++++++++++ src/shared/creds-util.h | 2 ++ 2 files changed, 13 insertions(+) diff --git a/src/shared/creds-util.c b/src/shared/creds-util.c index 7cc8889595..0026da5b48 100644 --- a/src/shared/creds-util.c +++ b/src/shared/creds-util.c @@ -100,6 +100,17 @@ int get_encrypted_credentials_dir(const char **ret) { return get_credentials_dir_internal("ENCRYPTED_CREDENTIALS_DIRECTORY", ret); } +int open_credentials_dir(void) { + const char *d; + int r; + + r = get_credentials_dir(&d); + if (r < 0) + return r; + + return RET_NERRNO(open(d, O_CLOEXEC|O_DIRECTORY)); +} + int read_credential(const char *name, void **ret, size_t *ret_size) { _cleanup_free_ char *fn = NULL; const char *d; diff --git a/src/shared/creds-util.h b/src/shared/creds-util.h index 5e39a6a022..36ca0fb610 100644 --- a/src/shared/creds-util.h +++ b/src/shared/creds-util.h @@ -31,6 +31,8 @@ bool credential_glob_valid(const char *s); int get_credentials_dir(const char **ret); int get_encrypted_credentials_dir(const char **ret); +int open_credentials_dir(void); + /* Where creds have been passed to the system */ #define SYSTEM_CREDENTIALS_DIRECTORY "/run/credentials/@system" #define ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY "/run/credentials/@encrypted" From 6f9dd36990fca2f99fab104b4bc78044de05c07e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Nov 2023 13:13:59 +0100 Subject: [PATCH 2/6] homectl: when taking a JSON user record as input, strip secttions we don't want rather than complain about them This makes it easier to take a user record from one host and create an identical user on another. --- src/home/homectl.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/home/homectl.c b/src/home/homectl.c index a6951c8562..9a433252c2 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -1146,7 +1146,18 @@ static int acquire_new_home_record(UserRecord **ret) { if (!hr) return log_oom(); - r = user_record_load(hr, v, USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_SECRET|USER_RECORD_ALLOW_PRIVILEGED|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_SIGNATURE|USER_RECORD_LOG|USER_RECORD_PERMISSIVE); + r = user_record_load( + hr, + v, + USER_RECORD_REQUIRE_REGULAR| + USER_RECORD_ALLOW_SECRET| + USER_RECORD_ALLOW_PRIVILEGED| + USER_RECORD_ALLOW_PER_MACHINE| + USER_RECORD_STRIP_BINDING| + USER_RECORD_STRIP_STATUS| + USER_RECORD_STRIP_SIGNATURE| + USER_RECORD_LOG| + USER_RECORD_PERMISSIVE); if (r < 0) return r; From 0a9c4a108258ce91edd27d224b0c52b431a75d87 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 30 Nov 2023 18:25:53 +0100 Subject: [PATCH 3/6] firstboot: adjust what systemd.firstboot=no on the kernel cmdline does So far by setting systemd.firstboot=no simply short-cut the whole tool and made it exit early. This is against what the docs say though: they just claim the user isn't asked for questions anymore. Let's change behaviour so that the code actually matches the docs, or more specifically: if credentials are passed into firstboot, then honour them, regardless of the kernel cmdline option. After all, if we get explicit data passed in we should operate on it, and then leave systemd.firstboot=no just affect the interactivity. I think this was actually mostly a bug introduced because the credential stuff was added after the kernel cmdline option, hence this just catches up with the new addition. --- src/firstboot/firstboot.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 17d344e980..129b73c80c 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -1655,8 +1655,8 @@ static int run(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m"); if (r > 0 && !enabled) { - log_debug("Found systemd.firstboot=no kernel command line argument, terminating."); - return 0; /* disabled */ + log_debug("Found systemd.firstboot=no kernel command line argument, turning off all prompts."); + arg_prompt_locale = arg_prompt_keymap = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = arg_prompt_root_shell = false; } } From 3ccadbce3358ba1db7ce5fa3f8dd17c627ffd93b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Nov 2023 10:58:14 +0100 Subject: [PATCH 4/6] homectl: add "firstboot" command This extends what systemd-firstboot does and runs on first boots only and either processes user records passed in via credentials to create, or asks the user interactively to create one (only if no regular user exists yet). --- man/homectl.xml | 54 +++++ man/kernel-command-line.xml | 2 + man/rules/meson.build | 2 +- man/systemd.system-credentials.xml | 10 + src/home/homectl.c | 275 ++++++++++++++++++++++---- units/meson.build | 4 + units/systemd-homed-firstboot.service | 28 +++ units/systemd-homed.service.in | 2 +- 8 files changed, 342 insertions(+), 35 deletions(-) create mode 100644 units/systemd-homed-firstboot.service diff --git a/man/homectl.xml b/man/homectl.xml index 7fc7d5f012..7e26c94179 100644 --- a/man/homectl.xml +++ b/man/homectl.xml @@ -18,6 +18,7 @@ homectl + systemd-homed-firstboot.service Create, remove, change or inspect home directories @@ -1138,6 +1139,59 @@ + + + firstboot + + This command is supposed to be invoked during the initial boot of the system. It + checks whether any regular home area exists so far, and if not queries the user interactively on the + console for user name and password and creates one. Alternatively, if one or more service credentials + whose name starts with home.create. are passed to the command (containing a user + record in JSON format) these users are automatically created at boot. + + This command is invoked by the systemd-homed-firstboot.service service + unit. + + + + + + + + Credentials + + When invoked with the firstboot command, homectl supports the + service credentials logic as implemented by + ImportCredential=/LoadCredential=/SetCredential= + (see systemd.exec1 for + details). The following credentials are used when passed in: + + + + home.create.* + + If one or more credentials whose names begin with home.create., + followed by a valid UNIX username are passed, a new home area is created, one for each specified user + record. + + + + + + + + Kernel Command Line + + + + systemd.firstboot= + + This boolean will disable the effect of homectl firstboot + command. It's primarily interpreted by + systemd-firstboot1. + + + diff --git a/man/kernel-command-line.xml b/man/kernel-command-line.xml index 6ac20ad2f4..7a7b2b7deb 100644 --- a/man/kernel-command-line.xml +++ b/man/kernel-command-line.xml @@ -594,6 +594,8 @@ Takes a boolean argument, defaults to on. If off, systemd-firstboot.service8 + and + systemd-homed-firstboot.service1 will not query the user for basic system settings, even if the system boots up for the first time and the relevant settings are not initialized yet. Not to be confused with systemd.condition-first-boot= (see below), which overrides the result of the diff --git a/man/rules/meson.build b/man/rules/meson.build index 5dc3e08896..3d63cf1131 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -18,7 +18,7 @@ manpages = [ 'ENABLE_RESOLVE'], ['environment.d', '5', [], 'ENABLE_ENVIRONMENT_D'], ['file-hierarchy', '7', [], ''], - ['homectl', '1', [], 'ENABLE_HOMED'], + ['homectl', '1', ['systemd-homed-firstboot.service'], 'ENABLE_HOMED'], ['homed.conf', '5', ['homed.conf.d'], 'ENABLE_HOMED'], ['hostname', '5', [], ''], ['hostnamectl', '1', [], 'ENABLE_HOSTNAMED'], diff --git a/man/systemd.system-credentials.xml b/man/systemd.system-credentials.xml index f7f0df18aa..2a2d03b29f 100644 --- a/man/systemd.system-credentials.xml +++ b/man/systemd.system-credentials.xml @@ -270,6 +270,16 @@ + + + home.create.* + + Creates a home area for the specified user with the user record data passed in. For details see + homectl1. + + + + diff --git a/src/home/homectl.c b/src/home/homectl.c index 9a433252c2..f2fe90c75b 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -12,6 +12,7 @@ #include "cap-list.h" #include "capability-util.h" #include "cgroup-util.h" +#include "creds-util.h" #include "dns-domain.h" #include "env-util.h" #include "fd-util.h" @@ -35,7 +36,9 @@ #include "percent-util.h" #include "pkcs11-util.h" #include "pretty-print.h" +#include "proc-cmdline.h" #include "process-util.h" +#include "recurse-dir.h" #include "rlimit-util.h" #include "spawn-polkit-agent.h" #include "terminal-util.h" @@ -45,6 +48,7 @@ #include "user-record-show.h" #include "user-record-util.h" #include "user-util.h" +#include "userdb.h" #include "verbs.h" static PagerFlags arg_pager_flags = 0; @@ -80,6 +84,7 @@ static enum { } arg_export_format = EXPORT_FORMAT_FULL; static uint64_t arg_capability_bounding_set = UINT64_MAX; static uint64_t arg_capability_ambient_set = UINT64_MAX; +static bool arg_prompt_new_user = false; STATIC_DESTRUCTOR_REGISTER(arg_identity_extra, json_variant_unrefp); STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_this_machine, json_variant_unrefp); @@ -1092,7 +1097,7 @@ static int add_disposition(JsonVariant **v) { return 1; } -static int acquire_new_home_record(UserRecord **ret) { +static int acquire_new_home_record(JsonVariant *input, UserRecord **ret) { _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_(user_record_unrefp) UserRecord *hr = NULL; int r; @@ -1102,12 +1107,16 @@ static int acquire_new_home_record(UserRecord **ret) { if (arg_identity) { unsigned line, column; + if (input) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Two identity records specified, refusing."); + r = json_parse_file( streq(arg_identity, "-") ? stdin : NULL, streq(arg_identity, "-") ? "" : arg_identity, JSON_PARSE_SENSITIVE, &v, &line, &column); if (r < 0) return log_error_errno(r, "Failed to parse identity at %u:%u: %m", line, column); - } + } else + v = json_variant_ref(input); r = apply_identity_changes(&v); if (r < 0) @@ -1258,7 +1267,7 @@ static int acquire_new_password( } } -static int create_home(int argc, char *argv[], void *userdata) { +static int create_home_common(JsonVariant *input) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(user_record_unrefp) UserRecord *hr = NULL; int r; @@ -1269,36 +1278,7 @@ static int create_home(int argc, char *argv[], void *userdata) { (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password); - if (argc >= 2) { - /* If a username was specified, use it */ - - if (valid_user_group_name(argv[1], 0)) - r = json_variant_set_field_string(&arg_identity_extra, "userName", argv[1]); - else { - _cleanup_free_ char *un = NULL, *rr = NULL; - - /* Before we consider the user name invalid, let's check if we can split it? */ - r = split_user_name_realm(argv[1], &un, &rr); - if (r < 0) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name '%s' is not valid: %m", argv[1]); - - if (rr) { - r = json_variant_set_field_string(&arg_identity_extra, "realm", rr); - if (r < 0) - return log_error_errno(r, "Failed to set realm field: %m"); - } - - r = json_variant_set_field_string(&arg_identity_extra, "userName", un); - } - if (r < 0) - return log_error_errno(r, "Failed to set userName field: %m"); - } else { - /* If neither a username nor an identity have been specified we cannot operate. */ - if (!arg_identity) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name required."); - } - - r = acquire_new_home_record(&hr); + r = acquire_new_home_record(input, &hr); if (r < 0) return r; @@ -1385,6 +1365,41 @@ static int create_home(int argc, char *argv[], void *userdata) { return 0; } +static int create_home(int argc, char *argv[], void *userdata) { + int r; + + if (argc >= 2) { + /* If a username was specified, use it */ + + if (valid_user_group_name(argv[1], 0)) + r = json_variant_set_field_string(&arg_identity_extra, "userName", argv[1]); + else { + _cleanup_free_ char *un = NULL, *rr = NULL; + + /* Before we consider the user name invalid, let's check if we can split it? */ + r = split_user_name_realm(argv[1], &un, &rr); + if (r < 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name '%s' is not valid: %m", argv[1]); + + if (rr) { + r = json_variant_set_field_string(&arg_identity_extra, "realm", rr); + if (r < 0) + return log_error_errno(r, "Failed to set realm field: %m"); + } + + r = json_variant_set_field_string(&arg_identity_extra, "userName", un); + } + if (r < 0) + return log_error_errno(r, "Failed to set userName field: %m"); + } else { + /* If neither a username nor an identity have been specified we cannot operate. */ + if (!arg_identity) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User name required."); + } + + return create_home_common(/* input= */ NULL); +} + static int remove_home(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r, ret = 0; @@ -2142,6 +2157,190 @@ static int rebalance(int argc, char *argv[], void *userdata) { return 0; } +static int create_from_credentials(void) { + _cleanup_close_ int fd = -EBADF; + int ret = 0, n_created = 0, r; + + fd = open_credentials_dir(); + if (IN_SET(fd, -ENXIO, -ENOENT)) /* Credential env var not set, or dir doesn't exist. */ + return 0; + if (fd < 0) + return log_error_errno(fd, "Failed to open credentials directory: %m"); + + _cleanup_free_ DirectoryEntries *des = NULL; + r = readdir_all(fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, &des); + if (r < 0) + return log_error_errno(r, "Failed to enumerate credentials: %m"); + + FOREACH_ARRAY(i, des->entries, des->n_entries) { + _cleanup_(json_variant_unrefp) JsonVariant *identity = NULL; + struct dirent *de = *i; + const char *e; + + if (de->d_type != DT_REG) + continue; + + e = startswith(de->d_name, "home.create."); + if (!e) + continue; + + if (!valid_user_group_name(e, 0)) { + log_notice("Skipping over credential with name that is not a suitable user name: %s", de->d_name); + continue; + } + + r = json_parse_file_at( + /* f= */ NULL, + fd, + de->d_name, + /* flags= */ 0, + &identity, + /* ret_line= */ NULL, + /* ret_column= */ NULL); + if (r < 0) { + log_warning_errno(r, "Failed to parse user record in credential '%s', ignoring: %m", de->d_name); + continue; + } + + JsonVariant *un; + un = json_variant_by_key(identity, "userName"); + if (un) { + if (!json_variant_is_string(un)) { + log_warning("User record from credential '%s' contains 'userName' field of invalid type, ignoring.", de->d_name); + continue; + } + + if (!streq(json_variant_string(un), e)) { + log_warning("User record from credential '%s' contains 'userName' field (%s) that doesn't match credential name (%s), ignoring.", de->d_name, json_variant_string(un), e); + continue; + } + } else { + r = json_variant_set_field_string(&identity, "userName", e); + if (r < 0) + return log_warning_errno(r, "Failed to set userName field: %m"); + } + + log_notice("Processing user '%s' from credentials.", e); + + r = create_home_common(identity); + if (r >= 0) + n_created++; + + RET_GATHER(ret, r); + } + + return ret < 0 ? ret : n_created; +} + +static int has_regular_user(void) { + _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL; + int r; + + r = userdb_all(USERDB_SUPPRESS_SHADOW, &iterator); + if (r < 0) + return log_error_errno(r, "Failed to create user enumerator: %m"); + + for (;;) { + _cleanup_(user_record_unrefp) UserRecord *ur = NULL; + + r = userdb_iterator_get(iterator, &ur); + if (r == -ESRCH) + break; + if (r < 0) + return log_error_errno(r, "Failed to enumerate users: %m"); + + if (user_record_disposition(ur) == USER_REGULAR) + return true; + } + + return false; +} + +static int create_interactively(void) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_free_ char *username = NULL; + int r; + + if (!arg_prompt_new_user) { + log_debug("Prompting for user creation was not requested."); + return 0; + } + + r = acquire_bus(&bus); + if (r < 0) + return r; + + (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password); + + (void) reset_terminal_fd(STDIN_FILENO, /* switch_to_text= */ false); + + for (;;) { + username = mfree(username); + + r = ask_string(&username, + "%s Please enter user name to create (empty to skip): ", + special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET)); + if (r < 0) + return log_error_errno(r, "Failed to query user for username: %m"); + + if (isempty(username)) { + log_info("No data entered, skipping."); + return 0; + } + + if (!valid_user_group_name(username, /* flags= */ 0)) { + log_notice("Specified user name is not a valid UNIX user name, try again: %s", username); + continue; + } + + r = userdb_by_name(username, USERDB_SUPPRESS_SHADOW, /* ret= */ NULL); + if (r == -ESRCH) + break; + if (r < 0) + return log_error_errno(r, "Failed to check if specified user '%s' already exists: %m", username); + + log_notice("Specified user '%s' exists already, try again.", username); + } + + r = json_variant_set_field_string(&arg_identity_extra, "userName", username); + if (r < 0) + return log_error_errno(r, "Failed to set userName field: %m"); + + return create_home_common(/* input= */ NULL); +} + +static int verb_firstboot(int argc, char *argv[], void *userdata) { + int r; + + /* Let's honour the systemd.firstboot kernel command line option, just like the systemd-firstboot + * tool. */ + + bool enabled; + r = proc_cmdline_get_bool("systemd.firstboot", /* flags = */ 0, &enabled); + if (r < 0) + return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m"); + if (r > 0 && !enabled) { + log_debug("Found systemd.firstboot=no kernel command line argument, turning off all prompts."); + arg_prompt_new_user = false; + } + + r = create_from_credentials(); + if (r < 0) + return r; + if (r > 0) /* Already created users from credentials */ + return 0; + + r = has_regular_user(); + if (r < 0) + return r; + if (r > 0) { + log_info("Regular user already present in user database, skipping user creation."); + return 0; + } + + return create_interactively(); +} + static int drop_from_identity(const char *field) { int r; @@ -2198,6 +2397,7 @@ static int help(int argc, char *argv[], void *userdata) { " deactivate-all Deactivate all active home areas\n" " rebalance Rebalance free space between home areas\n" " with USER [COMMAND…] Run shell or command with access to a home area\n" + " firstboot Run first-boot home area creation wizard\n" "\n%4$sOptions:%5$s\n" " -h --help Show this help\n" " --version Show package version\n" @@ -2216,6 +2416,8 @@ static int help(int argc, char *argv[], void *userdata) { " -E When specified once equals -j --export-format=\n" " stripped, when specified twice equals\n" " -j --export-format=minimal\n" + " --prompt-new-user firstboot: Query user interactively for user\n" + " to create\n" "\n%4$sGeneral User Record Properties:%5$s\n" " -c --real-name=REALNAME Real name for user\n" " --realm=REALM Realm to create user in\n" @@ -2423,6 +2625,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_FIDO2_CRED_ALG, ARG_CAPABILITY_BOUNDING_SET, ARG_CAPABILITY_AMBIENT_SET, + ARG_PROMPT_NEW_USER, }; static const struct option options[] = { @@ -2515,6 +2718,7 @@ static int parse_argv(int argc, char *argv[]) { { "rebalance-weight", required_argument, NULL, ARG_REBALANCE_WEIGHT }, { "capability-bounding-set", required_argument, NULL, ARG_CAPABILITY_BOUNDING_SET }, { "capability-ambient-set", required_argument, NULL, ARG_CAPABILITY_AMBIENT_SET }, + { "prompt-new-user", no_argument, NULL, ARG_PROMPT_NEW_USER }, {} }; @@ -3799,6 +4003,10 @@ static int parse_argv(int argc, char *argv[]) { break; } + case ARG_PROMPT_NEW_USER: + arg_prompt_new_user = true; + break; + case '?': return -EINVAL; @@ -3865,6 +4073,7 @@ static int run(int argc, char *argv[]) { { "lock-all", VERB_ANY, 1, 0, lock_all_homes }, { "deactivate-all", VERB_ANY, 1, 0, deactivate_all_homes }, { "rebalance", VERB_ANY, 1, 0, rebalance }, + { "firstboot", VERB_ANY, 1, 0, verb_firstboot }, {} }; diff --git a/units/meson.build b/units/meson.build index e7bfb7f838..8542245239 100644 --- a/units/meson.build +++ b/units/meson.build @@ -303,6 +303,10 @@ units = [ 'file' : 'systemd-homed-activate.service', 'conditions' : ['ENABLE_HOMED'], }, + { + 'file' : 'systemd-homed-firstboot.service', + 'conditions' : ['ENABLE_HOMED'], + }, { 'file' : 'systemd-homed.service.in', 'conditions' : ['ENABLE_HOMED'], diff --git a/units/systemd-homed-firstboot.service b/units/systemd-homed-firstboot.service new file mode 100644 index 0000000000..3615940a4e --- /dev/null +++ b/units/systemd-homed-firstboot.service @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=First Boot Home Area Wizard +Documentation=man:homectl(1) +ConditionFirstBoot=yes +After=home.mount systemd-homed.service +Before=systemd-user-sessions.service first-boot-complete.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=homectl firstboot --prompt-new-user +StandardOutput=tty +StandardInput=tty +StandardError=tty +ImportCredential=home.* + +[Install] +WantedBy=systemd-homed.service +Also=systemd-homed.service diff --git a/units/systemd-homed.service.in b/units/systemd-homed.service.in index e629048b97..bfc3df1021 100644 --- a/units/systemd-homed.service.in +++ b/units/systemd-homed.service.in @@ -39,4 +39,4 @@ TimeoutStopSec=3min [Install] WantedBy=multi-user.target Alias=dbus-org.freedesktop.home1.service -Also=systemd-homed-activate.service systemd-userdbd.service +Also=systemd-homed-activate.service systemd-userdbd.service systemd-homed-firstboot.service From 641489e25760a20e244f8ea5105cbcd9100555df Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 30 Nov 2023 18:41:53 +0100 Subject: [PATCH 5/6] mkosi: use systemd.firstboot=no to turn of interactivity at boot Now that creds are processed even if systemd.firstboot=no is set, we can use it to disable the root pw prompt *and* the new homectl prompt at the same time, without breaking the creds stuff. --- mkosi.conf | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mkosi.conf b/mkosi.conf index 9961407a04..b66b7726f1 100644 --- a/mkosi.conf +++ b/mkosi.conf @@ -18,8 +18,6 @@ Environment=ASAN_OPTIONS=verify_asan_link_order=false @Incremental=yes @QemuMem=2G @RuntimeSize=8G -# Make sure we don't trigger systemd-firstboot prompting for the root password. -Credentials=passwd.plaintext-password.root= KernelCommandLineExtra=systemd.crash_shell systemd.log_level=debug systemd.log_ratelimit_kmsg=0 @@ -37,3 +35,4 @@ KernelCommandLineExtra=systemd.crash_shell selinux=0 enforcing=0 systemd.early_core_pattern=/core + systemd.firstboot=no From 604cfd1e4a9a46c63186d9bd5ee6e85663a16617 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Nov 2023 10:59:57 +0100 Subject: [PATCH 6/6] update TODO --- TODO | 9 --------- 1 file changed, 9 deletions(-) diff --git a/TODO b/TODO index d74daa0663..519db3b446 100644 --- a/TODO +++ b/TODO @@ -931,10 +931,6 @@ Features: file system paths to enable on start. • make systemd-fstab-generator look for a system credential encoding root= or usr= - • systemd-homed: when initializing, look for a credential - systemd.homed.register or so with JSON user records to automatically - register if not registered yet. Use case: deploy a system, and add an - account one can directly log into. • 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. @@ -2259,11 +2255,6 @@ Features: - support new FS_IOC_ADD_ENCRYPTION_KEY ioctl for setting up fscrypt - maybe pre-create ~/.cache as subvol so that it can have separate quota easily? - - add a switch to homectl (maybe called --first-boot) where it will check if - any non-system users exist, and if not prompts interactively for basic user - info, mimicking systemd-firstboot. Then, place this in a service that runs - after systemd-homed, but before gdm and friends, as a simple, barebones - fallback logic to get a regular user created on uninitialized systems. - store PKCS#11 + FIDO2 token info in LUKS2 header, compatible with systemd-cryptsetup, so that it can unlock homed volumes - maybe make all *.home files owned by `systemd-home` user or so, so that we