Skip to Content
GuidesWorkflowsEnrollmentSPIFFE/SPIRE Deployment Guide

SPIFFE/SPIRE Deployment Guide

SPIFFE (Secure Production Identity Framework For Everyone) with SPIRE provides automatic, cryptographically verifiable workload identities. This is the recommended identity provider for production deployments.

Overview

SPIFFE provides:

  • Automatic SVIDs: X.509 certificates issued automatically to workloads
  • Short-lived credentials: Certificates with hour-long TTLs, automatically rotated
  • Workload attestation: Identity based on process attributes, not secrets
  • Zero-trust networking: Cryptographic proof of workload identity

Architecture

┌──────────────────────────────────────────────────────────────┐ │ Trust Domain │ │ (spiffe://example.org) │ │ │ │ ┌─────────────┐ ┌─────────────────────────────────┐ │ │ │SPIRE Server │◄────────│ Registration DB │ │ │ │ (CA + API) │ │ (workload → SPIFFE ID mapping) │ │ │ └──────┬──────┘ └─────────────────────────────────┘ │ │ │ │ │ │ Node Attestation │ │ │ (join token, x509pop) │ │ ▼ │ │ ┌─────────────┐ │ │ │ SPIRE Agent │ ◄── Workload API ──► Access Point │ │ │ (per node) │ (ah agent access-point)│ │ └──────┬──────┘ │ │ │ │ │ │ Workload Attestation │ │ │ (unix:uid, docker:label) │ │ ▼ │ │ ┌─────────────┐ │ │ │ Executor │ ◄── Workload API ──► SVID │ │ │ (ah agent) │ (X.509 certificate) │ │ └─────────────┘ │ └──────────────────────────────────────────────────────────────┘

NixOS Deployment

Agent Harbor provides NixOS modules for SPIRE deployment:

SPIRE Server

# /etc/nixos/configuration.nix { imports = [ /path/to/agent-harbor/nix/nixos-modules ]; services.spire.server = { enable = true; trustDomain = "example.org"; bindAddress = "0.0.0.0"; bindPort = 8081; dataStore = { type = "sqlite3"; connectionString = "/var/lib/spire/server/datastore.sqlite3"; }; nodeAttestors = { joinToken.enable = true; }; # Declarative registration entries registrationEntries = [ { parentId = "spiffe://example.org/spire/agent/join_token/access-point-1"; spiffeId = "spiffe://example.org/ah/serve"; selectors = [ "unix:user:agent-harbor" ]; } { parentId = "spiffe://example.org/spire/agent/join_token/executor-1"; spiffeId = "spiffe://example.org/ah/agent/executor-1"; selectors = [ "unix:user:executor" ]; } ]; }; # Open firewall for SPIRE server networking.firewall.allowedTCPPorts = [ 8081 ]; }

SPIRE Agent

# On executor nodes { services.spire.agent = { enable = true; trustDomain = "example.org"; serverAddress = "spire-server.example.org"; serverPort = 8081; # For production, provide a trust bundle trustBundlePath = /path/to/trust-bundle.pem; # Or for development: # insecureBootstrap = true; joinToken = "your-join-token-here"; # Or use secrets management: # joinTokenFile = config.sops.secrets."spire/join-token".path; workloadAttestors = { unix.enable = true; }; }; }

Manual Deployment

1. Deploy SPIRE Server

# Download SPIRE curl -sL https://github.com/spiffe/spire/releases/download/v1.14.1/spire-1.14.1-linux-amd64-musl.tar.gz | tar xz # Create server configuration cat > /etc/spire/server.conf << EOF server { bind_address = "0.0.0.0" bind_port = "8081" socket_path = "/run/spire/server.sock" trust_domain = "example.org" data_dir = "/var/lib/spire/server" log_level = "INFO" } plugins { DataStore "sql" { plugin_data { database_type = "sqlite3" connection_string = "/var/lib/spire/server/datastore.sqlite3" } } NodeAttestor "join_token" { plugin_data {} } KeyManager "disk" { plugin_data { keys_path = "/var/lib/spire/server/keys.json" } } } EOF # Start SPIRE server spire-server run -config /etc/spire/server.conf

2. Generate Join Token

spire-server token generate \ -socketPath /run/spire/server.sock \ -ttl 3600 # Output: Token: abc123def456...

3. Deploy SPIRE Agent

# Create agent configuration cat > /etc/spire/agent.conf << EOF agent { data_dir = "/var/lib/spire/agent" log_level = "INFO" server_address = "spire-server.example.org" server_port = "8081" socket_path = "/run/spire/agent.sock" trust_domain = "example.org" join_token = "abc123def456..." insecure_bootstrap = true # Remove in production } plugins { NodeAttestor "join_token" { plugin_data {} } KeyManager "disk" { plugin_data { directory = "/var/lib/spire/agent" } } WorkloadAttestor "unix" { plugin_data { discover_workload_path = true } } } EOF # Start SPIRE agent spire-agent run -config /etc/spire/agent.conf

4. Create Registration Entries

# Get agent SPIFFE ID AGENT_ID=$(spire-server agent list -socketPath /run/spire/server.sock | grep -oP 'spiffe://[^\s]+' | head -1) # Register access point workload spire-server entry create \ -socketPath /run/spire/server.sock \ -parentID "$AGENT_ID" \ -spiffeID "spiffe://example.org/ah/serve" \ -selector "unix:user:agent-harbor" # Register executor workload spire-server entry create \ -socketPath /run/spire/server.sock \ -parentID "$AGENT_ID" \ -spiffeID "spiffe://example.org/ah/agent/executor-1" \ -selector "unix:user:executor"

5. Configure Agent Harbor

# Access point ah agent access-point \ --fleet-listen 0.0.0.0:4433 \ --server-identity spiffe \ --spiffe-socket /run/spire/agent.sock \ --executor-san-uri-prefix "spiffe://example.org/ah/agent/" # Executor ah agent enroll \ --remote-server https://access-point.example.org:4433 \ --identity spiffe \ --spiffe-socket /run/spire/agent.sock \ --expected-server-id "spiffe://example.org/ah/serve"

Workload Selectors

SPIRE identifies workloads using selectors. Common selectors:

Selector TypeExampleDescription
unix:uidunix:uid:1000Process UID
unix:gidunix:gid:100Process GID
unix:userunix:user:agent-harborUsername
unix:groupunix:group:wheelGroup name
unix:pathunix:path:/usr/bin/ahExecutable path
docker:labeldocker:label:app:myappDocker label
k8s:pod-labelk8s:pod-label:app:myappKubernetes label

Certificate Rotation

SPIFFE SVIDs are automatically rotated:

  1. SPIRE agent requests new SVIDs before expiry
  2. Agent Harbor watches the SVID stream
  3. New connections use fresh certificates
  4. Existing connections continue until closed

Default TTL is 1 hour; configure on server:

server { default_x509_svid_ttl = "4h" # Longer TTL }

High Availability

For production HA:

Federated SPIRE

Multiple SPIRE servers can federate across regions:

server { federation { bundle_endpoint { address = "0.0.0.0" port = 8443 } } }

PostgreSQL Backend

Use PostgreSQL for multi-server deployments:

services.spire.server = { dataStore = { type = "postgres"; connectionString = "postgres://spire:password@db:5432/spire?sslmode=require"; }; };

Troubleshooting

Agent Can’t Connect to Server

error: failed to connect to spire server

Solutions:

  • Check firewall allows port 8081
  • Verify server address and port
  • Check join token is valid and not expired

No SVID for Workload

error: no identity issued

Solutions:

  • Check registration entry exists: spire-server entry show
  • Verify selectors match the workload
  • Check agent is attested: spire-server agent list

SPIFFE ID Mismatch

error: server SPIFFE ID mismatch: expected spiffe://example.org/ah/serve

Solutions:

  • Verify access point’s registration entry
  • Check --expected-server-id flag matches

Next Steps