debug-generator: Add unit and drop-in credentials
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Sun, 31 Mar 2024 20:18:19 +0000 (22:18 +0200)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 4 Apr 2024 14:17:38 +0000 (16:17 +0200)
These allow adding extra units and drop-ins via credentials.

man/systemd-debug-generator.xml
man/systemd.system-credentials.xml
src/debug-generator/debug-generator.c
test/TEST-54-CREDS/test.sh
test/units/testsuite-54.sh

index 5d1d5b7e605086e27e0c36924aaf999095e4d861..af3615175e58b46ce4649e49322e2946b2850cb0 100644 (file)
@@ -6,7 +6,7 @@
 %entities;
 ]>
 <!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
-<refentry id="systemd-debug-generator">
+<refentry id="systemd-debug-generator" xmlns:xi="http://www.w3.org/2001/XInclude">
 
   <refentryinfo>
     <title>systemd-debug-generator</title>
     <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
   </refsect1>
 
+  <refsect1>
+    <title>System Credentials</title>
+
+    <variablelist class='system-credentials'>
+      <varlistentry>
+        <term><varname>systemd.extra-unit.*</varname></term>
+
+        <listitem><para>Credentials prefixed with <literal>systemd.extra-unit.</literal> specify additional
+        units to add to the final system. Note that these additional units are added to both the initrd and
+        the final system. <varname>ConditionPathExists=!/etc/initrd-release</varname> can be used to make
+        sure the unit is conditioned out in the initrd.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>systemd.unit-dropin.*</varname></term>
+
+        <listitem><para>Credentials prefixed with <literal>systemd.unit-dropin.</literal> add drop-ins for
+        the corresponding units in the final system. Each credential must be suffixed with the full unit name
+        including the unit extension. Its contents must be a valid unit drop-in file. Only one drop-in per
+        unit can be specified. The name of the generated drop-in will be
+        <literal>50-credential.conf</literal>. Note that these additional drop-ins are added to both the
+        initrd and the final system.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>See Also</title>
     <para><simplelist type="inline">
index 2a345c47b4c26c159ba5ca916ecb53b9f57b3119..b0fb70c6c5f930801d9e67d5be70c9123518d7f6 100644 (file)
           <xi:include href="version-info.xml" xpointer="v256"/>
         </listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>systemd.extra-unit.*</varname></term>
+        <term><varname>systemd.unit-dropin.*</varname></term>
+
+        <listitem><para>These credentials specify extra units and drop-ins to add to the system. For details
+        see <citerefentry><refentrytitle>systemd-debug-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
index ed3dc20f7212860e864709b0b93fae077bf2f0c9..3526b84dee8ed181ee64eb56bb6136aafba54e61 100644 (file)
@@ -3,12 +3,17 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
+#include "creds-util.h"
 #include "dropin.h"
+#include "errno-util.h"
+#include "fd-util.h"
+#include "fileio-label.h"
 #include "generator.h"
 #include "initrd-util.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "proc-cmdline.h"
+#include "recurse-dir.h"
 #include "special.h"
 #include "string-util.h"
 #include "strv.h"
@@ -158,8 +163,74 @@ static void install_debug_shell_dropin(void) {
                 log_warning_errno(r, "Failed to write drop-in for debug-shell.service, ignoring: %m");
 }
 
