Skip to content

fix: pin enclave container image digest in attestation, fail closed#9454

Open
khoaguin wants to merge 2 commits into
devfrom
fix/enclave-pin-image-digest
Open

fix: pin enclave container image digest in attestation, fail closed#9454
khoaguin wants to merge 2 commits into
devfrom
fix/enclave-pin-image-digest

Conversation

@khoaguin

Copy link
Copy Markdown
Member

Summary

EXPECTED_IMAGE_DIGEST in syft-enclave was an empty TODO, and the verifier treated it as "skip the image check." That meant attestation only proved genuine Confidential Space hardware — not that the enclave runs the audited image. Anyone controlling the deployment (operator, CI, compromised SA) could swap the container and still pass attestation.

This PR pins the digest and makes the check fail closed:

  • EXPECTED_IMAGE_DIGEST pinned to the current docker.io/openminedreleasebot/syft-client-enclave:latest digest (sha256:23a06dd0…), with a comment on how to re-pin.
  • Verify path rejects on any of: empty pinned digest, token missing the image_digest claim, or digest mismatch. The old passed=None skip path is gone.
  • Tests cover all three reject paths, plus a guard that the constant stays a well-formed sha256: digest (so it can't be silently blanked again).
  • docs/security.md gains a "Verifying the enclave image digest" section: how a data owner independently fetches the published digest from Docker Hub (docker buildx imagetools inspect) and compares it to the constant at the installed release tag — plus the known gap (no signed release manifest yet; cosign/sigstore is the planned hardening).

Before / After

Before

  • Trigger: a data owner verifies an enclave peer's attestation before approving it.
  • Today: the checklist shows "Image digest … skipped" and verification passes — secure boot and hardware are checked, but which container is running is not.
  • Cost: silent trust gap. A tampered image in a real TEE passes attestation; the data owner believes they verified the audited code when they didn't.

After

  • Same trigger.
  • Now: the token's image digest must match the pinned released-image digest. Empty config, missing claim, or mismatch → hard AttestationError, named in the failure list like every other check.
  • Win: attestation now actually binds trust to the reviewed image, and a data owner can confirm the pinned digest independently of this repo (documented in docs/security.md).

Don't regress

  • All five checks still run even when one fails (full-picture checklist, JWT-signature failure remains the only fail-fast).
  • Version check keeps its skip semantics for tokens without a version nonce — only the image-digest check changed.
  • Peers with no attestation token at all (non-TEE peers) still skip attestation in client.py — unchanged, flagged as a separate discussion.

For maintainers

Releasing a new enclave image now requires re-pinning EXPECTED_IMAGE_DIGEST in the same release — otherwise clients correctly reject the mismatched enclave. Worth wiring into the just publish recipe as a follow-up.

Test plan

  • uv run --with pytest pytest tests/ in packages/syft-enclave — 69 passed.

🤖 Generated with Claude Code

The image_digest check previously skipped when EXPECTED_IMAGE_DIGEST was
empty (a TODO), so the verifier accepted any container running in a
genuine Confidential Space — whoever controls the deployment could swap
in a tampered image and attestation still passed.

- Pin EXPECTED_IMAGE_DIGEST to the released
  openminedreleasebot/syft-client-enclave:latest digest
- Fail closed: reject on empty pinned digest, missing image_digest
  claim, or mismatch — no skip path
- Tests for all three reject paths + guard that the constant stays a
  real sha256 digest
- docs/security.md: how data owners independently confirm the digest,
  and the maintainer re-pin step on each image release

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@khoaguin

Copy link
Copy Markdown
Member Author

Provenance of the pinned digest

EXPECTED_IMAGE_DIGEST = sha256:23a06dd0e2173e734eea9f0492ea019babfa6240e00702a6e76aee30a2cb1ec8 was fetched live from Docker Hub on 2026-07-19, for the image the Justfile deploys (image_base = docker.io/openminedreleasebot/syft-client-enclave, tag :latest):

TOKEN=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:openminedreleasebot/syft-client-enclave:pull" | jq -r .token)
curl -sI -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/vnd.oci.image.index.v1+json, application/vnd.docker.distribution.manifest.list.v2+json" \
  "https://registry-1.docker.io/v2/openminedreleasebot/syft-client-enclave/manifests/latest" \
  | grep -i docker-content-digest

Equivalent one-liner: docker buildx imagetools inspect docker.io/openminedreleasebot/syft-client-enclave:latest (top-level Digest: line).

Reviewer checks before marking ready

  1. Confirm the digest is the audited image. This pins whatever :latest pointed to at fetch time — it does NOT prove that image was built from reviewed source. Release owner should confirm it corresponds to the intended build (ideally reproduce from docker/Dockerfile).
  2. Confirm the claim format against a real token. This is the multi-arch manifest-list digest. Confidential Space's submods.container.image_digest claim should report the same resolved-reference digest, but verify once against a live enclave: deploy from current :latest, run attest_peer, check the image-digest line passes. If the claim carries the per-arch (amd64) manifest digest instead, the pinned value must be swapped to that.

🤖 Generated with Claude Code

@khoaguin
khoaguin marked this pull request as ready for review July 19, 2026 04:53
@khoaguin khoaguin changed the title Enclave attestation: pin container image digest, fail closed fix: pin enclave container image digest in attestation, fail closed Jul 19, 2026
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant