← Week 2: SPIRE Internals and Attestation

Day 13: Registering Workload Entries and Issuing SVIDs

Phase 4 · August 17, 2026

← Week 2: SPIRE Internals and Attestation

Agenda (2–3 hours)

  • Prerequisites: SPIRE running from Day 12
  • Register (45 min): Create entries for two workloads
  • Fetch (60 min): Have a process fetch its SVID via the Workload API
  • Inspect (45 min): Parse and print the received SVID
← Week 2: SPIRE Internals and Attestation

Registering a Workload Entry

With the agent's SPIFFE ID from spire-server agent list:

# Register workload "service-a" for a process with UID 1000
docker compose exec spire-server \
  spire-server entry create \
    -parentID spiffe://example.org/spire/agent/join_token/abc123 \
    -spiffeID spiffe://example.org/service-a \
    -selector unix:uid:1000 \
    -ttl 3600

# Register workload "service-b" for a process with UID 1001
docker compose exec spire-server \
  spire-server entry create \
    -parentID spiffe://example.org/spire/agent/join_token/abc123 \
    -spiffeID spiffe://example.org/service-b \
    -selector unix:uid:1001 \
    -ttl 3600

# Verify both entries
docker compose exec spire-server spire-server entry show
← Week 2: SPIRE Internals and Attestation

Fetching an SVID: Using spire-agent fetch

The SPIRE agent includes a fetch command for testing:

# Run as UID 1000 inside the agent container
docker compose exec --user 1000 spire-agent \
  spire-agent fetch x509 \
    -socketPath /tmp/spire-agent/public/api.sock \
    -write /tmp/svid-output/

# Files written:
# /tmp/svid-output/svid.0.pem        — the SVID cert
# /tmp/svid-output/svid.0.key.pem    — the private key
# /tmp/svid-output/bundle.0.pem      — the trust bundle

Then inspect:

openssl x509 -in /tmp/svid-output/svid.0.pem -text -noout | grep -A5 "Subject Alt"
# Should show: URI:spiffe://example.org/service-a
← Week 2: SPIRE Internals and Attestation

Fetching an SVID from Rust

Add to spiffe-demo — a new fetch-live subcommand:

use spiffe::WorkloadApiClient;

pub async fn run_fetch_live() -> Result<()> {
    // Connects to SPIFFE_ENDPOINT_SOCKET or default unix socket path
    let mut client = WorkloadApiClient::default().await?;

    let svids = client.fetch_x509_svid().await?;
    
    for svid in &svids {
        println!("SPIFFE ID:   {}", svid.spiffe_id());
        
        // svid.cert_chain() returns the DER cert chain
        let cert_der = svid.cert_chain().first()
            .ok_or("no cert")?;
        
        println!("Cert size:   {} bytes", cert_der.len());
        
        // svid.private_key() returns the DER private key
        println!("Key size:    {} bytes", svid.private_key().len());
        
        // svid.trust_bundle() returns the trust bundle cert DER list
        println!("Bundle certs: {}", svid.trust_bundle().len());
    }
    
    Ok(())
}
← Week 2: SPIRE Internals and Attestation

The SVID You Receive

When you fetch the SVID for spiffe://example.org/service-a, you should see:

Certificate:
  Data:
    Serial Number: <short-lived serial>
    Validity:
      Not Before: Aug 17 10:00:00 2026 GMT
      Not After : Aug 17 11:00:00 2026 GMT    ← 1 hour TTL
    Subject: (empty)
    Subject Public Key Info: EC P-256
    X509v3 Subject Alternative Name:
      URI:spiffe://example.org/service-a      ← the SPIFFE ID
    X509v3 Key Usage:
      Digital Signature, Key Agreement
    X509v3 Extended Key Usage:
      TLS Web Server Authentication, TLS Web Client Authentication
    X509v3 Basic Constraints:
      CA:FALSE

Compare this to the cert you built manually on Day 3.
SPIRE produces exactly the X.509-SVID spec shape.

← Week 2: SPIRE Internals and Attestation

Watching SVID Rotation

With a 1-hour SVID TTL, rotation happens automatically ~5 min before expiry.
You can observe it with a short-TTL entry:

# Create an entry with a 1-minute SVID TTL (for testing rotation)
docker compose exec spire-server \
  spire-server entry create \
    -parentID spiffe://example.org/spire/agent/join_token/abc123 \
    -spiffeID spiffe://example.org/service-test \
    -selector unix:uid:1002 \
    -ttl 60

# Watch the Workload API stream for updates
# The spiffe crate's watch_x509_svid() returns a stream
use futures::StreamExt;
let mut stream = client.watch_x509_svid().await?;
while let Some(update) = stream.next().await {
    let svids = update?;
    println!("SVID updated at {:?}", std::time::SystemTime::now());
}
← Week 2: SPIRE Internals and Attestation

Challenge Assignment

With your running SPIRE deployment:

  1. Register two workload entries (service-a UID 1000, service-b UID 1001)
  2. Use spire-agent fetch x509 to retrieve an SVID for each
  3. Inspect each SVID with openssl x509 -text:
    • Confirm the SPIFFE ID in the URI SAN
    • Confirm the 1-hour validity
    • Confirm empty Subject
  4. Run the Rust fetch-live subcommand against the live SPIRE agent socket
  5. What SPIFFE ID does your process (running as your user's UID) get?
    If no entry matches, what error do you see?

Document: what happens when no entry matches the connecting workload?
How does SPIRE signal "no identity for this process"?

← Week 2: SPIRE Internals and Attestation

Resources

  • SPIRE entry create: spiffe.io/docs/latest/deploying/registering
  • spiffe Rust crate WorkloadApiClient: docs.rs/spiffe
  • spire-agent fetch: docker compose exec spire-agent spire-agent fetch --help