diff --git a/man/systemd-repart.xml b/man/systemd-repart.xml index fb7b5496af..d05b74f57d 100644 --- a/man/systemd-repart.xml +++ b/man/systemd-repart.xml @@ -516,6 +516,47 @@ + + TYPE + + Takes one of sysext, confext or + portable. Generates a Discoverable Disk Image (DDI) for a system extension + (sysext, see + systemd-sysext8 + for details), configuration extension (confext) or portable service. The generated image will consist + of a signed Verity erofs file system as root partition. In this mode of operation + the partition definitions in /usr/lib/repart.d/*.conf and related directories + are not read, and is not supported, as appropriate definitions for + the selected DDI class will be chosen automatically. + + Must be used in conjunction with to specify the file hierarchy + to populate the DDI with. The specified directory should contain an etc/ + subdirectory if confext is selected. If sysext is selected it + should contain either a usr/ or opt/ directory, or both. If + portable is used a full OS file hierarchy can be provided. + + This option implies , and + (the latter two can be overriden). + + The private key and certificate for signing the DDI must be specified via the + and switches. + + + + + + + + + + Shortcuts for , + , , + respectively. + + + + @@ -530,13 +571,41 @@ On success, 0 is returned, a non-zero failure code otherwise. + + Example + + + Generate a configuration extension image + + The following creates a configuration extension DDI (confext) for an + /etc/motd update. + + mkdir tree tree/etc tree/etc/extension-release.d +echo "Hello World" > tree/etc/motd +cat > tree/etc/extension-release.d/extension-release.my-motd <<EOF +ID=fedora +VERSION_ID=38 +IMAGE_ID=my-motd +IMAGE_VERSION=7 +EOF +systemd-repart -C --private-key=privkey.pem --certificate=cert.crt -s tree/ /var/lib/confexts/my-motd.confext.raw +systemd-confext refresh + + The DDI generated that way may be applied to the system with + systemd-confext1. + + + + See Also systemd1, repart.d5, machine-id5, - systemd-cryptenroll1 + systemd-cryptenroll1, + portablectl1, + systemd-sysext8 diff --git a/meson.build b/meson.build index 1e59d353ee..88bc1367e0 100644 --- a/meson.build +++ b/meson.build @@ -171,6 +171,7 @@ systemdstatedir = localstatedir / 'lib/systemd' catalogstatedir = systemdstatedir / 'catalog' randomseeddir = localstatedir / 'lib/systemd' profiledir = libexecdir / 'portable' / 'profile' +repartdefinitionsdir = libexecdir / 'repart/definitions' ntpservicelistdir = prefixdir / 'lib/systemd/ntp-units.d' credstoredir = prefixdir / 'lib/credstore' diff --git a/src/partition/definitions/confext.repart.d/10-root.conf b/src/partition/definitions/confext.repart.d/10-root.conf new file mode 100644 index 0000000000..e41dc0578b --- /dev/null +++ b/src/partition/definitions/confext.repart.d/10-root.conf @@ -0,0 +1,15 @@ +# 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. + +[Partition] +Type=root +Format=erofs +CopyFiles=/etc/ +Verity=data +VerityMatchKey=root diff --git a/src/partition/definitions/confext.repart.d/20-root-verity.conf b/src/partition/definitions/confext.repart.d/20-root-verity.conf new file mode 100644 index 0000000000..437d88e068 --- /dev/null +++ b/src/partition/definitions/confext.repart.d/20-root-verity.conf @@ -0,0 +1,13 @@ +# 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. + +[Partition] +Type=root-verity +Verity=hash +VerityMatchKey=root diff --git a/src/partition/definitions/confext.repart.d/30-root-verity-sig.conf b/src/partition/definitions/confext.repart.d/30-root-verity-sig.conf new file mode 100644 index 0000000000..df160154a7 --- /dev/null +++ b/src/partition/definitions/confext.repart.d/30-root-verity-sig.conf @@ -0,0 +1,13 @@ +# 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. + +[Partition] +Type=root-verity-sig +Verity=signature +VerityMatchKey=root diff --git a/src/partition/definitions/portable.repart.d/10-root.conf b/src/partition/definitions/portable.repart.d/10-root.conf new file mode 100644 index 0000000000..78758002f5 --- /dev/null +++ b/src/partition/definitions/portable.repart.d/10-root.conf @@ -0,0 +1,15 @@ +# 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. + +[Partition] +Type=root +Format=erofs +CopyFiles=/ +Verity=data +VerityMatchKey=root diff --git a/src/partition/definitions/portable.repart.d/20-root-verity.conf b/src/partition/definitions/portable.repart.d/20-root-verity.conf new file mode 100644 index 0000000000..437d88e068 --- /dev/null +++ b/src/partition/definitions/portable.repart.d/20-root-verity.conf @@ -0,0 +1,13 @@ +# 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. + +[Partition] +Type=root-verity +Verity=hash +VerityMatchKey=root diff --git a/src/partition/definitions/portable.repart.d/30-root-verity-sig.conf b/src/partition/definitions/portable.repart.d/30-root-verity-sig.conf new file mode 100644 index 0000000000..df160154a7 --- /dev/null +++ b/src/partition/definitions/portable.repart.d/30-root-verity-sig.conf @@ -0,0 +1,13 @@ +# 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. + +[Partition] +Type=root-verity-sig +Verity=signature +VerityMatchKey=root diff --git a/src/partition/definitions/sysext.repart.d/10-root.conf b/src/partition/definitions/sysext.repart.d/10-root.conf new file mode 100644 index 0000000000..41a7ca56d7 --- /dev/null +++ b/src/partition/definitions/sysext.repart.d/10-root.conf @@ -0,0 +1,15 @@ +# 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. + +[Partition] +Type=root +Format=erofs +CopyFiles=/usr/ /opt/ +Verity=data +VerityMatchKey=root diff --git a/src/partition/definitions/sysext.repart.d/20-root-verity.conf b/src/partition/definitions/sysext.repart.d/20-root-verity.conf new file mode 100644 index 0000000000..437d88e068 --- /dev/null +++ b/src/partition/definitions/sysext.repart.d/20-root-verity.conf @@ -0,0 +1,13 @@ +# 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. + +[Partition] +Type=root-verity +Verity=hash +VerityMatchKey=root diff --git a/src/partition/definitions/sysext.repart.d/30-root-verity-sig.conf b/src/partition/definitions/sysext.repart.d/30-root-verity-sig.conf new file mode 100644 index 0000000000..df160154a7 --- /dev/null +++ b/src/partition/definitions/sysext.repart.d/30-root-verity-sig.conf @@ -0,0 +1,13 @@ +# 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. + +[Partition] +Type=root-verity-sig +Verity=signature +VerityMatchKey=root diff --git a/src/partition/meson.build b/src/partition/meson.build index 19f49f606e..78cde2ff52 100644 --- a/src/partition/meson.build +++ b/src/partition/meson.build @@ -48,3 +48,15 @@ executables += [ 'install' : have_standalone_binaries, }, ] + +if conf.get('ENABLE_REPART') == 1 + install_data('definitions/confext.repart.d/10-root.conf', install_dir : repartdefinitionsdir / 'confext.repart.d') + install_data('definitions/confext.repart.d/20-root-verity.conf', install_dir : repartdefinitionsdir / 'confext.repart.d') + install_data('definitions/confext.repart.d/30-root-verity-sig.conf', install_dir : repartdefinitionsdir / 'confext.repart.d') + install_data('definitions/portable.repart.d/10-root.conf', install_dir : repartdefinitionsdir / 'portable.repart.d') + install_data('definitions/portable.repart.d/20-root-verity.conf', install_dir : repartdefinitionsdir / 'portable.repart.d') + install_data('definitions/portable.repart.d/30-root-verity-sig.conf', install_dir : repartdefinitionsdir / 'portable.repart.d') + install_data('definitions/sysext.repart.d/10-root.conf', install_dir : repartdefinitionsdir / 'sysext.repart.d') + install_data('definitions/sysext.repart.d/20-root-verity.conf', install_dir : repartdefinitionsdir / 'sysext.repart.d') + install_data('definitions/sysext.repart.d/30-root-verity-sig.conf', install_dir : repartdefinitionsdir / 'sysext.repart.d') +endif diff --git a/src/partition/repart.c b/src/partition/repart.c index 770f7c48ff..855e908d75 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -163,6 +163,7 @@ static Architecture arg_architecture = _ARCHITECTURE_INVALID; static int arg_offline = -1; static char **arg_copy_from = NULL; static char *arg_copy_source = NULL; +static char *arg_make_ddi = NULL; STATIC_DESTRUCTOR_REGISTER(arg_root, freep); STATIC_DESTRUCTOR_REGISTER(arg_image, freep); @@ -177,6 +178,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_filter_partitions, freep); STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep); STATIC_DESTRUCTOR_REGISTER(arg_copy_from, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_copy_source, freep); +STATIC_DESTRUCTOR_REGISTER(arg_make_ddi, freep); typedef struct FreeArea FreeArea; @@ -6376,6 +6378,9 @@ static int help(void) { " --offline=BOOL Whether to build the image offline\n" " -s --copy-source=PATH Specify the primary source tree to copy files from\n" " --copy-from=IMAGE Copy partitions from the given image(s)\n" + " -S --make-ddi=sysext Make a system extension DDI\n" + " -C --make-ddi=confext Make a configuration extension DDI\n" + " -P --make-ddi=portable Make a portable service DDI\n" "\nSee the %s for details.\n", program_invocation_short_name, ansi_highlight(), @@ -6420,7 +6425,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_ARCHITECTURE, ARG_OFFLINE, ARG_COPY_FROM, - ARG_COPY_SOURCE, + ARG_MAKE_DDI, }; static const struct option options[] = { @@ -6457,6 +6462,7 @@ static int parse_argv(int argc, char *argv[]) { { "offline", required_argument, NULL, ARG_OFFLINE }, { "copy-from", required_argument, NULL, ARG_COPY_FROM }, { "copy-source", required_argument, NULL, 's' }, + { "make-ddi", required_argument, NULL, ARG_MAKE_DDI }, {} }; @@ -6465,7 +6471,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hs:", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "hs:SCP", options, NULL)) >= 0) switch (c) { @@ -6796,6 +6802,33 @@ static int parse_argv(int argc, char *argv[]) { return r; break; + case ARG_MAKE_DDI: + if (!filename_is_valid(optarg)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid DDI type: %s", optarg); + + r = free_and_strdup_warn(&arg_make_ddi, optarg); + if (r < 0) + return r; + break; + + case 'S': + r = free_and_strdup_warn(&arg_make_ddi, "sysext"); + if (r < 0) + return r; + break; + + case 'C': + r = free_and_strdup_warn(&arg_make_ddi, "confext"); + if (r < 0) + return r; + break; + + case 'P': + r = free_and_strdup_warn(&arg_make_ddi, "portable"); + if (r < 0) + return r; + break; + case '?': return -EINVAL; @@ -6807,6 +6840,38 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected at most one argument, the path to the block device."); + if (arg_make_ddi) { + if (arg_definitions) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Combination of --make-ddi= and --definitions= is not supported."); + if (!IN_SET(arg_empty, EMPTY_UNSET, EMPTY_CREATE)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Combination of --make-ddi= and --empty=%s is not supported.", + arg_empty == EMPTY_REFUSE ? "refuse" : + arg_empty == EMPTY_ALLOW ? "allow" : + arg_empty == EMPTY_REQUIRE ? "require" : "force"); + + /* Imply automatic sizing in DDI mode */ + if (arg_size == UINT64_MAX) + arg_size_auto = true; + + if (!arg_copy_source) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No --copy-source= specified, refusing."); + + r = dir_is_empty(arg_copy_source, /* ignore_hidden_or_backup= */ false); + if (r < 0) + return log_error_errno(r, "Failed to determine if '%s' is empty: %m", arg_copy_source); + if (r > 0) + return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Source directory '%s' is empty, refusing to create empty image.", arg_copy_source); + + if (sd_id128_is_null(arg_seed) && !arg_randomize) { + /* We don't want that /etc/machine-id leaks into any image built this way, hence + * let's randomize the seed if not specified explicitly */ + log_notice("No seed value specified, randomizing generated UUIDs, resulting image will not be reproducible."); + arg_randomize = true; + } + + arg_empty = EMPTY_CREATE; + } + if (arg_empty == EMPTY_UNSET) /* default to refuse mode, if not otherwise specified */ arg_empty = EMPTY_REFUSE; @@ -7371,7 +7436,22 @@ static int run(int argc, char *argv[]) { if (r < 0) return r; - strv_uniq(arg_definitions); + if (arg_make_ddi) { + _cleanup_free_ char *d = NULL, *dp = NULL; + assert(!arg_definitions); + + d = strjoin(arg_make_ddi, ".repart.d/"); + if (!d) + return log_oom(); + + r = search_and_access(d, F_OK, arg_root, CONF_PATHS_USR_STRV("systemd/repart/definitions"), &dp); + if (r < 0) + return log_error_errno(errno, "DDI type '%s' is not defined: %m", arg_make_ddi); + + if (strv_consume(&arg_definitions, TAKE_PTR(dp)) < 0) + return log_oom(); + } else + strv_uniq(arg_definitions); r = context_read_definitions(context); if (r < 0)