+static int process_unit_credentials(const char *credentials_dir) {
+        int r;
+
+        assert(credentials_dir);
+
+        _cleanup_free_ DirectoryEntries *des = NULL;
+        r = readdir_all_at(AT_FDCWD, credentials_dir, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, &des);
+        if (r < 0)
+                return log_error_errno(r, "Failed to enumerate credentials from credentials directory '%s': %m", credentials_dir);
+
+        FOREACH_ARRAY(i, des->entries, des->n_entries) {
+                _cleanup_free_ void *d = NULL;
+                struct dirent *de = *i;
+                const char *unit, *dropin;
+
+                if (de->d_type != DT_REG)
+                        continue;
+
+                unit = startswith(de->d_name, "systemd.extra-unit.");
+                dropin = startswith(de->d_name, "systemd.unit-dropin.");
+
+                if (!unit && !dropin)
+                        continue;
+
+                if (!unit_name_is_valid(unit ?: dropin, UNIT_NAME_ANY)) {
+                        log_warning("Invalid unit name '%s' in credential '%s', ignoring.",
+                                    unit ?: dropin, de->d_name);
+                        continue;
+                }
+
+                r = read_credential_with_decryption(de->d_name, &d, NULL);
+                if (r < 0)
+                        continue;
+
+                if (unit) {
+                        _cleanup_free_ char *p = NULL;
+
+                        p = path_join(arg_dest, unit);
+                        if (!p)
+                                return log_oom();
+
+                        r = write_string_file_atomic_label(p, d);
+                        if (r < 0) {
+                                log_warning_errno(r, "Failed to write unit file '%s' from credential '%s', ignoring: %m",
+                                                  unit, de->d_name);
+                                continue;
+                        }
+
+                        log_debug("Wrote unit file '%s' from credential '%s'", unit, de->d_name);
+
+                } else {
+                        r = write_drop_in(arg_dest, dropin, 50, "credential", d);
+                        if (r < 0) {
+                                log_warning_errno(r, "Failed to write drop-in for unit '%s' from credential '%s', ignoring: %m",
+                                                  dropin, de->d_name);
+                                continue;
+                        }
+
+                        log_debug("Wrote drop-in for unit '%s' from credential '%s'", dropin, de->d_name);
+                }
+        }
+
+        return 0;
+}
+
 static int run(const char *dest, const char *dest_early, const char *dest_late) {
-        int r, q;
+        const char *credentials_dir;
+        int r = 0;
 
         assert_se(arg_dest = dest_early);
 
@@ -175,10 +246,16 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
                 install_debug_shell_dropin();
         }
 
-        r = generate_mask_symlinks();
-        q = generate_wants_symlinks();
+        if (get_credentials_dir(&credentials_dir) >= 0)
+                RET_GATHER(r, process_unit_credentials(credentials_dir));
+
+        if (get_encrypted_credentials_dir(&credentials_dir) >= 0)
+                RET_GATHER(r, process_unit_credentials(credentials_dir));
 
-        return r < 0 ? r : q;
+        RET_GATHER(r, generate_mask_symlinks());
+        RET_GATHER(r, generate_wants_symlinks());
+
+        return r;
 }
 
 DEFINE_MAIN_GENERATOR_FUNCTION(run);
index c0a9d7a53d506cb33ecdaa1c5a3a267130799990..afcb348166865a1ff2ad74bb1c4539057e64a397 100755 (executable)
@@ -9,6 +9,19 @@ NSPAWN_CREDS=(
 )
 NSPAWN_ARGUMENTS="${NSPAWN_ARGUMENTS:-} ${NSPAWN_CREDS[*]}"
 
+UNIT_CRED=$(base64 -w 0 <<EOF
+[Service]
+Type=oneshot
+ExecStart=touch /tmp/unit-cred
+EOF
+)
+DROPIN_CRED=$(base64 -w 0 <<EOF
+[Service]
+ExecStart=
+ExecStart=touch /tmp/unit-dropin
+EOF
+)
+
 QEMU_CREDS=(
     "-fw_cfg name=opt/io.systemd.credentials/myqemucredential,string=othervalue"
     "-smbios type=11,value=io.systemd.credential:smbioscredential=magicdata"
@@ -17,6 +30,8 @@ QEMU_CREDS=(
     "-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"
+    "-smbios type=11,value=io.systemd.credential.binary:systemd.extra-unit.my-service.service=$UNIT_CRED"
+    "-smbios type=11,value=io.systemd.credential.binary:systemd.unit-dropin.my-service.service=$DROPIN_CRED"
 )
 QEMU_OPTIONS="${QEMU_OPTIONS:-} ${QEMU_CREDS[*]}"
 
index 7618e92c9fa4ffa2cdae096256187f688fae037c..3fa8822867d2c1687c78cb020aae1aeb8ec9970c 100755 (executable)
@@ -200,6 +200,10 @@ elif [ -d /sys/firmware/qemu_fw_cfg/by_name ]; then
     [ "$(cat /tmp/sourcedfromcredential)" = "tmpfilessecret" ]
     [ "$(cat /etc/motd.d/50-provision.conf)" = "hello" ]
     [ "$(cat /etc/issue.d/50-provision.conf)" = "welcome" ]
+
+    # Verify that adding a unit and drop-in via credentials worked
+    systemctl start my-service
+    test -f /tmp/unit-dropin
 else
     echo "qemu_fw_cfg support missing in kernel. Sniff!"
     expected_credential=""