Production Reference

SPIFFE & SPIRE Cheatsheet

Operational reference for SPIFFE workload identity and SPIRE deployment. SPIFFE IDs, SVID issuance, registration entries, attestor selectors, federation, and the diagnostic commands you reach for when a workload is not getting its identity.

Command-firstProduction notesSecurity warningsHardened patterns

SPIFFE ID structure

4 commands
spiffe://<trust-domain>/<path>

Canonical SPIFFE ID format. Trust domain is your organisation; path identifies the workload.

Production note: Use a hierarchical path that mirrors deployment scope: /ns/<namespace>/sa/<service-account> on Kubernetes.

spiffe://corp.example.com/ns/payments/sa/payments-api

Production-style SPIFFE ID. Encodes cluster, namespace, and service account in the path so authz can prefix-match.

Production note: Pick the trust domain once; changing it later requires re-issuing every SVID across the fleet.

X.509 SVID

X.509 certificate whose URI SAN is the SPIFFE ID. Used for mTLS handshakes.

Production note: Default TTL in SPIRE is 1 hour with rotation at half-life. Tune via -ttl on the registration entry.

JWT SVID

Signed JWT with the SPIFFE ID in the `sub` claim. For HTTP/gRPC services that cannot terminate TLS themselves.

Warning: JWT SVIDs are bearer tokens — anyone who steals them can impersonate the workload until they expire. Prefer X.509 mTLS where possible.

SPIRE Server: cluster bootstrap

4 commands
spire-server run -config server.conf

Start the SPIRE Server with the given config file. Server holds the trust domain CA and issues SVIDs.

Production note: Run as a StatefulSet on Kubernetes with persistent storage for the datastore. HA needs an external SQL backend.

spire-server token generate -spiffeID spiffe://corp.example.com/spire/agent/<id>

Generate a one-time join token to bootstrap a SPIRE Agent. Use only when no native attestation method is available.

Warning: Join tokens are bearer secrets. Prefer node-level attestation (k8s_psat, aws_iid, gcp_iit, k8s_sat) in production.

spire-server bundle show

Print the trust bundle (CA chain) for this trust domain. Federate it to peer trust domains.

Production note: Combine with the Bundle Endpoint protocol (`-format pem` for static seeding).

spire-server healthcheck

Liveness probe. Exits 0 when the server is ready to serve.

Production note: Wire into Kubernetes liveness/readiness probes. Combine with prometheus_metrics for SLOs.

SPIRE Agent: workload attestation

4 commands
spire-agent run -config agent.conf

Start the SPIRE Agent on a node. Attests workloads via local OS or Kubernetes signals and proxies the Workload API.

Production note: Deploy as a DaemonSet; mount /run/spire/agent.sock as the Workload API endpoint for pods.

spire-agent api fetch x509

Fetch the current X.509 SVID for the workload calling the Workload API. Used for diagnostics.

Production note: In code, use the go-spiffe SDK (workloadapi.NewX509Source) so rotation is automatic.

spire-agent api fetch jwt -audience https://api.example.com

Mint a JWT SVID with the given audience. Audience is the receiving service URL.

Warning: Always validate `aud` on the receiving end — a JWT minted for service A must not be accepted by service B.

spire-agent healthcheck

Verify the agent has registered with the server and is serving the Workload API.

Production note: If agent healthcheck passes but workloads cannot fetch SVIDs, the registration entries are missing or the selectors do not match.

Registration entries (workload identity assignment)

4 commands
spire-server entry create -spiffeID <id> -parentID <parent> -selector <key:val>

Create a registration entry. The agent issues the SVID to any workload that matches all selectors AND descends from the parent.

Production note: Combine multiple selectors (k8s:ns + k8s:sa + k8s:image-id) so identity binds to the specific deployment, not just the namespace.

spire-server entry list

Show all registration entries on this server. Pipe through grep to find the entry for a specific SPIFFE ID.

Production note: In production, manage entries declaratively with the SPIRE Controller Manager (CRDs ClusterSPIFFEID + ClusterFederatedTrustDomain) instead of imperative CLI.

spire-server entry update -entryID <id> -ttl 600

Update an existing entry. -ttl sets SVID validity in seconds (default 3600).

Production note: Shorter TTLs reduce blast radius of leaked SVIDs but increase load on SPIRE Server. 600–3600s is typical for production.

spire-server entry delete -entryID <id>

Delete a registration entry. Workload loses identity at next refresh.

Warning: Plan deletes carefully — services using the SVID will fail TLS handshakes within minutes.

Attestor selectors (Kubernetes Workload Attestor)

5 commands
k8s:ns:<namespace>

Match pods in a specific Kubernetes namespace.

Warning: Single-selector entries on namespace alone match every pod in the namespace — a misplaced pod gets the SVID.

k8s:sa:<service-account>

