Repository navigation
test(e2e): boot the real image in QEMU as a Root and an Intermediate and run the product suite - #296
Merged
Merged
Conversation
Bugs5382
marked this pull request as ready for review
September 30, 2026 11:03
Bugs5382
force-pushed
the
test/295-e2e-image-suite
branch
from
September 30, 2026 11:06
4a169ff to
da2a14a
Compare
15 tasks
15 tasks
This was referenced Sep 30, 2026
Bugs5382
force-pushed
the
test/295-e2e-image-suite
branch
3 times, most recently
from
September 30, 2026 23:20
5259589 to
392b64a
Compare
RFC 6960 appendix A.1 lets a client send the request as the URL-encoded base64 of its DER appended to the responder URL, and nginx staples that way. The mux only routed the exact path /ocsp, so every GET request got a 404. Route /ocsp/ as well and decode the request from the escaped path, so a percent-encoded / in the base64 survives. Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
…and run the product suite A second, slower end-to-end test next to the in-process kind test: the image boots in QEMU with swtpm and OVMF as a software-key Root and a TPM-key Intermediate, and a suite of named steps drives them over the mTLS API with cryptosctl, each reporting pass, fail or skip in a summary. The image is a coverage-instrumented variant built only for this test (the e2ecover tag and -cover); the init flushes its counters to a small FAT disk the harness reads back, and the merged report goes into the job summary. Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
…w server certificate Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
… successor image Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
… upgrades Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
…ing the report Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
The Root boots with state_key.mode nodeid on a tpm-default image. Now that later boots read the mode from the LUKS2 header, the Root comes back from a reboot, so the suite runs its in-place upgrade step instead of skipping it. The SNTP and SCEP skip reasons now say what is missing, since both are on main: the suite runs no NTP server and no SCEP client yet. Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
…ite's Go files Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
The status Protocols line lists SCEP since SCEP support landed, so after the reboots it reads "ACME on, EST on, SCEP off" and "ACME off, EST on, SCEP off". The protocol switch step matched the line up to its newline without SCEP and failed although the switch itself worked. Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
Once a node commits its CA, the management listener presents a leaf signed by that CA for the network.address IP and the EST hostnames, without localhost, followed by the CA chain. The suite pinned the self-signed leaf and checked the name localhost, so the first call after the Root's ceremony failed the handshake. The suite now checks the node by its address, trusts the last certificate the listener presents (the self-signed leaf before a CA, the root after), switches to the root once the ceremony, the subordinate submit or the key import commits a CA, and tells boots apart by the leaf rather than the trust file. Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
Bugs5382
force-pushed
the
test/295-e2e-image-suite
branch
from
October 1, 2026 04:23
392b64a to
9e45bc6
Compare
The suite took a server certificate other than the last one it pinned as proof of a new boot. A CA-signed management certificate is re-minted without a reboot when the CA is re-certified, so after the Intermediate was re-certified the upgrade step saw a "new" certificate from the boot it had just asked to activate, read its image status two seconds after the request and failed. After a reboot or an image activation, the suite now waits until the node's status reports a boot count above the one before the request. Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
Contributor
Author
|
Merging for the v0.1.0 test run: CI green and mergeable at merge time. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
The kind test from #293 proves cert-manager can enrol over ACME, but its node runs in-process. Nothing in CI booted the image operators install. This adds a second, slower test that does. It boots the real CryptOS image in QEMU (swtpm, OVMF) on a GitHub-hosted runner, as a software-key Root and a TPM-key Intermediate. A suite of named steps then drives both over the mTLS API with
cryptosctl. Each step reports pass, fail or skip (with the reason) in the job summary.test/integration/image_suite_test.go,TestImageSuite):ca get-subordinate-csr,ca sign-subordinateon the Root,ca submit-subordinate-cert, and the chain verifies.ca issue-leaffrom CSRs. ECDSA P-384 and RSA 3072 issue; RSA 2048 is refused.curl --cacert,openssl s_client -verify_return_errorandcurl --cert-statusall pass.ca revoke, then OCSP says revoked (POST, GET andopenssl ocsp) and the CRL lists the serial. nginx stops stapling good after a reload; it logs the revoked status and staples nothing, andcurl --cert-statusrefuses.config applywithpki.acmeandpki.estshowsACME on (not running, reboot pending)andReboot: pending. After a reboot, both are running and nothing is pending. Switching ACME off later readsstill running, reboot pendinguntil the reboot, then off.http-01. The chain verifies to the Root, andca list-issuedshows the serial. A forced renewal gives a new serial, also listed.simpleenroll(HTTP Basic, with a wrong password refused with 401) andsimplereenroll(TLS client certificate), both with curl, with openssl reading the PKCS#7.image stageof a signed successor,image activate, back on the new version with the same identity, and still issuing.main(feat(init): keep the node clock in sync with a client-only SNTP #271), but the suite runs no NTP server for the node to sync against yet.main(feat(scep): serve SCEP so network devices can enrol a trustpoint #288), but the suite runs no SCEP client against it yet.http-01fetch resolves the test name through QEMU's DNS proxy and the runner's resolver.run.shmaps the name to 10.0.0.1 in/etc/hosts, the runner's loopback as the VM sees it, and kind maps the ingress there. The cluster reaches the ACME origin through a TLS front on the kind bridge, whose certificate the booted Intermediate issued. There is no validator bypass.ubuntu-latestrunners have/dev/kvm. The workflow opens it with the udev rule GitHub documents, and the job summary says whether KVM was there. TCG is the fallback, with longer boot timeouts.test/image/build.shbuilds a variant used only by this test. It has the same kernel, static tools and rootfs;/initis built with-cover -covermode=atomicand thee2ecovertag, and the kernel command line isqemu-dev, so the boot log and console are on serial. The image carries a throwaway upgrade anchor, discarded with the build, plus a signed successor image. PID 1 never exits, socmd/init/coverflush_e2ecover_linux.go, compiled only with that tag, rewrites the counters every 2 s to a small FAT disk the harness reads back after the VM stops.cryptosctlon the runner is instrumented too.test/image/coverage.shmerges the counters into a per-package table in the summary and an HTML report in the artifacts. No release or CI image is built with-coveror the tag.ci-e2e-image.yml):ci-image.yml's cache.test/kind/versions.env; nginx is pinned by digest intest/image/versions.env. QEMU, swtpm and OVMF come from the runner's distro packages, likeci-image.yml's toolchain.task e2e:imageon Linux. It skips with a message on a non-Linux host or without docker, qemu, swtpm or OVMF.Found by booting the image:
fix(revocation)commit). RFC 6960 appendix A.1 lets a client append the URL-encoded base64 request to the responder URL, and nginx staples that way. The mux only routed the exact path/ocsp. It now routes/ocsp/and decodes from the escaped path, so a percent-encoded/survives. Test:TestOCSPEndpointGETServesResponse, which failed with 404 before the fix.state_key.modecame from the machine config (nodeidon atpm-default image) could not come back from any reboot, because later boots used the image's build-time mode. fix(init): read the state-key mode from the volume after the first boot #305 reads the mode from the LUKS2 header instead, and the Root upgrade step, skipped until then, runs and passes again.Design calls:
ci-image.yml. That workflow uploads nothing onmain(its per-run key is discarded), and its release assets carry neither counters nor a serial console. A cache keyed on the build inputs gives the same reuse without either problem./dev/kmsg, which the kernel rate-limits, and lines were lost in an early run.Closes #295
Related: #293, #284, #286, #305. Docs: CryptOS-PKI/docs#31 (
docs/25-use-cases-pages, 255d7af) extends the Kubernetes page's "Tested in CI" note to cover this suite; it depends on this PR. The README gains the layout lines,task e2e:imageand the workflow, anddocs/image-upgrade.mdsays what the suite now proves about the upgrade path on both a TPM node and anodeidnode.Verification
golangci-lintv2.12.2, also with--build-tags integration,e2ecoverfor Linux;go vet;gofmt;actionlint;shellcheck)go test ./...)How this was verified
go test ./...locally (CGO on, for the TPM simulator tests), and the new OCSP GET test failed before the fix./dev/kmsgrate limit and the set-mode coverage error.state_key.modereboot bug.ci-e2e-imagecheck, 36706496825 on da2a14a: 14 pass, 0 fail, 4 skip.internal/installandinternal/kms(0%; no step installs from the ISO or runs a KMS-mode node),internal/cms(6%; SCEP is not onmain),internal/console(7%; the console runs as its own process, which is not instrumented yet),internal/ca(32%) andinternal/grpc(45%).task e2e:imagelocally under TCG, after the fix(init): read the state-key mode from the volume after the first boot #305 rebase: 15 passed, 0 failed, 3 skipped (audit, SNTP, SCEP), both upgrade steps included.mainafter docs: OS-specific commands and risk callouts #277, fix(attest): bind Attest signatures to a versioned attestation context #279 and feat(tpm): hold an RSA CA key in the TPM #282 landed. Thedocs/image-upgrade.mdconflict with docs: OS-specific commands and risk callouts #277 was resolved by keeping its callout and updating the text inside it. The earlier.gitleaksignorecommit is dropped: feat(config): switch ACME and EST on and off through machine config #286's own entry (at its rebased commit205dd64) covers the test fixture, andgitleaks gitover the branch finds no leaks.Before merging: add a closing comment summarizing what was actually done in this PR
(not just the checked boxes).