Skip to main content
RunBook Academy

Secrets, PKI & CertificatesXIII · Dynamic Credentials and Workload IdentityDynamicCredentials

SPIFFE and SPIRE: a universal identity namespace and what it costs to run one

Advanced⏱ ~24 minspire-agent

What you'll learn

  • Write a valid SPIFFE ID and identify the constraints that make one invalid
  • Choose between the SVID formats from their documented security properties
  • Explain how the Workload API identifies a caller that presents no credential
  • Decide whether an estate should adopt SPIRE, and justify a decision not to

Prerequisites

Verified against OpenSSL 3.5.x teaching target; 3.0+ minimum · OpenSSH 10.x teaching target; 8.2+ minimum for certificate workflows · OpenBao 2.6.x · Smallstep step-ca 0.30.x · Certbot / Pebble Certbot current release; Pebble 2.10.x ACME test server · Kubernetes (cross-course target) 1.36.x · PostgreSQL 17.x · 2026-08-26

Not yet marked complete on this device.

SPIFFE is a specification for naming workloads and for the documents that carry those names. SPIRE is one implementation of it. Keeping the two apart matters, because the specification is small, stable and cheap to adopt, while the implementation is a stateful control plane with an upgrade policy and a certificate authority you now operate. A team that adopts the first without noticing it has signed up for the second tends to discover the difference during an incident.

What a SPIFFE ID is, and what makes one invalid

A SPIFFE ID is a URI. The scheme is spiffe, the authority is a trust domain and nothing else, and the path names a workload within that domain:

spiffe://prod.example.com/ns/analytics/sa/reporting
spiffe://prod.example.com/web-01/reporting

The constraints are stricter than a general URI and each of them exists to remove ambiguity from comparison. The userinfo and port components must be empty, so no credentials or ports may hide in the authority. The host must be lowercase and may contain only letters, numbers, dots, dashes and underscores, with no percent-encoded characters anywhere in the URI. Path segments must not be empty and must not be a single or double dot. There is no query and no fragment. Implementations must support identifiers up to 2048 bytes, and a trust domain is capped at 255 bytes.

One asymmetry catches people. The scheme and the trust domain are compared case insensitively, but the path is case sensitive. A workload named with a capital letter in its path is a different workload from the same name in lower case, and no component will normalise that for you.

The trust domain is the unit of trust, not an organisational label. Two workloads in the same trust domain share a root of trust; two in different domains do not until somebody federates them deliberately. Choosing trust domain boundaries is therefore a security decision made early and changed painfully, and the usual mistake is to create one domain per team when the real boundary is per environment or per organisation.

The three SVID formats

An SVID is a document that carries a SPIFFE ID and proves the bearer is entitled to it. There are three, and material written before the third appeared describes only two.

The X509-SVID is the one to reach for. The SPIFFE ID lives in a URI-type Subject Alternative Name entry, not in the Common Name and not in a custom extension, and there must be exactly one URI SAN in the certificate. Anything presenting two is invalid and must be rejected. The subject is optional, and when it is omitted the SAN extension must be marked critical, which is the same rule this course met when inspecting a certificate with an empty subject. A leaf sets basic constraints with the CA field false, must carry a critical key usage extension asserting digital signature, and must not assert certificate signing or CRL signing. Where extended key usage is present it carries both server and client authentication, because a workload is usually both. One structural rule catches out anyone building a signing hierarchy by hand: a leaf identifier must have a non-root path, and a signing certificate’s identifier must have no path at all.

The JWT-SVID exists for the cases mTLS cannot reach, such as a connection through an application-layer proxy that terminates TLS. The identity sits in the subject claim. The audience claim is mandatory and a validator must reject a token that lacks one or whose audience does not include the validator. The expiry claim is mandatory too, while issued-at and issuer are not required at all, so a validator that keys off the presence of an issuer will reject perfectly valid tokens. The signing algorithm set is restricted, and any other algorithm must be refused.

The specification is candid about the trade. JWT-SVIDs are susceptible to replay, and a token carrying two audiences allows the holder of one of them to impersonate the original workload to the other. That is why the third format exists: the WIT-SVID, still marked as incubating, binds a public key into the token and must not be used as a bearer token at all, being presented instead with a proof of possession of that key.

The Workload API asks the workload for nothing

The mechanism that makes this a genuine answer to secret zero is the Workload API, and its most important property is a negative one. A calling workload needs no token, no secret, and no knowledge of its own identity. The endpoint specification mandates the absence of direct client authentication and puts the burden on the implementation to identify the caller itself, through kernel introspection or by interrogating the local orchestrator. The check must not require the workload to participate, because a check a process can influence is a check it can lie to.

export SPIFFE_ENDPOINT_SOCKET=unix:///tmp/spire-agent/public/api.sock

The endpoint is discovered through that environment variable, which holds a URI with either a unix or a tcp scheme. A Unix domain socket is the correct answer in almost every case, and the specification restricts the TCP form to networks where the endpoint can strongly authenticate the caller by source address. Every client request must carry the static gRPC metadata key workload.spiffe.io with the value true, and a request without it must be rejected, which is the first thing to check when a hand-written client is refused for no visible reason.

The RPCs are server-streaming and each message is a complete snapshot rather than a delta, and clients are expected to hold the connection open for as long as they reasonably can. That is why an application using this properly never polls for a renewed certificate: it receives a new one on the stream before the old one matters.

