Skip to content

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 optional Observation<AttestationEvidence> on a device; context gains acceptedAttestationVerifiers + 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; explicit fail → deny; default (no accepted verifiers) → always indeterminate.
  • The verifier is assembled from swappable traits so real crypto / formats drop in without touching the evaluator or contract: EvidenceDecoder, CryptoVerifier, AppraisalPolicy, composed by ComposedVerifier and keyed in a VerifierRegistry. The shipped SimulatedVerifier (feature attestation-sim) does no cryptography; its id is sim:*, which enforcement never accepts.
  • The gate that keeps simulation out of production: acceptedAttestationVerifiers lists only real verifier ids in enforce / production contexts; sim:* only in offline / test contexts.

SPIFFE / mTLS collector identity

  • Feature mtls on server + agent. A SPIFFE SVID is an X.509 cert with a spiffe://<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/inventory derives the node UID from the verified cert, never the body. Mismatch → 403; non-monotonic snapshotRevision → 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, with spiffe-helper sidecars delivering SVIDs to disk for the file-based mtls binaries. A dedicated inventory Deployment receives collector reports; the observe / evaluate core is untouched. identity.spire.install=false consumes a pre-existing cluster SPIRE (BYO). This is authn of the collector, not attestation.

Consequences

  • attestation: required moves 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_collector and feed evaluation — GET /v1alpha1/inventory/{nodeUid} and POST /v1alpha1/evaluate-node evaluate using the stored, SVID-bound facts (fail-closed indeterminate if absent); enforce accepts authenticated_collector while still rejecting mock. 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 at replicas > 1. The observer consumes authenticated inventory via --authenticated-inventory=off|prefer|require (gated by Helm identity.observer.useAuthenticatedInventory).