[Guardian] Pin attestation PCR0 via a per-build allowlist#675
Draft
mskd12 wants to merge 3 commits into
Draft
Conversation
Replace the single config PCR0 with a PcrAllowlist (git revision -> BuildPcrs):
verification resolves the entry named by the session's signature-verified
untrusted_git_revision and pins the attestation's PCR0 to it. Listing multiple
builds lets an upgrade's old+new images both verify; an unknown build is rejected.
- verify_enclave_attestation takes (allowlist, git_revision) and does the lookup
inside the prod branch, so non-enclave-dev stays a complete no-op.
- Consolidate the S3 read path: get_verified_enclave_pubkey -> get_enclave_identity,
returning a verified EnclaveIdentity { signing_pubkey, info }. The session cache
holds the identity, so get_info is now a cache hit instead of a second read.
- Config: expected_pcr0 (String) -> expected_builds (git rev -> PCR0 hex), with a
pcr_allowlist() accessor; provisioner + monitor + sample YAMLs + README updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the HashMap with { expected_build, prev_build: Option }, type-encoding
the 1..=2 cap (one mandatory + one optional) so 0 or 3+ builds are unrepresentable
and rot is impossible — a retired build can't linger across upgrades. expected_build
is the build we run; prev_build is the outgoing image during an upgrade window.
- BuildPcrs carries its git_revision; resolve() scans the two fields.
- New BuildPcrsConfig (git_revision + pcr0 hex) shared by both configs; YAML gains
expected_build/prev_build named fields (a flat map couldn't label which is which).
- Drop now-unused hex dep from hashi-monitor (hex decode lives in hashi-types).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
resolve() takes an AttestationSource: the live enclave (relay GetGuardianInfo, hashi-node queries) must run expected_build, while historical S3 logs may also come from the outgoing prev_build during an upgrade. So you can never provision or act on a stale enclave, but prior state written by the outgoing build still verifies. The source is consumed inside verify_enclave_attestation, so dev stays a complete no-op. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Replaces #666's single config PCR0 with a per-build allowlist, completing IOP-225 check C. Verification resolves the build named by the session's signature-verified
untrusted_git_revisionand pins the attestation's PCR0 to it; an unknown build is rejected (we don't know its measurements, so we can't trust it).Allowlist — capped at two builds
expected_buildis the build we run;prev_buildis the outgoing image, set only during an upgrade window so its still-running sessions verify until they rotate.Targeted by source
resolve(revision, source)— the live enclave must beexpected_build; historical S3 logs may also come fromprev_build:GetGuardianInfoResponse::verify(relayGetGuardianInfo, hashi-node queries) →LiveEnclave, so you can never provision/act on a stale enclave.get_enclave_identity(theGuardianReaderS3 path: prior withdrawals, old committee) →HistoricalLog.The lookup happens inside
verify_enclave_attestation(prod branch), sonon-enclave-dev/test stays a complete no-op — dev configs never enumerate a revision.Read-path consolidation
get_verified_enclave_pubkey→get_enclave_identity: reads the AWS-self-signed attestation (anchoring the signing pubkey), then the signedGuardianInfo(sig-checked under that pubkey → reported build), then pins PCR0.GuardianSessionKeyCache→GuardianSessionCache) holds the wholeEnclaveIdentity { signing_pubkey, info }, soget_infois a cache hit instead of a second S3 read.Config
expected_pcr0: String→expected_build(+ optionalprev_build), each{ git_revision, pcr0 }, via the sharedBuildPcrsConfig; a flat map couldn't label which entry is expected vs outgoing. Threaded through provisioner + monitor (configs + sample YAMLs + README). Dropped the now-unusedhexdep fromhashi-monitor.Trust ordering
Attestation cert-chain → anchor pubkey → info signature under it → PCR0 pinned to the named build. Selecting the build by the self-reported revision can't be gamed: PCR0 is the real attested image hash, so a wrong revision resolves to a PCR0 the real image won't match. Side effect: after this,
untrusted_git_revisionis bound to the attested image.Notes
OIAttestationUnsignedandOIGuardianInfoas separate logs — the attestation is AWS-self-signed (its own root of trust), so it stays unsigned.install_operator_init_for_testing);bootstrap_guardianusessigned_info.verifydirectly. Every session logsOIGuardianInfobefore any traffic, so the commit is always available.get_infoisHistoricalLog(permissive); the authoritative "expected build" gate is the relay precheck it submits the share through.🤖 Generated with Claude Code