← Week 3: Integration and Application

Day 16: The Rust SPIFFE Ecosystem — spiffe-rs and SVID Parsing

Phase 4 · August 20, 2026

← Week 3: Integration and Application

Agenda (2–3 hours)

  • Read (45 min): spiffe crate docs (docs.rs/spiffe); README on GitHub
  • Explore (45 min): Walk through the crate's types, focusing on X509Svid + WorkloadApiClient
  • Build (60 min): Robust SVID parser module in spiffe-demo
← Week 3: Integration and Application

The spiffe Rust Crate

spiffe (github.com/spiffe/spiffe-rs) is the official Rust SPIFFE library.

Key modules:

spiffe/
├── workload_api/
│   ├── client.rs      ← WorkloadApiClient — connects to Unix socket
│   ├── x509_source.rs ← X509Source — auto-updating SVID source with watch support
│   └── jwt_source.rs  ← JwtSource — JWT-SVID fetching
├── svid/
│   ├── x509.rs        ← X509Svid — cert chain + key + bundle
│   └── jwt.rs         ← JwtSvid — token + claims
├── bundle/
│   └── x509.rs        ← X509Bundle — trust bundle for a trust domain
└── spiffe_id.rs       ← SpiffeId — parsed spiffe:// URI
← Week 3: Integration and Application

Key Types

use spiffe::{SpiffeId, X509Svid, X509Bundle};

// SpiffeId: parsed SPIFFE URI
let id: SpiffeId = "spiffe://example.org/service-a".parse()?;
println!("Trust domain: {}", id.trust_domain()); // "example.org"
println!("Path:         {}", id.path());          // "/service-a"

// X509Svid: the credential you receive from the Workload API
let svid: X509Svid = /* from WorkloadApiClient */;
println!("SPIFFE ID:  {}", svid.spiffe_id());
println!("Cert chain: {} certs", svid.cert_chain().len());
println!("Expiry:     {:?}", svid.leaf().not_after()); // x509 validity
println!("Key:        {} bytes", svid.private_key().as_ref().len());

// X509Bundle: the CA certs for a trust domain
let bundle: X509Bundle = /* from svid.trust_bundle() or client */;
println!("Trust domain: {}", bundle.trust_domain());
println!("Authorities:  {} certs", bundle.authorities().count());
← Week 3: Integration and Application

X509Source: The Production Pattern

For long-running services, use X509Source instead of WorkloadApiClient directly.
It maintains a continuously-updated SVID and trust bundle:

use spiffe::{X509Source, TrustDomain, SpiffeId};
use std::sync::Arc;

#[tokio::main]
async fn main() -> Result<()> {
    // Creates a Workload API connection and starts watching for updates
    let source = Arc::new(X509Source::default().await?);
    
    // Use the source in your TLS configuration
    // It refreshes SVIDs automatically — your TLS server picks up new certs
    // without restarting
    
    // Get current SVID
    let svid = source.get_x509_svid()?;
    println!("Current SPIFFE ID: {}", svid.spiffe_id());
    
    // Get trust bundle for a specific domain
    let bundle = source.get_bundle_for_trust_domain(
        &"example.org".parse::<TrustDomain>()?
    )?;
    
    Ok(())
}
← Week 3: Integration and Application

SpiffeId: Parsing and Validation

use spiffe::SpiffeId;

fn validate_spiffe_id(uri: &str) -> Result<SpiffeId> {
    let id: SpiffeId = uri.parse()
        .map_err(|e| format!("invalid SPIFFE ID: {e}"))?;
    
    // Trust domain checks
    assert_eq!(id.trust_domain().as_str(), "example.org");
    
    // Path checks
    assert!(id.path().starts_with("/service/"));
    
    Ok(id)
}

// Authorization check: is this the peer we expect?
fn authorize_peer(svid: &X509Svid, allowed_ids: &[&str]) -> bool {
    let peer_id = svid.spiffe_id().to_string();
    allowed_ids.iter().any(|allowed| peer_id == *allowed)
}
← Week 3: Integration and Application

Building a Robust SVID Module

// spiffe-demo/src/svid.rs

use spiffe::{SpiffeId, X509Svid, X509Bundle};

pub struct SvidInfo {
    pub spiffe_id: String,
    pub trust_domain: String,
    pub path: String,
    pub not_before: i64,
    pub not_after: i64,
    pub cert_size_bytes: usize,
    pub key_size_bytes: usize,
    pub bundle_cert_count: usize,
}

pub fn inspect(svid: &X509Svid, bundle: &X509Bundle) -> SvidInfo {
    let id = svid.spiffe_id();
    SvidInfo {
        spiffe_id:    id.to_string(),
        trust_domain: id.trust_domain().to_string(),
        path:         id.path().to_string(),
        not_before:   /* from svid.leaf().not_before() */,
        not_after:    /* from svid.leaf().not_after() */,
        cert_size_bytes:  svid.cert_chain().iter().map(|c| c.len()).sum(),
        key_size_bytes:   svid.private_key().as_ref().len(),
        bundle_cert_count: bundle.authorities().count(),
    }
}

pub fn is_authorized(svid: &X509Svid, allowed: &[SpiffeId]) -> bool {
    allowed.contains(svid.spiffe_id())
}
← Week 3: Integration and Application

Challenge Assignment

Implement a svid module in spiffe-demo with:

  1. SvidInfo struct (as sketched above)
  2. inspect(svid, bundle) -> SvidInfo function
  3. is_authorized(svid, allowed) -> bool function
  4. A spiffe-demo inspect subcommand that:
    • Fetches the live SVID from the Workload API (or uses the simulated one if no SPIRE running)
    • Prints all fields of SvidInfo in a formatted table
    • Prints "AUTHORIZED" or "UNAUTHORIZED" based on a hardcoded allowed list
  5. Parse the SPIFFE ID's path components and print each segment:
    e.g., spiffe://example.org/ns/prod/svc/provisioning["ns", "prod", "svc", "provisioning"]

Document: what happens when not_after is in the past?
Add a warning if the SVID is within 5 minutes of expiry.

← Week 3: Integration and Application

Resources

  • spiffe crate: docs.rs/spiffe
  • GitHub: github.com/spiffe/spiffe-rs — look at the examples/ directory
  • SpiffeId parsing: docs.rs/spiffe/latest/spiffe/struct.SpiffeId.html
  • X509Source: docs.rs/spiffe/latest/spiffe/workload_api/struct.X509Source.html