imds: add new systemd-imdsd.service that makes IMDS data accessible locally
This service's job is to talk to a VM associated IMDS service provided by the local Cloud. It tries to abstract the protocol differences various IMDS implementations implement, but does *not* really try to abstract more than a few basic fields of the actual IMDS metadata. IMDS access is wrapped in a Varlink API that local clients can talk to. If possible this makes use of the IMDS endpoint information that has been added to hwdb in the preceeding commit. However, endpoint info can also be provided via kernel command line and credentials. For debugging purposes we also accept them via environment variables and command line arguments. This adds a concept of early-boot networking, just enough to be able to talk to the IMDS service. It is minimally configurable via a kernel cmdline option (and a build-time option): the user may choose between "locked" and "unlocked" mode. In the former mode direct access to IMDS via HTTPS is blocked via a prohibit route (and thus all IMDS communication has to be done via systemd-imdsd@.service). In the latter case no such lockdown takes place, and IMDS may be acquired both via this new service and directly. The latter is typically a good idea for compatibility with current systems, the former is preferable for secure installations. Access to IMDS fields is controlled via PK.
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "imds-util.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "utf8.h"
|
||||
|
||||
bool imds_key_is_valid(const char *key) {
|
||||
/* Just some pretty superficial validation. */
|
||||
|
||||
if (!key)
|
||||
return false;
|
||||
|
||||
if (!startswith(key, "/"))
|
||||
return false;
|
||||
|
||||
if (!ascii_is_valid(key))
|
||||
return false;
|
||||
|
||||
if (string_has_cc(key, /* ok= */ NULL))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char* const imds_well_known_table[_IMDS_WELL_KNOWN_MAX] = {
|
||||
[IMDS_BASE] = "base",
|
||||
[IMDS_HOSTNAME] = "hostname",
|
||||
[IMDS_REGION] = "region",
|
||||
[IMDS_ZONE] = "zone",
|
||||
[IMDS_IPV4_PUBLIC] = "ipv4-public",
|
||||
[IMDS_IPV6_PUBLIC] = "ipv6-public",
|
||||
[IMDS_SSH_KEY] = "ssh-key",
|
||||
[IMDS_USERDATA] = "userdata",
|
||||
[IMDS_USERDATA_BASE] = "userdata-base",
|
||||
[IMDS_USERDATA_BASE64] = "userdata-base64",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(imds_well_known, ImdsWellKnown);
|
||||
|
||||
|
||||
static const char* const imds_network_mode_table[_IMDS_NETWORK_MODE_MAX] = {
|
||||
[IMDS_NETWORK_OFF] = "off",
|
||||
[IMDS_NETWORK_LOCKED] = "locked",
|
||||
[IMDS_NETWORK_UNLOCKED] = "unlocked",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(imds_network_mode, ImdsNetworkMode, IMDS_NETWORK_LOCKED);
|
||||
@@ -0,0 +1,38 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "macro.h"
|
||||
#include "string-table.h" /* IWYU pragma: keep */
|
||||
|
||||
typedef enum ImdsNetworkMode {
|
||||
IMDS_NETWORK_OFF, /* No automatic pre-IMDS network configuration, something else has to do this. (Also: no "prohibit" route) */
|
||||
IMDS_NETWORK_LOCKED, /* "Prohibit" route for the IMDS server, unless you have SO_MARK set to 0x7FFF0815 */
|
||||
IMDS_NETWORK_UNLOCKED, /* No "prohibit" route for the IMDS server */
|
||||
_IMDS_NETWORK_MODE_MAX,
|
||||
_IMDS_NETWORK_MODE_INVALID = -EINVAL,
|
||||
} ImdsNetworkMode;
|
||||
|
||||
/* Various well-known keys */
|
||||
typedef enum ImdsWellKnown {
|
||||
IMDS_BASE, /* The same as "/", typically suffixed */
|
||||
IMDS_HOSTNAME,
|
||||
IMDS_REGION,
|
||||
IMDS_ZONE,
|
||||
IMDS_IPV4_PUBLIC,
|
||||
IMDS_IPV6_PUBLIC,
|
||||
IMDS_SSH_KEY,
|
||||
IMDS_USERDATA,
|
||||
IMDS_USERDATA_BASE, /* typically suffixed */
|
||||
IMDS_USERDATA_BASE64,
|
||||
_IMDS_WELL_KNOWN_MAX,
|
||||
_IMDS_WELL_KNOWN_INVALID = -EINVAL,
|
||||
} ImdsWellKnown;
|
||||
|
||||
static inline bool imds_well_known_can_suffix(ImdsWellKnown wk) {
|
||||
return IN_SET(wk, IMDS_BASE, IMDS_USERDATA_BASE);
|
||||
}
|
||||
|
||||
bool imds_key_is_valid(const char *key);
|
||||
|
||||
DECLARE_STRING_TABLE_LOOKUP(imds_well_known, ImdsWellKnown);
|
||||
DECLARE_STRING_TABLE_LOOKUP(imds_network_mode, ImdsNetworkMode);
|
||||
+3163
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*-->
|
||||
<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
|
||||
"https://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
|
||||
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<policyconfig>
|
||||
|
||||
<vendor>The systemd Project</vendor>
|
||||
<vendor_url>https://systemd.io</vendor_url>
|
||||
|
||||
<action id="io.systemd.imds.get">
|
||||
<description gettext-domain="systemd">Acquire IMDS instance metadata.</description>
|
||||
<message gettext-domain="systemd">Authentication is required for an application to acquire IMDS instance metadata.</message>
|
||||
<defaults>
|
||||
<allow_any>auth_admin_keep</allow_any>
|
||||
<allow_inactive>auth_admin_keep</allow_inactive>
|
||||
<allow_active>auth_admin_keep</allow_active>
|
||||
</defaults>
|
||||
</action>
|
||||
</policyconfig>
|
||||
@@ -0,0 +1,21 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
if conf.get('ENABLE_IMDS') != 1
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
executables += [
|
||||
libexec_template + {
|
||||
'name' : 'systemd-imdsd',
|
||||
'public' : true,
|
||||
'sources' : files(
|
||||
'imdsd.c',
|
||||
'imds-util.c'
|
||||
) + import_curl_util_c,
|
||||
'dependencies' : [ libcurl ],
|
||||
},
|
||||
]
|
||||
|
||||
install_data(
|
||||
'io.systemd.imds.policy',
|
||||
install_dir : polkitpolicydir)
|
||||
@@ -1,5 +1,7 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
import_curl_util_c = files('curl-util.c')
|
||||
|
||||
if conf.get('ENABLE_IMPORTD') != 1
|
||||
subdir_done()
|
||||
endif
|
||||
@@ -32,14 +34,13 @@ executables += [
|
||||
'name' : 'systemd-pull',
|
||||
'public' : true,
|
||||
'sources' : files(
|
||||
'curl-util.c',
|
||||
'pull.c',
|
||||
'pull-common.c',
|
||||
'pull-job.c',
|
||||
'pull-oci.c',
|
||||
'pull-raw.c',
|
||||
'pull-tar.c',
|
||||
),
|
||||
) + import_curl_util_c,
|
||||
'objects' : ['systemd-importd'],
|
||||
'dependencies' : common_deps + [
|
||||
libopenssl,
|
||||
|
||||
@@ -207,6 +207,7 @@ shared_sources = files(
|
||||
'varlink-io.systemd.FactoryReset.c',
|
||||
'varlink-io.systemd.Hostname.c',
|
||||
'varlink-io.systemd.Import.c',
|
||||
'varlink-io.systemd.InstanceMetadata.c',
|
||||
'varlink-io.systemd.Journal.c',
|
||||
'varlink-io.systemd.JournalAccess.c',
|
||||
'varlink-io.systemd.Login.c',
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "bus-polkit.h"
|
||||
#include "varlink-io.systemd.InstanceMetadata.h"
|
||||
|
||||
static SD_VARLINK_DEFINE_ENUM_TYPE(
|
||||
WellKnown,
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(base),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(hostname),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(region),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(zone),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(ipv4_public),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(ipv6_public),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(ssh_key),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(userdata),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(userdata_base),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(userdata_base64));
|
||||
|
||||
static SD_VARLINK_DEFINE_METHOD(
|
||||
Get,
|
||||
SD_VARLINK_FIELD_COMMENT("The key to retrieve"),
|
||||
SD_VARLINK_DEFINE_INPUT(key, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Start with a well-known key"),
|
||||
SD_VARLINK_DEFINE_INPUT_BY_TYPE(wellKnown, WellKnown, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("The network interface to use"),
|
||||
SD_VARLINK_DEFINE_INPUT(interface, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Refresh cached data if older (CLOCK_BOOTTIME, µs)"),
|
||||
SD_VARLINK_DEFINE_INPUT(refreshUSec, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Whether to accept cached data"),
|
||||
SD_VARLINK_DEFINE_INPUT(cache, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("The firewall mark value to use"),
|
||||
SD_VARLINK_DEFINE_INPUT(firewallMark, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Controls whether to wait for connectivity"),
|
||||
SD_VARLINK_DEFINE_INPUT(wait, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_POLKIT_INPUT,
|
||||
SD_VARLINK_FIELD_COMMENT("The data in Base64 encoding."),
|
||||
SD_VARLINK_DEFINE_OUTPUT(data, SD_VARLINK_STRING, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("The interface the data was found on."),
|
||||
SD_VARLINK_DEFINE_OUTPUT(interface, SD_VARLINK_INT, SD_VARLINK_NULLABLE));
|
||||
|
||||
static SD_VARLINK_DEFINE_METHOD(
|
||||
GetVendorInfo,
|
||||
SD_VARLINK_FIELD_COMMENT("The detected cloud vendor"),
|
||||
SD_VARLINK_DEFINE_OUTPUT(vendor, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("The URL to acquire the token from"),
|
||||
SD_VARLINK_DEFINE_OUTPUT(tokenUrl, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("The HTTP header to configure the refresh timeout for the token in"),
|
||||
SD_VARLINK_DEFINE_OUTPUT(refreshHeaderName, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("The base URL to acquire the data from"),
|
||||
SD_VARLINK_DEFINE_OUTPUT(dataUrl, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("A suffix to append to the data URL"),
|
||||
SD_VARLINK_DEFINE_OUTPUT(dataUrlSuffix, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("The HTTP header to pass the token in when requesting data"),
|
||||
SD_VARLINK_DEFINE_OUTPUT(tokenHeaderName, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Additional HTTP headers to pass when acquiring data"),
|
||||
SD_VARLINK_DEFINE_OUTPUT(extraHeader, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY),
|
||||
SD_VARLINK_FIELD_COMMENT("IPv4 address of IMDS server"),
|
||||
SD_VARLINK_DEFINE_OUTPUT(addressIPv4, SD_VARLINK_INT, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY),
|
||||
SD_VARLINK_FIELD_COMMENT("IPv6 address of IMDS server"),
|
||||
SD_VARLINK_DEFINE_OUTPUT(addressIPv6, SD_VARLINK_INT, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY),
|
||||
SD_VARLINK_FIELD_COMMENT("Well-known fields"),
|
||||
SD_VARLINK_DEFINE_OUTPUT(wellKnown, SD_VARLINK_OBJECT, SD_VARLINK_NULLABLE));
|
||||
|
||||
static SD_VARLINK_DEFINE_ERROR(
|
||||
KeyNotFound);
|
||||
|
||||
static SD_VARLINK_DEFINE_ERROR(
|
||||
WellKnownKeyUnset);
|
||||
|
||||
static SD_VARLINK_DEFINE_ERROR(
|
||||
NotAvailable);
|
||||
|
||||
static SD_VARLINK_DEFINE_ERROR(
|
||||
NotSupported);
|
||||
|
||||
static SD_VARLINK_DEFINE_ERROR(
|
||||
CommunicationFailure);
|
||||
|
||||
static SD_VARLINK_DEFINE_ERROR(
|
||||
Timeout);
|
||||
|
||||
SD_VARLINK_DEFINE_INTERFACE(
|
||||
io_systemd_InstanceMetadata,
|
||||
"io.systemd.InstanceMetadata",
|
||||
SD_VARLINK_INTERFACE_COMMENT("APIs for acquiring cloud IMDS information."),
|
||||
SD_VARLINK_SYMBOL_COMMENT("Well known data fields"),
|
||||
&vl_type_WellKnown,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Acquire data."),
|
||||
&vl_method_Get,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Get information about cloud vendor and IMDS connectivity."),
|
||||
&vl_method_GetVendorInfo,
|
||||
SD_VARLINK_SYMBOL_COMMENT("The requested key is not found on the IMDS server."),
|
||||
&vl_error_KeyNotFound,
|
||||
SD_VARLINK_SYMBOL_COMMENT("IMDS is disabled or otherwise not available."),
|
||||
&vl_error_NotAvailable,
|
||||
SD_VARLINK_SYMBOL_COMMENT("IMDS is not supported."),
|
||||
&vl_error_NotSupported,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Well-known key is not set."),
|
||||
&vl_error_WellKnownKeyUnset,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Communication with IMDS failed."),
|
||||
&vl_error_CommunicationFailure,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Timeout reached"),
|
||||
&vl_error_Timeout);
|
||||
@@ -0,0 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "sd-varlink-idl.h"
|
||||
|
||||
extern const sd_varlink_interface vl_interface_io_systemd_InstanceMetadata;
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "varlink-io.systemd.FactoryReset.h"
|
||||
#include "varlink-io.systemd.Hostname.h"
|
||||
#include "varlink-io.systemd.Import.h"
|
||||
#include "varlink-io.systemd.InstanceMetadata.h"
|
||||
#include "varlink-io.systemd.Journal.h"
|
||||
#include "varlink-io.systemd.JournalAccess.h"
|
||||
#include "varlink-io.systemd.Login.h"
|
||||
@@ -190,6 +191,7 @@ TEST(parse_format) {
|
||||
&vl_interface_io_systemd_FactoryReset,
|
||||
&vl_interface_io_systemd_Hostname,
|
||||
&vl_interface_io_systemd_Import,
|
||||
&vl_interface_io_systemd_InstanceMetadata,
|
||||
&vl_interface_io_systemd_Journal,
|
||||
&vl_interface_io_systemd_JournalAccess,
|
||||
&vl_interface_io_systemd_Login,
|
||||
|
||||
Reference in New Issue
Block a user