ci: add test for the new logic

This commit is contained in:
Lennart Poettering
2026-06-19 05:23:18 +02:00
parent 7683c3281c
commit 9ebbbfd00e
+108 -1
View File
@@ -109,7 +109,9 @@ CERTDIR=$(mktemp -d)
at_exit() {
set +e
systemctl stop fake-report-server fake-report-server-tls
rm -rf "$CERTDIR"
systemctl stop systemd-report.socket systemd-report-files.socket systemd-report-sign-plain.socket
rm -f /run/systemd/report.files/testreportfile /run/systemd/report.files/binaryfile
rm -rf "$CERTDIR" "${SIGN_WORK:-}"
}
trap at_exit EXIT
@@ -132,3 +134,108 @@ systemctl status fake-report-server-tls
"$REPORT" upload --url=https://localhost:8090/ --key=- --trust="$CERTDIR/server.crt" \
--extra-header='Authorization: Bearer magic string'
# -----------------------------------------------------------------------------
# Test the systemd-report-files@.service metric provider: files dropped into one
# of the report.files directories are exposed verbatim as io.systemd.Files.*
# metrics, keyed by their file name.
mkdir -p /run/systemd/report.files
REPORT_FILE_CONTENT="hello from the report files metric provider"
printf '%s' "$REPORT_FILE_CONTENT" >/run/systemd/report.files/testreportfile
systemctl start systemd-report-files.socket
varlinkctl info /run/systemd/report/io.systemd.Files
varlinkctl list-methods /run/systemd/report/io.systemd.Files
# The metric value must be the exact file contents.
files_metrics="$(varlinkctl call --more /run/systemd/report/io.systemd.Files io.systemd.Metrics.List {})"
files_value() { echo "$files_metrics" | jq --seq -r "select(.name == \"$1\") | .value"; }
[ "$(files_value io.systemd.Files.testreportfile)" = "$REPORT_FILE_CONTENT" ]
# Describe must list the very same metric, of type string.
files_describe="$(varlinkctl call --more /run/systemd/report/io.systemd.Files io.systemd.Metrics.Describe {})"
echo "$files_describe" | jq --seq -r 'select(.name == "io.systemd.Files.testreportfile") | .type' | grep -wx string >/dev/null
# Files that aren't valid UTF-8 text must be skipped silently (the value is a
# JSON string, so only text files can be reported).
printf '\xff\xfe binary garbage' >/run/systemd/report.files/binaryfile
files_metrics="$(varlinkctl call --more /run/systemd/report/io.systemd.Files io.systemd.Metrics.List {})"
[ -z "$(files_value io.systemd.Files.binaryfile)" ]
rm -f /run/systemd/report.files/binaryfile
# -----------------------------------------------------------------------------
# Test report signing through the plain software backend, driven entirely via
# the io.systemd.Report Varlink interface (the GenerateSigned method). The plain
# backend is only built/installed with OpenSSL support, so skip if unavailable.
if ! systemctl cat systemd-report-sign-plain.socket &>/dev/null; then
echo "systemd-report-sign-plain.socket is not installed, skipping report signing test."
exit 0
fi
systemctl start systemd-report.socket
systemctl start systemd-report-sign-plain.socket
SIGN_WORK="$(mktemp -d)"
# Ask systemd-report to generate a *signed* report over Varlink. The reply
# carries the signed report as base64-encoded JSON-SEQ data.
varlinkctl call /run/systemd/io.systemd.Report io.systemd.Report.GenerateSigned \
'{"matches":["io.systemd.Manager.UnitsTotal"]}' | jq -r .reportData | base64 -d >"$SIGN_WORK/report.seq"
# The first JSON-SEQ record is the report itself. This is exactly the byte
# sequence that got signed, including the leading record separator (0x1e) and
# the trailing newline, so 'head -n1' reproduces it verbatim.
head -n1 "$SIGN_WORK/report.seq" >"$SIGN_WORK/message.bin"
tr -d '\036' <"$SIGN_WORK/message.bin" | jq -e '.mediaType == "application/vnd.io.systemd.report"' >/dev/null
# The remaining record(s) are signature objects. With only the plain backend
# enabled there is exactly one, with mechanism "plain".
sig_json="$(tail -n +2 "$SIGN_WORK/report.seq" | tr -d '\036')"
[ "$(echo "$sig_json" | jq -r .mechanism)" = "plain" ]
[ "$(echo "$sig_json" | jq -r .mediaType)" = "application/vnd.io.systemd.report.signature" ]
# The sha256 recorded in the signature must match the digest of the report bytes.
[ "$(echo "$sig_json" | jq -r .sha256)" = "$(sha256sum "$SIGN_WORK/message.bin" | cut -d' ' -f1)" ]
# Extract the embedded public key and the raw Ed25519 signature.
echo "$sig_json" | jq -r .data.key >"$SIGN_WORK/pub.pem"
echo "$sig_json" | jq -r .data.signature | base64 -d >"$SIGN_WORK/sig.bin"
# The signature is made over the SHA256 digest bytes directly (Ed25519, the
# signer does not pre-hash), so verify the raw digest against the signature.
openssl dgst -sha256 -binary "$SIGN_WORK/message.bin" >"$SIGN_WORK/digest.bin"
openssl pkeyutl -verify -pubin -inkey "$SIGN_WORK/pub.pem" -rawin \
-in "$SIGN_WORK/digest.bin" -sigfile "$SIGN_WORK/sig.bin"
# The public key embedded in the report must be the very key the plain backend
# generated and persisted on disk.
diff <(openssl pkey -pubin -in "$SIGN_WORK/pub.pem" -pubout) \
<(openssl pkey -pubin -in /var/lib/systemd/report.sign.plain/local.public -pubout)
# A tampered report must no longer verify against the signature.
printf 'x' >>"$SIGN_WORK/message.bin"
openssl dgst -sha256 -binary "$SIGN_WORK/message.bin" >"$SIGN_WORK/digest-bad.bin"
if openssl pkeyutl -verify -pubin -inkey "$SIGN_WORK/pub.pem" -rawin \
-in "$SIGN_WORK/digest-bad.bin" -sigfile "$SIGN_WORK/sig.bin"; then
echo "Signature verification unexpectedly succeeded for tampered report" >&2
exit 1
fi
# -----------------------------------------------------------------------------
# The plain backend also acts as a metric provider, exposing its public signing
# key as the io.systemd.Report.SignPlain.PublicKey metric. This is served off the
# very same socket as the signer, reachable in the metrics directory under
# /run/systemd/report/ (the signer endpoint is just a symlink into the root-only
# /run/systemd/report.sign/ directory pointing at the same socket).
varlinkctl info /run/systemd/report/io.systemd.Report.SignPlain
varlinkctl list-methods /run/systemd/report/io.systemd.Report.SignPlain
# The exposed public key must be the very key persisted on disk.
sign_metrics="$(varlinkctl call --more /run/systemd/report/io.systemd.Report.SignPlain io.systemd.Metrics.List {})"
echo "$sign_metrics" | jq --seq -r 'select(.name == "io.systemd.Report.SignPlain.PublicKey") | .value' >"$SIGN_WORK/metric-pub.pem"
diff <(openssl pkey -pubin -in "$SIGN_WORK/metric-pub.pem" -pubout) \
<(openssl pkey -pubin -in /var/lib/systemd/report.sign.plain/local.public -pubout)
# Describe must list the very same metric, of type string.
varlinkctl call --more /run/systemd/report/io.systemd.Report.SignPlain io.systemd.Metrics.Describe {} |
jq --seq -r 'select(.name == "io.systemd.Report.SignPlain.PublicKey") | .type' | grep -wx string >/dev/null