Cheat sheet — Workload Identity & mTLS (SPIFFE / SPIRE)¶
Companion to Module 10 — Workload Identity & mTLS (SPIFFE/SPIRE) · CC BY 4.0 — print it, pin it, share it.
Last reviewed: 2026-07
The vocabulary¶
SPIFFE ID spiffe://corp.local/ledger — a workload's identity, a URI
SVID the SPIFFE ID wrapped in a short-lived X.509 cert (or JWT), signed by the trust-domain CA
trust domain corp.local — the CA root everyone in the domain validates against
Workload API the local socket a workload calls to fetch its own SVID at runtime
selector an unforgeable property the agent observes (unix:uid, k8s:sa, docker:image-id)
registration entry selectors → SPIFFE ID (the workload-identity equivalent of an IAM policy)
SPIRE server¶
spire-server run -config server.conf # start the server (the CA + registry)
spire-server healthcheck
spire-server token generate -spiffeID spiffe://corp.local/agent # join token for node attestation
spire-server bundle show # the trust bundle (CA roots) to distribute
# register a workload: bind a SPIFFE ID to hard-to-spoof selectors
spire-server entry create \
-spiffeID spiffe://corp.local/ledger \
-parentID spiffe://corp.local/agent \
-selector docker:image_id:sha256:<digest> # pin to the IMAGE DIGEST, not a settable label
spire-server entry show # list registration entries
SPIRE agent + workload¶
spire-agent run -config agent.conf -joinToken <TOKEN> # node attestation via the join token
spire-agent healthcheck
# a workload fetches its OWN identity off the Workload API (no secret shipped to it)
spire-agent api fetch x509 -write /tmp/svid/ # writes svid.pem, key.pem, bundle.pem
spire-agent api fetch jwt -audience ledger # JWT-SVID for a given audience
Verify the mTLS + inspect the SVID¶
# read the SPIFFE ID out of the cert (it's in the SAN URI)
openssl x509 -in /tmp/svid/svid.0.pem -noout -text | grep -A1 "Subject Alternative Name"
openssl x509 -in /tmp/svid/svid.0.pem -noout -enddate # confirm the SHORT TTL (minutes, not months)
# mutual TLS handshake: BOTH sides present + validate a cert
openssl s_client -connect ledger:8443 \
-cert /tmp/svid/svid.0.pem -key /tmp/svid/key.0.pem \
-CAfile /tmp/svid/bundle.0.pem # verify return code should be 0 (ok)
Prove the boundary (the deny)¶
# a workload with NO matching registration entry must be refused an identity
spire-agent api fetch x509 -write /tmp/nope/ # from an unregistered process → no SVID, error out
# → and therefore the mTLS handshake cannot complete: identity, not network position, grants access
Gotchas worth remembering¶
- The security control is "get the attestation right," not "protect the key." There's no long-lived
key to steal — the risk moves to the selectors. A selector that's too loose (
unix:uid:0, a label any deployment can set) lets the wrong workload claim an identity — the workload-identity equivalent of a wildcard IAM policy. Ask of every selector: could a different workload satisfy this? - Pin to genuinely-unforgeable selectors. Prefer an image digest or a namespace-bound Kubernetes service account over a shared UID or a free-form Docker label. The model will happily pick a selector that works without seeing it's forgeable.
- Keep SVID TTLs short. A leaked SVID should expire in minutes; the agent auto-rotates. Long TTLs re-create the long-lived-secret problem SPIFFE exists to kill.
- mTLS means BOTH ends present and validate a cert. Client proves itself to server and server to client, each reading the peer's SPIFFE ID out of the SAN — no shared secret anywhere.
- SPIFFE is authentication, not authorization. It proves which workload is calling and encrypts
the channel; whether
spiffe://.../webmay callspiffe://.../ledgeris a policy question — the handoff to OPA (Module 08). Identity here, decision there. - No bootstrap secret is shipped to the workload. Its identity is derived from what it demonstrably is (node attestation, then workload attestation) — which is why an unregistered workload simply gets nothing.
Comments
Sign in with GitHub to comment. Choose the type: Feedback (errors or suggestions on this page) · Hints (help for fellow learners — no spoilers) · General (anything else).