report: add systemd-report-sign-tsm backend
A report signing backend that returns a confidential-computing attestation report obtained via configfs-tsm. Implements io.systemd.Report.Signer.Sign(): embeds the digest as the report's inblob and returns the outblob (plus provider and any aux/manifest blobs). Wired up as the "tsm" mechanism with a socket-activated service. Signed-off-by: Paul Meyer <katexochen0@gmail.com>
This commit is contained in:
@@ -1196,6 +1196,11 @@ manpages = [
|
||||
['systemd-report-sign-plain',
|
||||
'systemd-report-sign-plain.socket'],
|
||||
'HAVE_OPENSSL'],
|
||||
['systemd-report-sign-tsm@.service',
|
||||
'8',
|
||||
['systemd-report-sign-tsm',
|
||||
'systemd-report-sign-tsm.socket'],
|
||||
''],
|
||||
['systemd-resolved.service', '8', ['systemd-resolved'], 'ENABLE_RESOLVE'],
|
||||
['systemd-rfkill.service',
|
||||
'8',
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
<?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-report-sign-tsm_.service"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<refentryinfo>
|
||||
<title>systemd-report-sign-tsm@.service</title>
|
||||
<productname>systemd</productname>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>systemd-report-sign-tsm@.service</refentrytitle>
|
||||
<manvolnum>8</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>systemd-report-sign-tsm@.service</refname>
|
||||
<refname>systemd-report-sign-tsm.socket</refname>
|
||||
<refname>systemd-report-sign-tsm</refname>
|
||||
<refpurpose>Sign system reports with a confidential-computing attestation report</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<para><filename>systemd-report-sign-tsm@.service</filename></para>
|
||||
<para><filename>systemd-report-sign-tsm.socket</filename></para>
|
||||
<para><filename>/usr/lib/systemd/systemd-report-sign-tsm</filename></para>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><command>systemd-report-sign-tsm@.service</command> is a system service that signs system reports
|
||||
generated by
|
||||
<citerefentry><refentrytitle>systemd-report</refentrytitle><manvolnum>1</manvolnum></citerefentry>. It is
|
||||
a signing backend for the <option>--sign=</option> logic of that tool: it implements the
|
||||
<function>io.systemd.Report.Signer.Sign()</function> Varlink method and is reached via a socket linked into
|
||||
the <filename>/run/systemd/report.sign/</filename> directory, named <filename>tsm</filename>.</para>
|
||||
|
||||
<para>The service is socket-activated (one instance per connection) via
|
||||
<filename>systemd-report-sign-tsm.socket</filename>. Rather than signing with a local key, it obtains a
|
||||
hardware-backed attestation report from the platform's Trusted Security Module (TSM) through the kernel's
|
||||
configfs-TSM interface at <filename>/sys/kernel/config/tsm/report/</filename>. The digest passed to it is
|
||||
embedded into the report as its run-time provided data (<literal>inblob</literal>), cryptographically
|
||||
binding the attestation report to the system report being signed.</para>
|
||||
|
||||
<para>The returned signature carries the binary attestation report (the <literal>outblob</literal>), the
|
||||
name of the TSM provider that generated it (for example <literal>sev_guest</literal> for AMD SEV-SNP or
|
||||
<literal>tdx_guest</literal> for Intel TDX), and, where the provider supplies them, supplemental
|
||||
certificate (<literal>auxblob</literal>) and manifest (<literal>manifestblob</literal>) data. A verifier
|
||||
selects the appropriate validation logic based on the provider field.</para>
|
||||
|
||||
<para>This backend is only functional inside a confidential virtual machine whose kernel exposes the
|
||||
configfs-TSM interface (such as AMD SEV-SNP or Intel TDX guests). On systems where the interface is
|
||||
unavailable the signing operation is reported as unsupported and the mechanism is skipped. The service
|
||||
keeps no persistent state.</para>
|
||||
</refsect1>
|
||||
|
||||
<!-- Note: we do not document the command line switches here. The systemd-report-sign-tsm binary itself
|
||||
should not be invoked by users, but only as a service. Use systemd-report(1) to generate signed
|
||||
reports. -->
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para><simplelist type="inline">
|
||||
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-report</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
</simplelist></para>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
||||
@@ -35,6 +35,7 @@ enable systemd-report-basic.socket
|
||||
enable systemd-report-cgroup.socket
|
||||
enable systemd-report-files.socket
|
||||
enable systemd-report-sign-plain.socket
|
||||
enable systemd-report-sign-tsm.socket
|
||||
enable systemd-report.socket
|
||||
enable systemd-resolved.service
|
||||
enable systemd-sysext.service
|
||||
|
||||
@@ -44,4 +44,10 @@ executables += [
|
||||
),
|
||||
'dependencies' : libopenssl_cflags,
|
||||
},
|
||||
libexec_template + {
|
||||
'name' : 'systemd-report-sign-tsm',
|
||||
'sources' : files(
|
||||
'report-sign-tsm.c',
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "sd-json.h"
|
||||
#include "sd-varlink.h"
|
||||
|
||||
#include "build.h"
|
||||
#include "format-table.h"
|
||||
#include "help-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "json-util.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
#include "options.h"
|
||||
#include "sha256.h"
|
||||
#include "string-util.h"
|
||||
#include "tsm-report.h"
|
||||
#include "varlink-io.systemd.Report.Signer.h"
|
||||
#include "varlink-util.h"
|
||||
|
||||
typedef struct SignParameters {
|
||||
struct iovec digest;
|
||||
const char *algorithm;
|
||||
} SignParameters;
|
||||
|
||||
static void sign_parameters_done(SignParameters *p) {
|
||||
iovec_done(&p->digest);
|
||||
}
|
||||
|
||||
static int vl_method_sign(
|
||||
sd_varlink *link,
|
||||
sd_json_variant *parameters,
|
||||
sd_varlink_method_flags_t flags,
|
||||
void *userdata) {
|
||||
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "digest", SD_JSON_VARIANT_STRING, json_dispatch_unhex_iovec, offsetof(SignParameters, digest), SD_JSON_MANDATORY },
|
||||
{ "algorithm", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(SignParameters, algorithm), SD_JSON_MANDATORY },
|
||||
{}
|
||||
};
|
||||
|
||||
_cleanup_(sign_parameters_done) SignParameters sp = {};
|
||||
_cleanup_(tsm_report_freep) TsmReport *report = NULL;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(parameters);
|
||||
|
||||
r = sd_varlink_dispatch(link, parameters, dispatch_table, &sp);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
if (!streq(sp.algorithm, "SHA256"))
|
||||
return sd_varlink_error_invalid_parameter_name(link, "algorithm");
|
||||
if (sp.digest.iov_len != SHA256_DIGEST_SIZE)
|
||||
return sd_varlink_error_invalid_parameter_name(link, "digest");
|
||||
|
||||
uint8_t report_data[TSM_REPORT_DATA_SIZE] = {};
|
||||
memcpy(report_data, sp.digest.iov_base, sp.digest.iov_len);
|
||||
|
||||
r = tsm_report_acquire(&IOVEC_MAKE(report_data, sizeof report_data), /* options= */ NULL, &report);
|
||||
if (IN_SET(r, -EOPNOTSUPP, -ENXIO))
|
||||
/* Returning an error will fail the whole report signing, so we return
|
||||
* no signature instead which will be ignored by the aggregator. */
|
||||
return sd_varlink_replybo(link, SD_JSON_BUILD_PAIR_EMPTY_ARRAY("data"));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to acquire TSM report: %m");
|
||||
|
||||
return sd_varlink_replybo(
|
||||
link,
|
||||
SD_JSON_BUILD_PAIR("data",
|
||||
SD_JSON_BUILD_ARRAY(
|
||||
SD_JSON_BUILD_OBJECT(
|
||||
SD_JSON_BUILD_PAIR_STRING("provider", report->provider),
|
||||
JSON_BUILD_PAIR_IOVEC_BASE64("outblob", &report->outblob),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(iovec_is_set(&report->auxblob),
|
||||
"auxblob", JSON_BUILD_IOVEC_BASE64(&report->auxblob)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(iovec_is_set(&report->manifestblob),
|
||||
"manifestblob", JSON_BUILD_IOVEC_BASE64(&report->manifestblob))))));
|
||||
}
|
||||
|
||||
static int vl_server(void) {
|
||||
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *vs = NULL;
|
||||
int r;
|
||||
|
||||
r = varlink_server_new(&vs, SD_VARLINK_SERVER_ROOT_ONLY|SD_VARLINK_SERVER_MYSELF_ONLY, /* userdata= */ NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allocate Varlink server: %m");
|
||||
|
||||
r = sd_varlink_server_add_interface(vs, &vl_interface_io_systemd_Report_Signer);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add Varlink interface: %m");
|
||||
|
||||
r = sd_varlink_server_bind_method(vs, "io.systemd.Report.Signer.Sign", vl_method_sign);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to bind Varlink methods: %m");
|
||||
|
||||
r = sd_varlink_server_loop_auto(vs);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to run Varlink event loop: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_(table_unrefp) Table *options = NULL;
|
||||
int r;
|
||||
|
||||
r = option_parser_get_help_table(&options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_cmdline("[OPTIONS...]");
|
||||
help_abstract("Get an attestation report via configfs Trusted Security Module (TSM) "
|
||||
"that includes the hash of the system report.");
|
||||
help_section("Options");
|
||||
|
||||
r = table_print_or_warn(options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
help_man_page_reference("systemd-report-sign-tsm@.service", "8");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
int r;
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
OptionParser opts = { argc, argv };
|
||||
|
||||
FOREACH_OPTION_OR_RETURN(c, &opts)
|
||||
switch (c) {
|
||||
OPTION_COMMON_HELP:
|
||||
return help();
|
||||
OPTION_COMMON_VERSION:
|
||||
return version();
|
||||
}
|
||||
|
||||
if (option_parser_get_n_args(&opts) > 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"This program takes no arguments.");
|
||||
|
||||
r = sd_varlink_invocation(SD_VARLINK_ALLOW_ACCEPT);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
|
||||
if (r == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"This program can only run as a Varlink service.");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
int r;
|
||||
|
||||
log_setup();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
return vl_server();
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
||||
@@ -747,6 +747,8 @@ units = [
|
||||
'file' : 'systemd-report-sign-plain@.service.in',
|
||||
'conditions' : ['HAVE_OPENSSL'],
|
||||
},
|
||||
{ 'file' : 'systemd-report-sign-tsm.socket' },
|
||||
{ 'file' : 'systemd-report-sign-tsm@.service.in' },
|
||||
{
|
||||
'file' : 'systemd-resolved.service.in',
|
||||
'conditions' : ['ENABLE_RESOLVE'],
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# 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.
|
||||
|
||||
[Unit]
|
||||
Description=Report Signing Socket (configfs-tsm)
|
||||
Documentation=man:systemd-report-sign-tsm@.service(8)
|
||||
DefaultDependencies=no
|
||||
Before=sockets.target shutdown.target
|
||||
Conflicts=shutdown.target
|
||||
ConditionSecurity=cvm
|
||||
|
||||
[Socket]
|
||||
ListenStream=/run/systemd/report.sign/tsm
|
||||
FileDescriptorName=varlink
|
||||
SocketMode=0600
|
||||
Accept=yes
|
||||
MaxConnectionsPerSource=16
|
||||
RemoveOnStop=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
@@ -0,0 +1,46 @@
|
||||
# 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.
|
||||
|
||||
[Unit]
|
||||
Description=Report Signing Service (configfs-tsm)
|
||||
Documentation=man:systemd-report-sign-tsm@.service(8)
|
||||
DefaultDependencies=no
|
||||
Before=shutdown.target
|
||||
Conflicts=shutdown.target
|
||||
Wants=sys-kernel-config.mount
|
||||
After=sys-kernel-config.mount
|
||||
|
||||
[Service]
|
||||
ExecStart={{LIBEXECDIR}}/systemd-report-sign-tsm
|
||||
CapabilityBoundingSet=
|
||||
DeviceAllow=
|
||||
IPAddressDeny=any
|
||||
LockPersonality=yes
|
||||
MemoryDenyWriteExecute=yes
|
||||
NoNewPrivileges=yes
|
||||
PrivateDevices=yes
|
||||
PrivateIPC=yes
|
||||
PrivateNetwork=yes
|
||||
PrivateTmp=disconnected
|
||||
ProtectControlGroups=yes
|
||||
ProtectHome=yes
|
||||
ProtectHostname=yes
|
||||
ProtectKernelLogs=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectSystem=strict
|
||||
ReadWritePaths=-/sys/kernel/config/tsm/report
|
||||
RestrictAddressFamilies=AF_UNIX
|
||||
RestrictNamespaces=yes
|
||||
RestrictRealtime=yes
|
||||
RestrictSUIDSGID=yes
|
||||
RuntimeMaxSec=5min
|
||||
SystemCallArchitectures=native
|
||||
SystemCallErrorNumber=EPERM
|
||||
SystemCallFilter=@system-service
|
||||
Reference in New Issue
Block a user