flowchart TD
    A["Platform evidence\ninstance document, projected token, device key"] --> B["Node attestation\nagent proves which node it runs on"]
    B --> C["Agent trusted\nfor that node only"]
    D["Process connects\nto the local socket"] --> E["Workload attestation\nkernel and kubelet interrogated"]
    C --> F["SVID issued\nfor the matched registration entry"]
    E --> F

Identity is assembled from two independent observations. The platform vouches for the node, the node vouches for the process, and a registration entry says which SPIFFE ID that combination is entitled to. Registration entries are the piece people underestimate: every workload identity is a record that somebody has to create, update and eventually remove, and a stale entry is a standing grant to whatever now matches it.

Rotation, and the numbers that govern it

Rotation is automatic and does not wait for expiry. The default behaviour renews at half of the credential’s lifetime with jitter applied, the jitter existing specifically so that a large fleet does not renew in lockstep. An alternative strategy expresses the goal directly as an availability target, rotating when the remaining lifetime falls to the amount of control-plane downtime you want to survive, and it requires at least twelve hours of grace between the lifetime and the target for the guarantee to hold.

ca_ttl                = "72h"
default_x509_svid_ttl = "6h"
default_jwt_svid_ttl  = "5m"

Those are the shipped sample values, and the ratio between the first two is the part to think about rather than copy. A signing key that expires too soon relative to the credentials it issues cannot give those credentials their full nominal lifetime, and the server warns when the configuration would do that. No fixed multiplier is stated normatively, so treat the sample ratio as a starting point and read the warning rather than trusting a rule of thumb. Note also that the older combined setting for credential lifetime was removed several releases ago; a configuration sample using it will not start.

The operational cost, stated plainly

Adopting SPIRE means taking on a highly available, stateful control plane with a datastore, and putting it in the critical path of every workload’s ability to authenticate. It means an agent on every node, running privileged enough to read kernel and kubelet state. It means operating a certificate authority, with rotation ratios you have to get right. It means a registration-entry lifecycle that somebody owns. And it means the upgrade cadence above, indefinitely.

Two limits deserve to be stated because they are frequently misrepresented in adoption proposals. SPIFFE is authentication, not authorisation: it tells a service who is calling, and deciding what that caller may do remains entirely your problem, since registration entries are issuance policy rather than access policy. And it is not a secrets manager. Database credentials, third-party API keys and encryption keys still need the machinery of the earlier lessons in this part. What changes is how a workload authenticates to that machinery, which is genuinely valuable and is not the same as replacing it.

When an infrastructure team should not adopt SPIRE

Say no in these situations, and say it early.

  • You are single-cloud and the platform already has workload identity. The native mechanism gives you the same property with no control plane, no certificate authority and no upgrade treadmill. Exhaust the federation pattern from the previous lesson first; it usually gets most of the benefit with no infrastructure at all.
  • Your actual problem is human or static secrets. Shared administrative passwords, credentials to third-party services and encryption keys are not what this solves. Adopting it will not reduce that inventory by one entry.
  • You cannot staff the operation. A poorly run identity control plane is a single point of authentication failure for everything that depends on it, which is a worse position than the one you started in.
  • The estate is small and homogeneous. Below a few dozen services on one runtime with no cross-boundary requirement, the registration-entry overhead outweighs what you gain.

The case for adoption is genuinely strong in the opposite conditions: multiple clouds or a hybrid estate, identity that must cross organisational boundaries, and a scale at which per-platform mechanisms have stopped composing. Federation between trust domains is the feature that earns the cost, publishing each domain’s trust bundle at an endpoint the other retrieves, with per-entry opt-in rather than ambient trust between domains.

Production discipline

  1. Fix trust domain boundaries before issuing anything. They are a security boundary and changing one later means reissuing every identity inside it.
  2. Read the SVID structure when debugging, not the subject. The identity is in the single URI SAN entry, and a certificate with a familiar-looking Common Name and no URI SAN is not an SVID.
  3. Treat registration entries as inventory. Give each one an owner and a review date, and remove entries for workloads that no longer exist before something else matches them.
  4. Plan the upgrade cadence as recurring work. No minor-version skipping and a one-minor skew ceiling make a neglected deployment expensive to recover rather than merely out of date.
  5. Write down what it does not do. State in the adoption document that authorisation and secret storage remain separate problems, so nobody plans to decommission the secret manager.

Cross-course references

  • Kubernetes for Production Sysadmins - Part CXIV (TLS) covers in-cluster mutual TLS, which is the transport that carries an X509-SVID in most deployments.
  • Observability for Production Sysadmins - Part LXVII (HA) covers running a stateful control plane without single points of failure, which is the requirement an identity server inherits.
  • Linux for Production Sysadmins - Part LXXI (TLS) covers the certificate handling on the host, including the socket permissions that decide which local processes can reach an agent.

Quiz

Knowledge check · 4 questions

  1. Q1. Where does the SPIFFE ID appear in an X509-SVID, and how many may there be?

  2. Q2. SVIDs are rotated part-way through their lifetime with jitter applied, rather than being replaced when they expire.

  3. Q3. A workload calls the Workload API over a Unix socket and is rejected immediately, before any attestation appears in the logs. Name the likely omission and the property of the API that explains why no credential was needed.

  4. Q4. Give a recommendation and the reasoning behind it.

    A team of four runs eighteen services on one managed Kubernetes cluster in a single cloud account. They propose adopting SPIRE to remove the last AppRole SecretIDs from their deployments, and to give every service an mTLS identity. They have no second cloud, no on-premises estate and no partner integrations. Their remaining static secrets are two vendor API keys and a shared administrative password for a legacy appliance.

Passing score: 75%. Answers are checked in this browser.