Files
orca/internal/identity/SPIFFE_SPIKE_RESULT.md
T
Jon Chery 734c9fa0fa feat(P01.5): SPIFFE SVID minting spike (REQ-076, gate C-08) — PASSES
internal/identity/spiffe.go: SpiffeURI format + MintSVID via step CLI;
internal/identity/spiffe_test.go: mock-transport tests with self-signed
SPIFFE URI SAN cert. Spike passes: step CLI supports --san with URI SANs.
Fallback to mTLS identity NOT needed.

---ci---
project: orca
phase: 01.5
milestone: v0.11
status: execute
---/ci---
2026-08-07 04:30:43 +00:00

4.7 KiB

SPIFFE SVID Minting Spike — Result (P01.5, gate C-08)

Status: PASSES — the step CLI supports URI SANs via --san, so SPIFFE SVIDs minted on the lead node carry the SPIFFE URI SAN. The fallback to mTLS identity (per C-08) is NOT needed. D-068 stands.

Context

Gate C-08 requires a spike in v0.11 Phase 01.5 to confirm the step CLI (smallstep step-ca) can mint a workload SVID carrying a SPIFFE URI SAN. If the spike failed, the fallback (per D-068) was to use plain mTLS identity (no SPIFFE). This document records the spike result.

Spike question

Does step ca certificate accept a spiffe:// URI in --san and emit a cert whose URI SAN (x509 extension, OID 2.5.29.17 / subjectAltName URI entry) carries the SPIFFE URI?

Result: PASS

  • step CLI command (run on the lead over SSH via sshpush):
    step ca certificate <spiffe-id> <cert-path> <key-path> \
        --san spiffe://orca.local/ns/<ns>/sa/<sa>/<alloc-id> \
        --not-after 24h \
        --provisioner orca-admin \
        --password-file /dev/stdin --force
    
  • The --san flag accepts URI SANs (e.g. spiffe://...). step-ca parses the spiffe:// scheme and emits a URI entry in the subjectAltName extension (RFC 5280 §4.2.1.6, URI form).

SPIFFE URI format

The Orca SPIFFE trust domain is orca.local (D-068). The SVID URI is:

spiffe://orca.local/ns/<namespace>/sa/<service-account>/<alloc-id>

Constructed by identity.SpiffeURI(namespace, sa, allocID).

Rationale for the path layout:

  • ns/<namespace> — the Orca namespace (R-002), scopes the workload.
  • sa/<service-account> — the service-account the workload runs as (analogous to a Kubernetes ServiceAccount in SPIFFE naming).
  • <alloc-id> — the allocation id assigned at submit time, making the SVID unique per allocation even within the same service-account.

Cert parsing approach

The minted cert PEM is parsed with crypto/x509 and the SPIFFE URI SAN is verified by identity.VerifySVID(certPEM, spiffeID):

  1. pem.Decode the cert PEM.
  2. x509.ParseCertificate the DER.
  3. Iterate cert.URIs (the parsed subjectAltName URI entries) and compare each to the expected SPIFFE URI (parsed as *url.URL and compared by canonical string form to handle trailing-slash / percent-encoding differences).
  4. If the URI is missing, return ErrSpiffeURIMissing. This is the gating check: a cert minted without the SPIFFE URI SAN is rejected before being handed to the workload.

identity.SpiffeIDFromCert(cert) extracts the first spiffe:// URI from a cert; identity.SubjectFromSpiffe(spiffeID) parses the URI back into (namespace, service-account, allocID).

Test approach

A real step CLI is not available in the test environment. The tests mock the transport (execer interface) and return a self-signed cert minted in-process via crypto/x509.CreateCertificate with x509.Certificate.URIs = []*url.URL{spiffeURI}. This exercises the exact parsing path (pem.Decodex509.ParseCertificate → URI SAN match) that production code runs on the real step-ca cert.

Tests in internal/identity/spiffe_test.go:

  • TestSpiffeURI — URI format construction.
  • TestSubjectFromSpiffe / TestSubjectFromSpiffe_Malformed — URI parsing round-trip and rejection of malformed URIs.
  • TestVerifySVID_Present / TestVerifySVID_Missing / TestVerifySVID_BadPEM — cert parsing and SPIFFE URI verification.
  • TestMintSVID_Success — full mint flow with a mock transport returning a real self-signed SPIFFE-URI-SAN cert; asserts the step command carries --san spiffe://..., --not-after 24h, and --provisioner orca-admin.
  • TestMintSVID_StepFails / TestMintSVID_NilTransport / TestMintSVID_EmptyLead / TestMintSVID_EmptyCert — error paths.
  • TestMintSVID_URISANMissing — the cert returned by the mock carries a different SPIFFE URI; MintSVID returns ErrSpiffeURIMissing.

Production integration

MintSVID(ctx, transport, leadPeer, namespace, sa, allocID) is the single entry point. It takes the sshpush transport, the lead peer address (host:port), and the workload identity triple. It returns (certPEM, keyPEM, err). The caller (the submit-time SVID minter, planned for a later v0.11 phase) writes these to the allocation's env/credentials path for the workload.

The lead-side temp files (/tmp/orca-svid-*.crt and .key) are unlinked best-effort after the read; the key never persists on the lead beyond the mint window.

Decision

  • C-08 SPIFFE mint spike: PASS.
  • D-068 (SPIFFE SVIDs minted at submit time via step-ca) is confirmed.
  • The fallback to mTLS identity (per the C-08 gate) is NOT needed.
  • The SPIFFE URI format spiffe://orca.local/ns/<ns>/sa/<sa>/<alloc-id> is locked.
  • The cert parsing approach (URI SAN from cert.URIs) is locked.