From: Lennart Poettering Date: Thu, 21 May 2026 06:39:23 +0000 (+0200) Subject: firstboot: allow configuring machine tags via firstboot X-Git-Tag: v261-rc1~29^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b6d9a987f7fbc82cf0f09f6f6b4cc9e1bb26ee78;p=thirdparty%2Fsystemd.git firstboot: allow configuring machine tags via firstboot --- diff --git a/man/systemd-firstboot.xml b/man/systemd-firstboot.xml index db6f2569a8d..252ec73fedd 100644 --- a/man/systemd-firstboot.xml +++ b/man/systemd-firstboot.xml @@ -67,6 +67,8 @@ The system hostname + The machine tags + The kernel command line used when installing kernel images The root user's password and shell @@ -198,6 +200,27 @@ + + + + Set the machine tags to the specified colon-separated list. Machine tags are short + labels that may be used to classify and group machines for management purposes, for example to + identify the role a machine plays in a deployment, the fleet or organizational unit it belongs to, or + any other administrator-defined attribute. Each individual tag must be 1…255 characters long and + consist only of ASCII alphanumeric characters, - and .. This + controls the TAGS= field of + machine-info5. The + tags may later be queried and changed with the tags command of + hostnamectl1, and + matched against with the ConditionMachineTag=/AssertMachineTag= + unit settings, see + systemd.unit5. + + Unlike most other settings, machine tags are not prompted for interactively. + + + + @@ -458,6 +481,18 @@ + + + firstboot.machine-tags + + This credential specifies the machine tags to set during first boot, as a + colon-separated list, equivalent to the switch described above. The + tags are written into the TAGS= field of /etc/machine-info + (if that file is not already present), and only have an effect on first boot. If the list contains + invalid tags it is ignored in its entirety. + + + Note that by default the systemd-firstboot.service unit file is set up to diff --git a/man/systemd.system-credentials.xml b/man/systemd.system-credentials.xml index fb1377c560c..f316961cd72 100644 --- a/man/systemd.system-credentials.xml +++ b/man/systemd.system-credentials.xml @@ -91,6 +91,21 @@ + + firstboot.machine-tags + + The machine tags to set, as a colon-separated list (e.g. + webserver:frontend:berlin). Read by + systemd-firstboot1, + and only honoured if /etc/machine-info has not been configured before. Written + into the TAGS= field of that file, see + machine-info5 for + details. + + + + + login.issue diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index e328c855001..e922abdd63c 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -67,6 +67,7 @@ static char *arg_keymap = NULL; static char *arg_timezone = NULL; static char *arg_hostname = NULL; static sd_id128_t arg_machine_id = {}; +static char **arg_machine_tags = NULL; static char *arg_root_password = NULL; static char *arg_root_shell = NULL; static char *arg_kernel_cmdline = NULL; @@ -98,6 +99,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_locale_messages, freep); STATIC_DESTRUCTOR_REGISTER(arg_keymap, freep); STATIC_DESTRUCTOR_REGISTER(arg_timezone, freep); STATIC_DESTRUCTOR_REGISTER(arg_hostname, freep); +STATIC_DESTRUCTOR_REGISTER(arg_machine_tags, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_root_password, erase_and_freep); STATIC_DESTRUCTOR_REGISTER(arg_root_shell, freep); STATIC_DESTRUCTOR_REGISTER(arg_kernel_cmdline, freep); @@ -778,6 +780,71 @@ static int process_machine_id(int rfd) { return 0; } +static int process_machine_tags(int rfd) { + int r; + + assert(rfd >= 0); + + _cleanup_free_ char *f = NULL; + _cleanup_close_ int pfd = chase_and_open_parent_at( + /* root_fd= */ rfd, + /* dir_fd= */ rfd, + "/etc/machine-info", + CHASE_MKDIR_0755|CHASE_WARN|CHASE_NOFOLLOW, + &f); + if (pfd < 0) + return log_error_errno(pfd, "Failed to chase /etc/machine-info parent: %m"); + + r = should_configure(pfd, f); + if (r == 0) + log_debug("Found /etc/machine-info, assuming machine tags have been configured."); + if (r <= 0) + return r; + + if (!arg_machine_tags) { + _cleanup_free_ char *tags = NULL; + r = read_credential("firstboot.machine-tags", (void**) &tags, /* ret_size= */ NULL); + if (r < 0) + log_debug_errno(r, "Failed to read credential firstboot.machine-tags, ignoring: %m"); + else { + _cleanup_strv_free_ char **l = NULL; + r = machine_tags_from_string(tags, /* graceful= */ false, &l); + if (r < 0) + log_warning_errno(r, "Failed to parse machine tags '%s', ignoring credential: %m", tags); + else { + strv_free_and_replace(arg_machine_tags, l); + log_debug("Acquired machine tags list from credentials."); + } + } + } + + /* NB: We do not prompt for machine tags, at least not for now */ + + if (!arg_machine_tags) { + log_debug("Initialization of machine tags was not requested, skipping."); + return 0; + } + + _cleanup_free_ char *j = strv_join(arg_machine_tags, ":"); + if (!j) + return log_oom(); + + _cleanup_free_ char *c = strjoin("TAGS=\"", j, "\"\n"); + if (!c) + return log_oom(); + + r = write_string_file_at( + pfd, + "machine-info", + c, + WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_SYNC|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_LABEL); + if (r < 0) + return log_error_errno(r, "Failed to write /etc/machine-info: %m"); + + log_info("/etc/machine-info written."); + return 0; +} + static int prompt_root_password(int rfd, sd_varlink **mute_console_link) { const char *msg1, *msg2; int r; @@ -1363,6 +1430,16 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(r, "Failed to parse machine id %s.", opts.arg); break; + OPTION_LONG("machine-tags", "TAG[:…]", "Set machine tags"): { + _cleanup_strv_free_ char **tags = NULL; + r = machine_tags_from_string(opts.arg, /* graceful= */ false, &tags); + if (r < 0) + return log_error_errno(r, "Failed to parse machine tags '%s': %m", opts.arg); + + strv_free_and_replace(arg_machine_tags, tags); + break; + } + OPTION_LONG("root-password", "PASSWORD", "Set root password from plaintext password"): r = free_and_strdup_warn(&arg_root_password, opts.arg); if (r < 0) @@ -1693,6 +1770,10 @@ static int run(int argc, char *argv[]) { if (r < 0) return r; + r = process_machine_tags(rfd); + if (r < 0) + return r; + return 0; }