]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
repart: make it easy to generate DDIs
authorLennart Poettering <lennart@poettering.net>
Thu, 28 Sep 2023 16:16:59 +0000 (18:16 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 5 Oct 2023 17:08:11 +0000 (19:08 +0200)
This adds --make-ddi=confext, --make-ddi=sysext, --make-ddi=portable, to
make it really easiy to generate DDIs of the specified class. It
it's ultimately just a fancy wrapper around some defaults and in
particular --definitions=.

This makes it very easy to generate a confext:

 $ systemd-repart -C --private-key=privkey.pem --certificate=cert.crt -s mytree/ mytree.confext.raw

13 files changed:
man/systemd-repart.xml
meson.build
src/partition/definitions/confext.repart.d/10-root.conf [new file with mode: 0644]
src/partition/definitions/confext.repart.d/20-root-verity.conf [new file with mode: 0644]
src/partition/definitions/confext.repart.d/30-root-verity-sig.conf [new file with mode: 0644]
src/partition/definitions/portable.repart.d/10-root.conf [new file with mode: 0644]
src/partition/definitions/portable.repart.d/20-root-verity.conf [new file with mode: 0644]
src/partition/definitions/portable.repart.d/30-root-verity-sig.conf [new file with mode: 0644]
src/partition/definitions/sysext.repart.d/10-root.conf [new file with mode: 0644]
src/partition/definitions/sysext.repart.d/20-root-verity.conf [new file with mode: 0644]
src/partition/definitions/sysext.repart.d/30-root-verity-sig.conf [new file with mode: 0644]
src/partition/meson.build
src/partition/repart.c

index fb7b5496af54718944067430d3ec5e57e04f13eb..d05b74f57db42834f4bfae5b4bfffc44e7702fcf 100644 (file)
         <xi:include href="version-info.xml" xpointer="v255"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--make-ddi=</option><replaceable>TYPE</replaceable></term>
+
+        <listitem><para>Takes one of <literal>sysext</literal>, <literal>confext</literal> or
+        <literal>portable</literal>. Generates a Discoverable Disk Image (DDI) for a system extension
+        (sysext, see
+        <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        for details), configuration extension (confext) or <ulink
+        url="https://systemd.io/PORTABLE_SERVICES">portable service</ulink>. The generated image will consist
+        of a signed Verity <literal>erofs</literal> file system as root partition. In this mode of operation
+        the partition definitions in <filename>/usr/lib/repart.d/*.conf</filename> and related directories
+        are not read, and <option>--definitions=</option> is not supported, as appropriate definitions for
+        the selected DDI class will be chosen automatically.</para>
+
+        <para>Must be used in conjunction with <option>--copy-source=</option> to specify the file hierarchy
+        to populate the DDI with. The specified directory should contain an <filename>etc/</filename>
+        subdirectory if <literal>confext</literal> is selected. If <literal>sysext</literal> is selected it
+        should contain either a <filename>usr/</filename> or <filename>opt/</filename> directory, or both. If
+        <literal>portable</literal> is used a full OS file hierarchy can be provided.</para>
+
+        <para>This option implies <option>--empty=create</option>, <option>--size=auto</option> and
+        <option>--seed=random</option> (the latter two can be overriden).</para>
+
+        <para>The private key and certificate for signing the DDI must be specified via the
+        <option>--private-key=</option> and <option>--certificate=</option> switches.</para>
+
+        <xi:include href="version-info.xml" xpointer="v255"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-S</option></term>
+        <term><option>-C</option></term>
+        <term><option>-P</option></term>
+
+        <listitem><para>Shortcuts for <option>--make-ddi=sysext</option>,
+        <option>--make-ddi=confext</option>, <option>--make-ddi=portable</option>,
+        respectively.</para>
+
+        <xi:include href="version-info.xml" xpointer="v255"/></listitem>
+      </varlistentry>
+
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
       <xi:include href="standard-options.xml" xpointer="no-pager" />
     <para>On success, 0 is returned, a non-zero failure code otherwise.</para>
   </refsect1>
 
+  <refsect1>
+    <title>Example</title>
+
+    <example>
+      <title>Generate a configuration extension image</title>
+
+      <para>The following creates a configuration extension DDI (confext) for an
+      <filename>/etc/motd</filename> update.</para>
+
+      <programlisting>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 &lt;&lt;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</programlisting>
+
+      <para>The DDI generated that way may be applied to the system with
+      <citerefentry><refentrytitle>systemd-confext</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
+    </example>
+
+  </refsect1>
+
   <refsect1>
     <title>See Also</title>
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>repart.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>portablectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index 1e59d353ee5128ff5ce01338170267f9743d66ac..88bc1367e05069847a243284206ed5cb7a6ff761 100644 (file)
@@ -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 (file)
index 0000000..e41dc05
--- /dev/null
@@ -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 (file)
index 0000000..437d88e
--- /dev/null
@@ -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 (file)
index 0000000..df16015
--- /dev/null
@@ -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 (file)
index 0000000..7875800
--- /dev/null
@@ -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 (file)
index 0000000..437d88e
--- /dev/null
@@ -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 (file)
index 0000000..df16015
--- /dev/null
@@ -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 (file)
index 0000000..41a7ca5
--- /dev/null
@@ -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 (file)
index 0000000..437d88e
--- /dev/null
@@ -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 (file)
index 0000000..df16015
--- /dev/null
@@ -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
index 19f49f606e5fcaf962ead8565fe18307c268e0b1..78cde2ff5281aa3e22fd695081620d3e944fc12c 100644 (file)
@@ -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
index 770f7c48ff9a5f92225d438a4126cbb6744261a3..855e908d7539b81a5adab4e84db16137028f0266 100644 (file)
@@ -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)