attestation environments which use hardware CC registers and not the
TPM quote.
+ * By default networking to cloud IMDS services is now locked down, for
+ recognized clouds. This is recommended for secure installations, but
+ typically conflicts with traditional IMDS clients such as cloud-init,
+ which require direct IMDS access currently. The new meson option
+ "imds-network" can be used to change the default networking mode to
+ "unlocked" at build-time, for compatibility. This is probably what
+ general purpose distributions should set for now.
+
CHANGES WITH 260:
Feature Removals and Incompatible Changes:
['systemd-hostnamed.service', '8', ['systemd-hostnamed'], 'ENABLE_HOSTNAMED'],
['systemd-hwdb', '8', [], 'ENABLE_HWDB'],
['systemd-id128', '1', [], ''],
+ ['systemd-imds-generator', '8', [], 'ENABLE_IMDS'],
['systemd-imds', '1', ['systemd-imds-import.service'], 'ENABLE_IMDS'],
['systemd-imdsd@.service',
'8',
--- /dev/null
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="systemd-imds-generator" conditional='ENABLE_IMDS'
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>systemd-imds-generator</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-imds-generator</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-imds-generator</refname>
+ <refpurpose>Generator to automatically enable IMDS on supporting environments</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><filename>/usr/lib/systemd/system-generators/systemd-imds-generator</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><command>systemd-imds-generator</command> is a generator that enables IMDS (Instance Metadata
+ Service) functionality at boot on systems that support it. Specifically it does three things:</para>
+
+ <itemizedlist>
+ <listitem><para>It pulls the <filename>systemd-imdsd.socket</filename> unit (which activates
+ <citerefentry><refentrytitle>systemd-imdsd@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>)
+ into the initial transaction, which provides IMDS access to local applications via Varlink
+ IPC.</para></listitem>
+
+ <listitem><para>It pulls the <filename>systemd-imds-early-network.service</filename> unit into the
+ initial transaction, which generates a suitable
+ <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ network configuration file that allows early-boot network access to the IMDS
+ functionality.</para></listitem>
+
+ <listitem><para>It pulls the <filename>systemd-imds-import.service</filename> unit into the initial
+ transaction, which automatically imports various credentials from IMDS into the local system, storing
+ them in <filename>/run/credstore/</filename>.</para></listitem>
+ </itemizedlist>
+
+ <para>By default, whether to pull in these services or not is decided based on
+ <citerefentry><refentrytitle>hwdb</refentrytitle><manvolnum>7</manvolnum></citerefentry> information,
+ that detects various IMDS environments automatically. However, this logic may be overridden via
+ <varname>systemd.imds=</varname>, see below.</para>
+
+ <para><command>systemd-imds-generator</command> implements
+ <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Kernel Command Line</title>
+
+ <para><command>systemd-imds-generator</command> understands the following kernel command line
+ parameters:</para>
+
+ <variablelist class='kernel-commandline-options'>
+
+ <varlistentry>
+ <term><varname>systemd.imds=</varname></term>
+ <listitem>
+ <para>Takes a boolean argument or the special value <literal>auto</literal>, and may be used to
+ enable or disable the IMDS logic. Note that this controls only whether the relevant services (as
+ listed above) are automatically pulled into the initial transaction, it has no effect if some other
+ unit or the user explicitly activate the relevant units. If this option is not used (or set to
+ <literal>auto</literal>) automatic detection of IMDS is used, see above.</para>
+
+ <xi:include href="version-info.xml" xpointer="v261"/>
+ </listitem>
+ </varlistentry>
+
+ <xi:include href="systemd-imdsd@.service.xml" xpointer="kernel-cmdline-imds-network"/>
+
+ <varlistentry>
+ <term><varname>systemd.imds.import=</varname></term>
+ <listitem>
+ <para>Takes a boolean argument. If false the <filename>systemd-imds-import.service</filename> (see
+ above) is not pulled into the initial transaction, i.e. no credentials are imported from
+ IMDS. Defaults to true.</para>
+
+ <xi:include href="version-info.xml" xpointer="v261"/>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para><simplelist type="inline">
+ <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>systemd-imds</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>systemd-imdsd@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>systemd.system-credentials</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
+ </simplelist></para>
+ </refsect1>
+
+</refentry>
conf.get('HAVE_LIBCURL') == 1,
error_message : 'curl required').allowed()
conf.set10('ENABLE_IMDS', have)
+conf.set10('IMDS_NETWORK_LOCKED_DEFAULT', get_option('imds-network') == 'locked')
have = get_option('importd').require(
conf.get('HAVE_LIBCURL') == 1 and
'default user $PATH' : default_user_path != '' ? default_user_path : '(same as system services)',
'systemd service watchdog' : service_watchdog == '' ? 'disabled' : service_watchdog,
'time epoch' : f'@time_epoch@ (@alt_time_epoch@)',
- 'TPM2 nvpcr base' : run_command(sh, '-c', 'printf 0x%x @0@'.format(get_option('tpm2-nvpcr-base')), check : true).stdout()
+ 'TPM2 nvpcr base' : run_command(sh, '-c', 'printf 0x%x @0@'.format(get_option('tpm2-nvpcr-base')), check : true).stdout(),
+ 'IMDS networking' : get_option('imds-network'),
})
# TODO:
description : 'install the systemd-timesyncd daemon')
option('imds', type : 'feature',
description : 'install the systemd-imds stack')
+option('imds-network', type : 'combo', choices : [ 'locked', 'unlocked' ],
+ description : 'whether to default to locked/unlocked IMDS network mode')
option('journal-storage-default', type : 'combo', choices : ['persistent', 'auto', 'volatile', 'none'],
description : 'default storage mode for journald (main namespace)')
option('remote', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "sd-hwdb.h"
+
+#include "dropin.h"
+#include "fileio.h"
+#include "generator.h"
+#include "imds-util.h"
+#include "log.h"
+#include "parse-util.h"
+#include "proc-cmdline.h"
+#include "special.h"
+#include "string-util.h"
+#include "virt.h"
+
+static int arg_enabled = -1; /* Whether we shall offer local IMDS APIs */
+static bool arg_import = true; /* Whether we shall import IMDS credentials, SSH keys, … into the local system */
+static ImdsNetworkMode arg_network_mode =
+ IMDS_NETWORK_LOCKED_DEFAULT ? IMDS_NETWORK_LOCKED : IMDS_NETWORK_UNLOCKED;
+
+static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
+ int r;
+
+ assert(key);
+
+ if (proc_cmdline_key_streq(key, "systemd.imds")) {
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ r = parse_tristate_full(value, "auto", &arg_enabled);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to parse systemd.imds= value: %m");
+
+ } else if (proc_cmdline_key_streq(key, "systemd.imds.import")) {
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ r = parse_boolean(value);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to parse systemd.imds.import= value: %m");
+
+ arg_import = r;
+ } else if (proc_cmdline_key_streq(key, "systemd.imds.network")) {
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ ImdsNetworkMode m = imds_network_mode_from_string(value);
+ if (m < 0)
+ return log_warning_errno(m, "Failed to parse systemd.imds.network= value: %m");
+
+ arg_network_mode = m;
+ }
+
+ return 0;
+}
+
+static int smbios_get_modalias(char **ret) {
+ int r;
+
+ assert(ret);
+
+ _cleanup_free_ char *modalias = NULL;
+ r = read_virtual_file("/sys/devices/virtual/dmi/id/modalias", SIZE_MAX, &modalias, /* ret_size= */ NULL);
+ if (r < 0)
+ return r;
+
+ truncate_nl(modalias);
+
+ /* To detect Azure we need to check the chassis asset tag. Unfortunately the kernel does not include
+ * it in the modalias string right now. Let's hence append it manually. This matches similar logic in
+ * rules.d/60-dmi-id.rules. */
+ _cleanup_free_ char *cat = NULL;
+ r = read_virtual_file("/sys/devices/virtual/dmi/id/chassis_asset_tag", SIZE_MAX, &cat, /* ret_size= */ NULL);
+ if (r < 0)
+ log_debug_errno(r, "Failed to read chassis asset tag, ignoring: %m");
+ else {
+ truncate_nl(cat);
+
+ if (!string_has_cc(cat, /* ok= */ NULL) && !isempty(cat) && !strextend(&modalias, "cat", cat, ":"))
+ return -ENOMEM;
+ }
+
+ log_debug("Constructed SMBIOS modalias string: %s", modalias);
+ *ret = TAKE_PTR(modalias);
+ return 0;
+}
+
+static int smbios_query(void) {
+ int r;
+
+ /* Let's check whether the DMI device's hwdb data suggests IMDS support is available. Note, we cannot
+ * ask udev for this, as we typically run long before udev. Hence we'll do the hwdb lookup via
+ * sd-hwdb directly. */
+
+ _cleanup_free_ char *modalias = NULL;
+ r = smbios_get_modalias(&modalias);
+ if (r == -ENOENT) {
+ log_debug("No DMI device found, assuming IMDS is not available.");
+ return false;
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to read DMI modalias: %m");
+
+ _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL;
+ r = sd_hwdb_new(&hwdb);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open hwdb: %m");
+
+ r = sd_hwdb_seek(hwdb, modalias);
+ if (r < 0)
+ return log_error_errno(r, "Failed to seek in hwdb for '%s': %m", modalias);
+
+ for (;;) {
+ const char *key, *value;
+ r = sd_hwdb_enumerate(hwdb, &key, &value);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enumerate hwdb entry for '%s': %m", modalias);
+ if (r == 0)
+ break;
+
+ if (streq(key, "IMDS_VENDOR"))
+ return true;
+ }
+
+ log_debug("IMDS_VENDOR= property for DMI device not set, assuming IMDS is not available.");
+ return false;
+}
+
+static int run(const char *dest, const char *dest_early, const char *dest_late) {
+ int r;
+
+ r = proc_cmdline_parse(parse_proc_cmdline_item, /* userdata= */ NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
+
+ if (arg_enabled < 0) {
+ Virtualization v = detect_container();
+ if (v < 0)
+ log_debug_errno(v, "Container detection failed, ignoring: %m");
+ if (v > 0) {
+ log_debug("Running in a container, disabling IMDS logic.");
+ arg_enabled = false;
+ } else {
+ r = smbios_query();
+ if (r < 0)
+ return r;
+ arg_enabled = r > 0;
+ }
+ }
+
+ if (!arg_enabled) {
+ log_debug("IMDS not enabled, skipping generator.");
+ return 0;
+ }
+
+ log_info("IMDS support enabled, pull in IMDS units.");
+
+ /* Enable IMDS early networking, so that we can actually reach the IMDS server. */
+ if (arg_network_mode != IMDS_NETWORK_OFF) {
+ r = generator_add_symlink(dest_early, SPECIAL_SYSINIT_TARGET, "wants", SYSTEM_DATA_UNIT_DIR "/systemd-imds-early-network.service");
+ if (r < 0)
+ return log_error_errno(r, "Failed to hook in systemd-imds-early-network.service: %m");
+ }
+
+ /* Enable the IMDS service socket */
+ r = generator_add_symlink(dest_early, SPECIAL_SOCKETS_TARGET, "wants", SYSTEM_DATA_UNIT_DIR "/systemd-imdsd.socket");
+ if (r < 0)
+ return log_error_errno(r, "Failed to hook in systemd-imdsd.socket: %m");
+
+ /* We now know the SMBIOS device exists, hence it's safe now to order the IMDS service after it, so
+ * that it has all properties properly initialized. */
+ r = write_drop_in(
+ dest_early,
+ "systemd-imdsd@.service",
+ 50, "dmi-id",
+ "# Automatically generated by systemd-imds-generator\n\n"
+ "[Unit]\n"
+ "Wants=sys-devices-virtual-dmi-id.device\n"
+ "After=sys-devices-virtual-dmi-id.device\n");
+ if (r < 0)
+ return log_error_errno(r, "Failed to hook DMI id device before systemd-imdsd@.service: %m");
+
+ if (arg_import) {
+ /* Enable that we import IMDS data */
+ r = generator_add_symlink(dest_early, SPECIAL_SYSINIT_TARGET, "wants", SYSTEM_DATA_UNIT_DIR "/systemd-imds-import.service");
+ if (r < 0)
+ return log_error_errno(r, "Failed to hook in systemd-imds-import.service: %m");
+ }
+
+ return 0;
+}
+
+DEFINE_MAIN_GENERATOR_FUNCTION(run);
'imds-util.c'
),
},
+ generator_template + {
+ 'name' : 'systemd-imds-generator',
+ 'sources' : files(
+ 'imds-generator.c',
+ 'imds-util.c'
+ ),
+ },
]
install_data(