← Week 1: The SPIFFE Model

Day 4: The SPIFFE Workload API

Phase 4 · August 8, 2026

← Week 1: The SPIFFE Model

Agenda (2–3 hours)

  • Read (45 min): Workload API spec; SPIRE agent docs (how it serves the API)
  • Study (45 min): The gRPC proto; understand what a workload receives
  • Build (60 min): Add a simulated Workload API reader to spiffe-demo
← Week 1: The SPIFFE Model

What Is the Workload API?

The SPIFFE Workload API is the interface between the SPIRE agent and workloads.

  • A gRPC service served over a Unix domain socket (no network, no port)
  • Default socket path: unix:///tmp/spire-agent/public/api.sock
  • No authentication required — the agent uses kernel-attested identity (PID, UID)
    to determine which SVID to return for each connecting process
  • Streaming response — the agent sends an update whenever the SVID is rotated

A workload just opens the Unix socket, calls FetchX509SVIDs, and receives
its current cert + private key + trust bundle. No credentials needed.

← Week 1: The SPIFFE Model

The Workload API Proto (simplified)

service SpiffeWorkloadAPI {
  // Streams X.509 SVIDs to the caller; re-sends on rotation
  rpc FetchX509SVID (X509SVIDRequest) returns (stream X509SVIDResponse);

  // Streams trust bundle updates
  rpc FetchX509Bundles (X509BundlesRequest) returns (stream X509BundlesResponse);

  // JWT-SVID fetch (for HTTP/API contexts)
  rpc FetchJWTSVID (JWTSVIDRequest) returns (JWTSVIDResponse);
}

The X509SVIDResponse contains:

  • svids: list of X509SVID (cert DER + private key DER + bundle DER)
  • crl: revocation lists (rarely used — short validity replaces CRL)
  • federated_bundles: trust bundles from federated trust domains
← Week 1: The SPIFFE Model

What a Workload Receives

X509SVIDResponse {
  svids: [
    X509SVID {
      spiffe_id: "spiffe://leo.amazon.com/ns/prod/svc/provisioning"
      x509_svid: <DER-encoded certificate chain>
      x509_svid_key: <PKCS#8 DER private key>
      bundle: <DER-encoded trust bundle (CA certs)>
    }
  ]
  federated_bundles: {}
}

A workload uses this to:

  1. Configure its TLS server cert (mTLS server side)
  2. Configure its TLS client cert (mTLS client side)
  3. Validate peer SVIDs (using the bundle as trust anchor)

The SPIRE agent rotates the SVID ~5 min before expiry.
The workload gets a new message on the stream — hot reload, no restart.

← Week 1: The SPIFFE Model

The Security Model: Why No Auth on the Socket?

The socket lives in a directory only accessible to the SPIRE agent and root.
The agent uses kernel-level facts to attest the caller:

Workload (PID 1234) → connects to socket
SPIRE agent → reads /proc/1234/... to determine:
  - binary path: /opt/provisioning-service/bin/server
  - UID: 1001
  - GID: 1001
  - k8s labels (if running in k8s): namespace=prod, sa=provisioning
→ matches a registered entry → issues SVID for that entry's SPIFFE ID

The attestation is implicit — the workload doesn't need to prove anything.
The operating system provides the proof.

This is fundamentally different from fetching a cert from Secrets Manager
(where the secret is the proof of identity).

← Week 1: The SPIFFE Model

Using the Workload API in Rust

The spiffe crate provides a client:

use spiffe::{WorkloadApiClient, X509SvidFetcher};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Connect to the SPIRE agent Unix socket
    let mut client = WorkloadApiClient::default().await?;
    // Default: reads SPIFFE_ENDPOINT_SOCKET env var or
    //          unix:///tmp/spire-agent/public/api.sock

    // Fetch the current X.509 SVID
    let svids = client.fetch_x509_svid().await?;

    for svid in &svids {
        println!("SPIFFE ID: {}", svid.spiffe_id());
        println!("Cert expiry: {:?}", svid.cert_expiry());
    }

    Ok(())
}
← Week 1: The SPIFFE Model

Simulating the Workload API (for local dev without SPIRE)

When SPIRE isn't running (i.e., now), you can simulate the response:

pub struct SimulatedWorkloadApi {
    svid: X509Svid,     // cert + key + bundle you generated in Day 3
}

impl SimulatedWorkloadApi {
    pub fn fetch(&self) -> &X509Svid { &self.svid }
}

pub struct X509Svid {
    pub spiffe_id: String,
    pub cert_der: Vec<u8>,
    pub key_der: Vec<u8>,       // PKCS#8 DER
    pub bundle_der: Vec<Vec<u8>>, // CA certs
}

This abstraction lets you write your mTLS code against the simulated API now,
and swap in the real spiffe crate client when SPIRE is running.

← Week 1: The SPIFFE Model

Challenge Assignment

Add a workload_api module to spiffe-demo:

  1. Define an X509Svid struct with spiffe_id, cert_der, key_der, bundle_der
  2. Implement SimulatedWorkloadApi::fetch() that returns an X509Svid built from
    the cert you generated on Day 3 (make-svid)
  3. In main.rs, add a fetch subcommand that:
    • Calls the simulated API
    • Prints the SPIFFE ID, cert validity window, and cert size
    • Prints "Would configure mTLS with this SVID"
  4. Add the spiffe crate to Cargo.toml as an optional dependency (features = ["workload-api"])
    — you'll use it for real in Week 2

Document: what would the real client look like vs. your simulation?
What env var does the spiffe crate read to find the socket?

← Week 1: The SPIFFE Model

Resources

  • SPIFFE Workload API spec: github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Workload_API.md
  • SPIFFE Workload API proto: github.com/spiffe/spiffe/tree/main/proto/workload.proto
  • spiffe Rust crate: docs.rs/spiffe — WorkloadApiClient docs
  • SPIRE agent config: spiffe.io/docs/latest/deploying/spire_agent