ADR 0006 — Remote attestation (RATS) and SPIFFE collector identity¶
Status: accepted (both opt-in, default-off) · Date: 2026-10-10
Context¶
Before trusting a networked collector or an attestation claim, the project needs: authenticated per-node identity bound server-side to a node UID; and an attestation verifier that establishes fresh, device-bound, cryptographically appraised evidence — with mock / simulated evidence never accepted by enforcement. These are two different problems (identity of the collector vs. attestation of the device) and are implemented as two independent, opt-in features.
Decision¶
Two orthogonal, opt-in features¶
| Feature | Problem | Enable | Default |
|---|---|---|---|
| RATS attestation | Device attestation (what the GPU is / its measurements) | Cargo attestation / attestation-sim; context acceptedAttestationVerifiers |
off |
| SPIFFE / mTLS identity | Collector identity (who produced a report) | Cargo mtls on server / agent |
off |
Neither changes the default build or behavior. SPIFFE attests the node / workload, never the GPU; it is never treated as attestation (avoids the common category error).
RATS attestation (RFC 9334), modular by construction¶
AttestationEvidence(= Attestation Result) is carried as an optionalObservation<AttestationEvidence>on a device; context gainsacceptedAttestationVerifiers+attestationNonce.- The pure evaluator does no cryptography. It consumes an already-appraised
result and enforces provenance (
attestation_verifier), explicit verifier acceptance, freshness, nonce / replay binding, device binding, and outcome. Fail-closed: missing / not-accepted / stale / unbindable →indeterminate; explicitfail→deny; default (no accepted verifiers) → alwaysindeterminate. - The verifier is assembled from swappable traits so real crypto / formats
drop in without touching the evaluator or contract:
EvidenceDecoder,CryptoVerifier,AppraisalPolicy, composed byComposedVerifierand keyed in aVerifierRegistry. The shippedSimulatedVerifier(featureattestation-sim) does no cryptography; its id issim:*, which enforcement never accepts. - The gate that keeps simulation out of production:
acceptedAttestationVerifierslists only real verifier ids in enforce / production contexts;sim:*only in offline / test contexts.
SPIFFE / mTLS collector identity¶
- Feature
mtlson server + agent. A SPIFFE SVID is an X.509 cert with aspiffe://<trust-domain>/node/<node_uid>URI SAN. - The server serves HTTPS requiring a client cert rooted at the CA bundle,
extracts the peer SPIFFE ID, and on
POST /v1alpha1/inventoryderives the node UID from the verified cert, never the body. Mismatch → 403; non-monotonicsnapshotRevision→ 409 (replay); unverified / untrusted cert → TLS handshake rejected. - Two delivery paths: the offline host-process demo mints SVIDs with openssl; the
in-cluster path uses real SPIRE (
identity.enabled, default-off): SPIRE server + agent (k8s_psat) + SPIFFE CSI driver, withspiffe-helpersidecars delivering SVIDs to disk for the file-basedmtlsbinaries. A dedicated inventory Deployment receives collector reports; the observe / evaluate core is untouched.identity.spire.install=falseconsumes a pre-existing cluster SPIRE (BYO). This is authn of the collector, not attestation.
Consequences¶
attestation: requiredmoves from "always indeterminate" to "satisfiable only by an accepted, fresh, device-bound verifier result" — with simulation never accepted by enforcement.- Networked inventory can be bound to a server-verified node identity, without a shared-service-account bypass.
- Real GPU attestation still requires a confidential-computing-capable GPU + a real verifier (e.g. NVIDIA NRAS).
- Hardening (B): authenticated snapshots are attributed to
authenticated_collectorand feed evaluation —GET /v1alpha1/inventory/{nodeUid}andPOST /v1alpha1/evaluate-nodeevaluate using the stored, SVID-bound facts (fail-closedindeterminateif absent); enforce acceptsauthenticated_collectorwhile still rejectingmock. SVID rotation via a spiffe-helper daemon sidecar + reloading rustls cert resolver (client-CA bundle is leaf-only live reload, not hot-swapped). HA via observer / scheduler leader election atreplicas > 1. The observer consumes authenticated inventory via--authenticated-inventory=off|prefer|require(gated by Helmidentity.observer.useAuthenticatedInventory).