You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Container pulls inside the guest (via our dd-podman wrapper) use a policy.json that requires sigstoreSigned signatures backed by GitHub's OIDC workflow identity — so podman refuses to run any image we didn't attest.
✅ CP receives an ITA-verified TDX quote with the MRTD at /register (src/cp.rs:287, ita_claims.mrtd).
❌ CP does nothing with that MRTD beyond logging it. Any ITA-verified VM with the right owner/env_label gets a tunnel.
❌ Guest's policy.json is insecureAcceptAnything — any image digest runs (see apps/podman-bootstrap/workload.json).
So the infrastructure exists to prove "this binary came from CI" and "this TDX VM is running some binary" — but nothing checks they're the same binary, and nothing checks the containers running inside are ones we signed.
Proposed scope
Phase 1 — CP verifies build attestation on /register
Add a CP config DD_TRUSTED_REPO (default devopsdefender/dd) and DD_TRUSTED_WORKFLOW_REF (default refs/heads/main).
In src/cp.rs::register, after ITA verify:
Extract mrtd from ita_claims (already parsed).
Query GitHub REST: GET /repos/{trusted_repo}/attestations/sha256:{mrtd_hex}.
Parse the returned Sigstore bundle; verify the certificate's SAN matches the expected workflow identity (https://github.com/{trusted_repo}/.github/workflows/release.yml@{trusted_ref}).
Reject with 403 if no attestation matches.
Cache verified MRTDs per process lifetime (in-memory map).
Library: try the sigstore Rust crate. If maturity's thin, shell out to gh attestation verify at runtime.
Prereqs: a stable, reproducible MRTD for each release. actions/attest-build-provenance signs the musl binary's sha256, but MRTD is over the VM's launch memory image (kernel + initrd + rootfs + QEMU params), not just the binary. Either:
(a) Also attest the EE image (qcow2) — requires CI integration with the easyenclave repo's release pipeline.
(b) Derive MRTD deterministically offline from the EE image bytes, attest that.
(c) Launch-and-measure: spin up a throwaway TDX VM in CI, pull MRTD from an ITA round-trip, attest.
(c) is simplest if we can get a TDX self-hosted runner; (a) is cleanest long-term. Pick one; rollout order forces it to happen before the /register verify is turned on.
Plus a CI step that signs published container images (e.g. a thin ollama+openclaw wrapper image we publish to ghcr.io/devopsdefender) with cosign sign --identity-token=$GITHUB_TOKEN ghcr.io/....
Upstream ollama/ollama:latest isn't signed by us, so any workload that wants to run a third-party image needs an explicit policy exception. For the canonical path, publish our own image layered on top of theirs — one-time work, gives us signing authority.
Phase 3 — Per-workload attestation on /health
Agent already returns ita_token + deployments[]. Extend so each deployment entry includes:
image_digest (from podman inspect)
attestation_status (green / yellow / red / unknown — result of a sigstore verify pass)
Dashboard shows a per-workload badge. The fleet view at /agent/{id} surfaces which workloads are backed by a signed image vs. running a best-effort unverified pull.
Out of scope / later
Drop PAT from /register entirely (plan file discussed this). Once MRTD attestation lands, GitHub ownership via PAT becomes redundant — the signed MRTD IS the identity.
Dropping ITA in favour of pure sigstore: nope — ITA proves Intel hardware, sigstore proves our code, both matter.
TUF / update server with attestation bundles — the GitHub-attestation REST endpoint is already a hosted verification service, no extra infra.
Summary
Close the loop from "CI built this artifact" to "this VM is running exactly that artifact" across two surfaces:
/registerverifies the registering agent's MRTD against a GitHub build attestation on this repo (PR ci(release): publish a signed attestation for every devopsdefender binary #125 already publishes them for everydevopsdefenderbinary).dd-podmanwrapper) use apolicy.jsonthat requiressigstoreSignedsignatures backed by GitHub's OIDC workflow identity — so podman refuses to run any image we didn't attest.Today we have:
actions/attest-build-provenance@v2attestations over the builtdevopsdefendermusl binary (PR ci(release): publish a signed attestation for every devopsdefender binary #125, merged)./register(src/cp.rs:287,ita_claims.mrtd).policy.jsonisinsecureAcceptAnything— any image digest runs (seeapps/podman-bootstrap/workload.json).So the infrastructure exists to prove "this binary came from CI" and "this TDX VM is running some binary" — but nothing checks they're the same binary, and nothing checks the containers running inside are ones we signed.
Proposed scope
Phase 1 — CP verifies build attestation on /register
DD_TRUSTED_REPO(defaultdevopsdefender/dd) andDD_TRUSTED_WORKFLOW_REF(defaultrefs/heads/main).src/cp.rs::register, after ITA verify:mrtdfromita_claims(already parsed).GET /repos/{trusted_repo}/attestations/sha256:{mrtd_hex}.https://github.com/{trusted_repo}/.github/workflows/release.yml@{trusted_ref}).sigstoreRust crate. If maturity's thin, shell out togh attestation verifyat runtime.Prereqs: a stable, reproducible MRTD for each release.
actions/attest-build-provenancesigns the musl binary's sha256, but MRTD is over the VM's launch memory image (kernel + initrd + rootfs + QEMU params), not just the binary. Either:easyenclaverepo's release pipeline.(c) is simplest if we can get a TDX self-hosted runner; (a) is cleanest long-term. Pick one; rollout order forces it to happen before the /register verify is turned on.
Phase 2 — Guest policy.json requires sigstoreSigned images
Replace
apps/podman-bootstrap/workload.json's{"default":[{"type":"insecureAcceptAnything"}]}with:{ "default": [{"type": "reject"}], "transports": { "docker": { "ghcr.io/devopsdefender": [{ "type": "sigstoreSigned", "fulcioCert": "<Fulcio root CA>", "identity": { "exactRepo": "devopsdefender/dd", "exactWorkflow": ".github/workflows/release.yml" } }] } } }Plus a CI step that signs published container images (e.g. a thin
ollama+openclawwrapper image we publish to ghcr.io/devopsdefender) withcosign sign --identity-token=$GITHUB_TOKEN ghcr.io/....Upstream
ollama/ollama:latestisn't signed by us, so any workload that wants to run a third-party image needs an explicit policy exception. For the canonical path, publish our own image layered on top of theirs — one-time work, gives us signing authority.Phase 3 — Per-workload attestation on /health
Agent already returns
ita_token+deployments[]. Extend so each deployment entry includes:image_digest(frompodman inspect)attestation_status(green / yellow / red / unknown — result of a sigstore verify pass)Dashboard shows a per-workload badge. The fleet view at
/agent/{id}surfaces which workloads are backed by a signed image vs. running a best-effort unverified pull.Out of scope / later
Related
apps/podman-bootstrap/workload.jsonis the file that'd change in Phase 2.~/.claude/plans/fuzzy-hopping-marshmallow.md§ "Release attestation verification on /register" and § "MRTD-bound registration via GitHub release attestations" — earlier design notes.🤖 Generated with Claude Code