diff --git a/README.md b/README.md index 1863150..0d59d25 100644 --- a/README.md +++ b/README.md @@ -415,7 +415,7 @@ cargo build --release -p enclave-runtime --features testing --bin passkey-client | `scripts/ci-check.sh` | Formatting, Clippy with warnings denied, workspace library tests, boot-origin tests | | `scripts/ci-guests.sh` | Builds the guests; tests tasks, held connections, dispatch, TLS binding, HTTP/2, gRPC, authentication and logging | | `scripts/ci-storage.sh` | Real MinIO/S3: retention, remount, corruption, rollback and competing claims (needs Docker) | -| `scripts/ci-e2e.sh` | The stages above, then the full QEMU stack (needs KVM, Docker and Nix) | +| `scripts/ci-e2e.sh` | The stages above, then the full QEMU stack (needs KVM, Docker and Nix), once with the example guest and once with the released [cosigner](https://github.com/vtxos/cosigner) pinned in [`cosigner.lock`](cosigner.lock) | | `scripts/wit-drift.sh` | Vendored guest WIT matches [`wit/`](wit/) | | `scripts/ci-bench.sh` | Criterion storage and instance-cost benchmarks | diff --git a/cosigner.lock b/cosigner.lock new file mode 100644 index 0000000..50bede1 --- /dev/null +++ b/cosigner.lock @@ -0,0 +1,4 @@ +# The cosigner this runtime's CI serves: a release of vtxos/cosigner, by name and the sha256 of its +# cosigner.wasm. scripts/cosigner.sh fetches it and refuses any other bytes. Shell syntax. +RELEASE=v0.1.0 +SHA256=ba4976e0b547e9e342fca273e1cd78d2426d0df55e2ea5d9761b6428b86889cf diff --git a/deploy/qemu-nitro/run-e2e.sh b/deploy/qemu-nitro/run-e2e.sh index dad2751..4853f63 100755 --- a/deploy/qemu-nitro/run-e2e.sh +++ b/deploy/qemu-nitro/run-e2e.sh @@ -59,6 +59,10 @@ PREFIX=e2e # A second guest, altered, for leg 8. Nothing else here needs one, so lib.sh # does not stage it unless asked. WITH_SUBSTITUTE=1 +# Any guest, not the example one (with GUEST_WASM): only the legs that hold for +# every guest run — the gate, the attestation, the PCRs, a restart and a +# substitution. The rest drive guest-http's own routes. +GUEST_AGNOSTIC="${GUEST_AGNOSTIC:-}" # shellcheck source=lib.sh source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib.sh" @@ -91,6 +95,7 @@ code="$(curl -sk -o /dev/null -w '%{http_code}' --max-time 20 \ || fail "a request with no nonce answered $code; it should be refused before routing" echo "un-nonced requests refused: 400" +if [[ -z "$GUEST_AGNOSTIC" ]]; then # guest-http's routes, to the end of 2b say "2/8 a passkey enrols and its signed requests reach the guest" rm -f "$RUNDIR/alice.json" "$RUNDIR/bob.json" signed enrol >/dev/null || fail "registration failed" @@ -134,6 +139,7 @@ for i in $(seq 1 "$FILES"); do || fail "/files/work-$i.txt kept a stale value \"$got\" after being overwritten" done echo "$FILES files written, read, overwritten and re-read through the gate" +fi say "3/8 the attestation binds this connection's certificate, runtime and guest" # There is no attestation endpoint: the document rides on the `/auth/` exchange, @@ -151,6 +157,7 @@ say "3/8 the attestation binds this connection's certificate, runtime and guest grep -q "binding the attested certificate" "$RUNDIR/attest.log" \ || fail "the document did not bind the certificate this connection was served" +if [[ -z "$GUEST_AGNOSTIC" ]]; then # signs for guest-http's /counter say "3b/8 a signed interaction verifies the enclave before it trusts it" # The proof comes from the `/auth/` exchange, not from the guest's response: # that exchange is where a client identifies the enclave, before it hands over @@ -179,6 +186,7 @@ signed --dump-proof "$RUNDIR/proof" get --path /counter >/dev/null \ grep -q "binding the attested certificate" "$RUNDIR/attest-guest.log" \ || fail "the document did not bind that connection's certificate" +fi say "4/8 the attested PCR0 and PCR16 are the ones the builds produced" # `nitro-attest --pcr0 --guest` already enforced both, so reaching here means @@ -192,6 +200,7 @@ echo " attested: $(grep -oE '^PCR16 +[0-9a-f]+' "$RUNDIR/attest.log" | awk # --------------------------------------------------------------------------- # 5/8 — one instance per request, and one approval per interaction. # --------------------------------------------------------------------------- +if [[ -z "$GUEST_AGNOSTIC" ]]; then # guest-http's routes, to the end of 6b say "5/8 every request gets a fresh instance, and no two tenants share one" # `/memory` counts in the guest's linear memory and writes nowhere. What it @@ -409,6 +418,7 @@ echo "$wake" | jq -e '.message.notification == null' >/dev/null \ keys="$(echo "$wake" | jq -r '.message.data | keys | join(",")')" [[ "$keys" == "category,ref,v" ]] || fail "the wake carried more than its labels: $keys" echo "a data-only wake reached the enrolled device: category=task-done ref=e2e-job" +fi # --------------------------------------------------------------------------- # 7/8 — the boot machine, across a restart. @@ -515,6 +525,23 @@ echo "it attests the substitute it is running: PCR16 $SUBSTITUTE_PCR16" docker rm -f "$PREFIX-qemu-substitute" >/dev/null 2>&1 || true +if [[ -n "$GUEST_AGNOSTIC" ]]; then + cat </dev/null; then + echo "cosigner $RELEASE already at $out" + exit 0 +fi + +say "fetching cosigner $RELEASE" +mkdir -p "$(dirname "$out")" +url="https://github.com/vtxos/cosigner/releases/download/$RELEASE/cosigner.wasm" +curl -fL --retry 3 --retry-all-errors --max-time 300 -o "$out.part" "$url" \ + || fail "could not download $url" +echo "$SHA256 $out.part" | sha256sum -c - \ + || fail "cosigner $RELEASE does not match cosigner.lock" +mv "$out.part" "$out"