mirror of
https://github.com/systemd/systemd.git
synced 2026-08-12 22:17:19 +00:00
imds: add generator that hooks in IMDS logic on cloud guests
The infrastructure added in the previous commits added support for IMDS client functionality, but didn't really to enable the logic by default on suitable hosts. This commit adds a generator that automatically hooks the IMDS functionality into the boot process if it detects that the system is running on a compliant cloud system. it enables both the imds daemon and the client.
This commit is contained in:
8
NEWS
8
NEWS
@@ -13,6 +13,14 @@ CHANGES WITH 261 in spe:
|
||||
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:
|
||||
|
||||
@@ -1045,6 +1045,7 @@ manpages = [
|
||||
['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',
|
||||
|
||||
107
man/systemd-imds-generator.xml
Normal file
107
man/systemd-imds-generator.xml
Normal file
@@ -0,0 +1,107 @@
|
||||
<?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>
|
||||
@@ -1544,6 +1544,7 @@ have = get_option('imds').require(
|
||||
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
|
||||
@@ -3087,7 +3088,8 @@ summary({
|
||||
'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:
|
||||
|
||||
@@ -144,6 +144,8 @@ option('timesyncd', type : 'boolean',
|
||||
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' },
|
||||
|
||||
193
src/imds/imds-generator.c
Normal file
193
src/imds/imds-generator.c
Normal file
@@ -0,0 +1,193 @@
|
||||
/* 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);
|
||||
@@ -22,6 +22,13 @@ executables += [
|
||||
'imds-util.c'
|
||||
),
|
||||
},
|
||||
generator_template + {
|
||||
'name' : 'systemd-imds-generator',
|
||||
'sources' : files(
|
||||
'imds-generator.c',
|
||||
'imds-util.c'
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
install_data(
|
||||
|
||||
Reference in New Issue
Block a user