Skip to content
SRE & DevOpsDeep Dive Published Updated 6 min readViews unavailable

SPIFFE/SPIRE Workload API: Local Attestation and SVID Rotation

Trace how SPIRE binds process selectors to workload identities, delivers SVIDs over its local Workload API, and replaces credentials safely.

SPIFFE gives a workload a portable identifier and a credential format; SPIRE is one system that attests workloads and operates the local endpoint through which they obtain those credentials. This division matters operationally. A workload does not normally ask a remote certificate authority to trust a name it supplied. A node agent first establishes its own identity with the SPIRE Server, then identifies local processes and requests only the identities allowed by registration policy.

The SPIFFE Workload API is the delivery and update contract between the local process and that agent. It is not the same mechanism as AKS or GKE federating a Kubernetes service-account token to a cloud identity provider. SPIRE’s main boundary is local process attestation plus a server-maintained registry; cloud federation may still be used downstream, but it is a separate trust exchange.

Keep node and workload attestation separate

A SPIRE Server is the signing authority for its trust domain and stores registration entries. A SPIRE Agent runs on a node, authenticates that node through a configured node-attestation method, and exposes the Workload API locally. When a process connects, the agent’s workload-attestor plugins gather attributes from the operating system or platform. The agent compares those selectors with cached registration entries and obtains the matching SVIDs from the server.

These are two decisions, not one. Node attestation asks whether this agent belongs in the trust domain. Workload attestation asks which registered workload, if any, is making this local request. A successfully enrolled node does not give every process on it the same identity. Conversely, a matching process selector does not bypass the requirement that the parent agent be authorized to serve that workload.

A registration entry combines a SPIFFE ID, a parent ID, and one or more selectors. In a disposable Unix-host lab, an entry can be scoped to the agent identity and a test account:

spire-server entry create \
  -parentID spiffe://lab.example/agent/test-node \
  -spiffeID spiffe://lab.example/workload/ledger \
  -selector unix:uid:1001

Replace the parent with the agent’s actual SPIFFE ID and confirm the test account’s numeric UID before using the entry. A UID selector identifies a class of processes running under that UID; it does not distinguish two binaries that share the account. If that is too broad for the threat model, choose platform selectors and process-isolation controls that express the intended boundary rather than treating the SPIFFE ID as proof of a particular executable.

A local socket still needs an identity policy

On Unix, the agent exposes a Unix-domain socket. The socket path and its filesystem permissions determine which processes can reach the endpoint, but reachability alone is not a workload identity. The SPIFFE Workload Endpoint contract requires the implementation to identify callers out of band. SPIRE’s Unix workload attestor can use operating-system metadata such as UID and GID; Kubernetes deployments use a different attestor and selector vocabulary.

This also makes socket distribution security-sensitive. Mounting the agent socket into a container gives that container a path to request identity, while the agent’s attestor and registration entries decide what it can receive. Mount only the intended socket, keep filesystem access narrow, and test the identity from inside the actual workload container rather than from an administrator shell. An operator’s ability to connect says little about what a restricted service account will receive.

For an observation-only lab, run the agent’s watcher as the test account and point it at the configured socket:

sudo -u ledger ./bin/spire-agent api watch \
  -socketPath /run/spire/sockets/agent.sock

The command is useful for seeing whether X.509-SVID updates arrive, but it is not a complete authorization test. Confirm the agent’s registration entry, run the application under its production-equivalent UID or container identity, and exercise the application’s own client library. Avoid exporting private keys to shared temporary directories merely to prove that the socket works.

Treat a stream response as the complete current state

The X.509-SVID Workload API uses a server stream. A client should keep its connection open and reconnect if it is terminated. Each response represents the full authorized set at that point in time, not a patch that can be appended to an old cache. That distinction is important during rotation and revocation: if an identity is omitted from a later response or becomes redacted, the client should stop using it instead of retaining stale credentials indefinitely.

Build the client around an explicit replacement operation. Parse and validate a new response, prepare the new TLS material, then atomically swap the active certificate and trust-bundle view. Keep the old context only for the bounded overlap needed to finish in-flight requests; do not preserve it because an update happened to omit a previously present field. On stream failure, reconnect with bounded backoff and expose the last successful update time as health data. A live TCP connection does not prove that the client has fresh identity material.

JWT-SVID retrieval has a different shape. The request must include the audience for the service the workload intends to authenticate to, and the JWT-SVID response is not the same long-lived X.509 update stream. Treat audience selection as part of the authorization contract; do not reuse a token intended for one relying party at another. Likewise, do not assume that a valid SPIFFE ID grants application-level permissions. The peer still validates the SVID and applies its own authorization policy.

A practical failure-isolation sequence

When a process receives PermissionDenied, first verify that it can reach the intended socket, then record its actual UID or platform selectors without logging tokens or private keys. Check that the entry’s parent ID matches the node agent and that every selector reflects the deployed process. Next inspect agent/server synchronization and the trust domain, then test the same request with the supported SPIRE client library. An empty or stale application TLS cache is a client lifecycle problem even when the agent is healthy.

For rotation tests, record certificate validity intervals and the time the client installs each complete update. Exercise a new certificate, a removed identity, a socket restart, and a server interruption in a non-production trust domain. Confirm that the client reconnects and ceases using redacted material. Keep traces limited to SPIFFE IDs, selector names that are approved for logs, timestamps, and error codes; the Workload API response can contain private key material.

SPIRE’s Workload API is most useful when teams preserve the whole chain: node enrollment, process attestation, selector scope, local socket access, client update handling, and peer authorization. The credential is only as specific as the registration rule that issued it, and rotation is only complete when the application replaces the state it actually uses.

Related:

Sources:

Comments