Repository navigation
Description
Summary
The kind test from #293 proves cert-manager can enrol over ACME, but the node side runs in-process: no kernel, no init, no LUKS, no TPM, no real listeners. Nothing in CI boots the image operators actually install and drives it the way they would.
This adds a second, slower test that boots the real CryptOS image (unsigned UKI, swtpm, OVMF) in QEMU on a GitHub-hosted runner as a Root and an Intermediate, and runs a suite of named steps against them over the real mTLS API with cryptosctl. Each step reports pass, fail or skip (with the reason) in the job summary. Anything that needs outside gear (AD/LDAP, Cisco, Palo Alto, VMware) stays out.
It also measures how much of the codebase the booted run exercises: a coverage-instrumented build of the binaries, used only by this test, with per-package percentages in the summary and an HTML report as an artifact. No threshold yet; the first runs set the baseline.
Acceptance criteria
- Two VMs boot from the same unsigned image: a Root and an Intermediate. The Root runs its ceremony; the Intermediate's CSR is signed by the Root with
ca sign-subordinateand submitted back, and the chain verifies. - Leaf issuance with
ca issue-leaffrom CSRs: ECDSA P-384 and RSA 3072 issue, RSA 2048 is refused. - The leaf is served by nginx (pinned by digest) with OCSP stapling;
curl --cacertandopenssl s_client -verify_return_errorsucceed and the stapled status is good. - Revocation: after
ca revoke, OCSP answers revoked, the CRL lists the serial and nginx's stapled status turns revoked after a refresh. - Protocol switch the production way:
config applywithpki.acmeandpki.estenabled shows a pending reboot, the reboot clears it and status shows them running; switching ACME off does the same in reverse. - ACME: a kind cluster with cert-manager (the pins from test(e2e): prove cert-manager gets a CryptOS certificate over ACME in a kind cluster #293) gets a Certificate from the booted Intermediate over http-01; the chain verifies to the Root,
ca list-issuedon the node shows the serial, and a forced renewal gives a new serial that is also listed. - EST simpleenroll and simplereenroll with curl and openssl.
- Re-certify the Intermediate on the same key.
- Escrow export and import on a software-key node (short passphrase refused), refused on a TPM node.
- The audit chain verifies after all of the above.
- Console reset on a throwaway VM, driven over the serial console.
- Steps the code can't do yet skip with a clear message (SCEP until feat(scep): serve SCEP so network devices can enrol a trustpoint #185 lands, SNTP until feat(init): keep the node clock in sync (NTP client with network.ntp_servers) #249 lands, image upgrade if QEMU can't carry it).
- Coverage: per-package percentages in the job summary, an HTML report artifact, and the production image never built with
-cover. -
task e2e:imageruns it on Linux and skips cleanly elsewhere. - A separate workflow runs it nightly, on changes to the image build, init, ACME and config paths, and on manual dispatch. It is not a required check, has a 40-minute timeout (split into jobs if needed), pins its tools with checksums, uses KVM when the runner has it and reports TCG otherwise.
Notes
Related: #293 (the fast in-process kind test, which stays the pull-request check), #284 (config apply for ACME and EST, needed for the protocol switch).
Activity
Bugs5382 commentedon Sep 30, 2026
Found while booting the image and fixed in #296: the revocation listener answered 404 to OCSP GET requests (RFC 6960 appendix A.1, the form nginx staples with), because only the exact path
/ocspwas routed. It now routes/ocsp/and decodes the request from the escaped path, withTestOCSPEndpointGETServesResponsecovering it.