Match pods running under a specific ServiceAccount.

Production note: Combine with k8s:ns: for tenant + role scoping.

k8s:pod-label:<key>:<value>

Match pods with a specific label.

Production note: Use for "app=payments-api" style identity binding when ServiceAccounts are shared.

k8s:container-image:<image>

Match pods running a specific container image (by digest).

Production note: Strongest selector — binds identity to the exact image hash. Combine with k8s:ns: and k8s:sa: for defence in depth.

unix:uid:<uid> / unix:path:<path>

Unix Workload Attestor selectors. Match the calling process by UID, path, or SHA256.

Production note: Use for VM / non-Kubernetes workloads. unix:sha256:<digest> binds identity to the specific binary.

Federation (cross trust domain)

3 commands
spire-server federation create -trustDomain peer.example.com -bundleEndpointURL https://peer.example.com:8443 -bundleEndpointProfile https_web

Federate with another trust domain. SPIRE will fetch the peer's trust bundle dynamically over the bundle endpoint.

Production note: https_web uses public CA validation for the bundle endpoint (simplest). https_spiffe uses a SPIFFE ID for endpoint authentication (most secure).

spire-server federation list

Show configured federation relationships.

Production note: Workloads can authenticate peers in any federated trust domain — combine with authz policy that allowlists the peer SPIFFE IDs you trust.

spire-server bundle set -id spiffe://peer.example.com -path peer-bundle.pem

Static seed of a peer trust bundle. One-time bootstrap before bundle endpoint takes over.

Warning: Static bundles go stale. Always pair with bundle endpoint for ongoing rotation.

Diagnostics ("my workload cannot get an SVID")

4 commands
spire-server entry show -spiffeID spiffe://corp.example.com/ns/payments/sa/payments-api

Confirm the registration entry exists with the expected selectors.

Production note: If empty: the entry was never created (most common cause).

kubectl logs -n spire spire-agent-xxx | grep "Workload"

Tail the SPIRE Agent on the node where the workload is running. Look for "Workload not registered" or selector-mismatch messages.

Production note: Run on the right node — DaemonSet means there is one agent per node.

spire-agent api fetch x509 -socketPath /run/spire/agent.sock

From inside the workload pod (or via kubectl exec), call the Workload API directly to see what (if anything) the agent issues.

Production note: If empty here but entry exists: selectors do not match. Compare actual pod labels/SA to what the entry expects.

spire-server entry list -selector k8s:ns:payments

List all entries scoped to a namespace. Useful when entries were created with overly broad selectors.

Warning: If an entry matches more workloads than intended, every match gets the same SVID — review selectors carefully.

Hardened patterns

Common misconfigurations

The unsafe pattern, the replacement, and the reason the two are not equivalent in production.

FIXReview

Risky

spire-server entry create \
  -spiffeID spiffe://example.com/payments-api \
  -selector k8s:ns:payments

Hardened

spire-server entry create \
  -spiffeID spiffe://example.com/ns/payments/sa/payments-api \
  -selector k8s:ns:payments \
  -selector k8s:sa:payments-api \
  -selector k8s:container-image:registry/payments-api@sha256:abc...

Why it matters: A single namespace selector matches every pod in the namespace. Combining ns + ServiceAccount + image-digest binds identity to the specific deployment, so a misplaced or compromised pod cannot acquire an unintended SVID.

FIXReview

Risky

// Read SVID once at startup, never refresh:
src, _ := workloadapi.NewX509Source(ctx)
cert := src.GetX509SVID()
tlsConfig := &tls.Config{
  Certificates: []tls.Certificate{cert.Certificate()},
}

Hardened

// Use the live source for every handshake:
src, _ := workloadapi.NewX509Source(ctx)
tlsConfig := tlsconfig.MTLSServerConfig(src, src,
  tlsconfig.AuthorizeAny())

Why it matters: GetX509SVID() returns a snapshot; the tlsconfig helpers install GetCertificate / GetClientCertificate callbacks that read from the live source on each handshake. Without that, SVID rotation has no effect — the server keeps presenting the cert it captured at startup.

FIXReview

Risky

// Static bundle seed only:
spire-server bundle set \
  -id spiffe://us-west.example.com \
  -path us-west-bundle-2024-Q3.pem
# (re-applied manually every quarter)

Hardened

spire-server federation create \
  -trustDomain us-west.example.com \
  -bundleEndpointURL https://spire.us-west.example.com:8443 \
  -bundleEndpointProfile https_spiffe \
  -endpointSpiffeID spiffe://us-west.example.com/spire/server

Why it matters: Static bundle copies go stale on rotation. The bundle endpoint protocol fetches the federated trust bundle dynamically (default every 5 minutes), so a CA rotation propagates within minutes — no manual ops, no 3am incident when the upstream CA rolls.

Go deeper