Bridge Human Identity with SPIFFE Workload Identity
Federate Vouch with SPIRE to bridge human and workload identity
SPIFFE gives every workload a cryptographic identity, but it does not address who deployed the workload or who authorized the action. Because Vouch is a standards-compliant OIDC provider, you can configure SPIRE (the SPIFFE reference implementation) to trust Vouch-issued tokens – bridging human and workload identity in a single architecture.
How it works
Vouch and SPIRE operate at different layers of the identity stack:
Human layer (Vouch) Workload layer (SPIFFE/SPIRE)
───────────────────── ─────────────────────────────
YubiKey tap Workload attestation
→ Vouch OIDC ID token → X.509-SVID or JWT-SVID
→ kubectl, AWS, SSH, etc. → mTLS, service-to-service auth
The integration points:
- SPIRE trusts Vouch as an OIDC issuer — SPIRE validates Vouch tokens using the
/.well-known/openid-configurationand JWKS endpoints to make authorization decisions based on hardware-verified human identity. - Workloads get SPIFFE SVIDs — SPIRE issues short-lived X.509 or JWT credentials to workloads via the Workload API, independent of any human session.
- Both coexist in the same infrastructure — Humans authenticate with
vouch login+ YubiKey; services authenticate with SPIFFE SVIDs. Downstream systems (Kubernetes, AWS, databases) can accept both.
Prerequisites
- Vouch CLI installed and enrolled — Complete the Getting Started guide.
- SPIRE Server and Agent deployed — See the SPIRE Getting Started guide or the Kubernetes quickstart.
spire-serverandspire-agentCLI tools available on your path.
Configure SPIRE to trust Vouch tokens
This is the integration itself: SPIRE validates Vouch OIDC tokens so that human identity can inform workload registration and authorization decisions.
Register Vouch as a federated trust domain
Add a federation block to your SPIRE Server config so SPIRE automatically fetches and refreshes Vouch’s signing keys:
# spire-server.conf
server {
trust_domain = "example.org"
# ...
}
plugins {
# Existing plugins...
KeyManager "disk" {
plugin_data {
keys_path = "/opt/spire/data/keys.json"
}
}
}
# Federation with Vouch OIDC provider
federation {
bundle_endpoint {
address = "0.0.0.0"
port = 8443
}
federates_with "vouch.sh" {
bundle_endpoint_url = "https://us.vouch.sh"
bundle_endpoint_profile "https_web" {}
}
}
The https_web profile tells SPIRE to authenticate the endpoint using its public TLS certificate (standard web PKI). SPIRE fetches the JWKS from https://us.vouch.sh/oauth/jwks and automatically refreshes it as keys rotate.
For air-gapped environments, fetch the keys manually and import them:
curl -s https://us.vouch.sh/oauth/jwks -o vouch-jwks.json
spire-server bundle set \
-id spiffe://vouch.sh \
-format jwks \
-path vouch-jwks.json
Note: The manual approach requires re-running these commands whenever Vouch rotates its signing keys. Prefer the automatic federation config above unless your SPIRE Server cannot reach
https://us.vouch.sh.
Create workload registration entries with deployer identity
With federation in place, you can register workloads and tag them with the Vouch-authenticated deployer’s identity. This creates an audit trail from the human who deployed a workload to the SPIFFE identity the workload runs with:
# Register a backend API workload
# The "deployer" selector records who authorized this registration
spire-server entry create \
-spiffeID spiffe://example.org/backend-api \
-parentID spiffe://example.org/spire-agent \
-selector k8s:ns:production \
-selector k8s:sa:backend-api \
-metadata "deployer:alice@example.com"
Validate Vouch tokens in a custom attestor
For advanced use cases, you can write a custom workload attestor plugin that validates a Vouch OIDC token presented by a workload during attestation. This lets workloads bootstrap their SPIFFE identity using a short-lived Vouch token:
# A workload requests a Vouch token with a SPIFFE-specific audience
vouch credential k8s --audience spiffe://example.org
# The custom attestor validates the token against Vouch's JWKS
# and maps the `sub` claim to a SPIFFE ID
| Vouch token claim | SPIRE mapping |
|---|---|
iss | Must match https://us.vouch.sh |
sub | Maps to deployer identity metadata |
aud | Must match the trust domain or a configured audience |
exp | Token must not be expired |
amr | Can require ["hwk", "pin"] for hardware attestation |
Patterns
Once SPIRE trusts Vouch tokens, the common architectures are standard SPIFFE/SPIRE deployments with Vouch supplying the human identity layer. The SPIRE mechanics are covered by the SPIRE documentation; what Vouch adds to each:
- Kubernetes with human + service identity – Developers reach the API server with Vouch OIDC (see the Kubernetes guide); pods authenticate to each other with X.509-SVIDs issued by SPIRE. The layers are complementary: Vouch covers human-to-cluster, SPIRE covers pod-to-pod mTLS.
- Multi-cloud service mesh – SPIRE servers in each cloud federate via bundle exchange so services authenticate across trust domains, while operators use the same
vouch loginsession for access to every environment. - Zero-trust CI/CD with human approval – a self-hosted runner attests its own identity via SPIFFE SVID, and the deployment additionally requires a Vouch OIDC token minted by a human with a YubiKey – see CI/CD approval gates for the Vouch half of that pattern.
SPIFFE concepts reference
| Concept | Description |
|---|---|
| SPIFFE ID | A URI that uniquely identifies a workload: spiffe://trust-domain/path |
| SVID | SPIFFE Verifiable Identity Document — an X.509 certificate or JWT that encodes a SPIFFE ID |
| Trust domain | The root of trust for a SPIFFE deployment (e.g., example.org) |
| Workload API | Local API (Unix socket) that workloads call to get their SVIDs and trust bundles |
| SPIRE Server | Central component that manages identities and issues SVIDs |
| SPIRE Agent | Per-node component that attests workloads and exposes the Workload API |
| Federation | Cross-trust-domain authentication via bundle exchange |
For full details, see the SPIFFE specification and SPIRE documentation.
Troubleshooting
SPIRE cannot reach the Vouch OIDC discovery endpoint
- Verify the SPIRE Server can make outbound HTTPS requests to
https://us.vouch.sh/.well-known/openid-configuration. - Check network policies, firewall rules, and DNS resolution from the SPIRE Server pod or host.
- Test connectivity:
curl -s https://us.vouch.sh/.well-known/openid-configuration | jq .
SVID validation failures
- Ensure the trust bundles are exchanged correctly between federated SPIRE servers. Check with
spire-server bundle show. - Verify the workload registration entries match the actual pod selectors:
spire-server entry show. - Check that the SPIRE Agent is running on the node where the workload is scheduled.
Token audience mismatch
- The
audclaim in Vouch tokens must match what SPIRE expects. When usingvouch credential k8s, the default audience iskubernetes. For SPIFFE integration, specify a custom audience:vouch credential k8s --audience spiffe://example.org. - Verify with:
vouch credential k8s --audience spiffe://example.org | jq -r '.status.token' | step crypto jwt inspect --insecure | jq '.payload.aud'
Clock skew causing JWT validation errors
- SPIRE validates
expandnbfclaims in Vouch tokens. Ensure clocks are synchronized across all nodes using NTP. - Vouch tokens are short-lived — even a few minutes of clock skew can cause validation failures.
- Check the SPIRE Server logs:
kubectl logs -n spire-system deployment/spire-server
“No identity issued” from Workload API
- Confirm the SPIRE Agent is running:
spire-agent healthcheck -socketPath /run/spire/sockets/agent.sock. - Verify that a registration entry exists for the workload:
spire-server entry show -selector k8s:ns:YOUR_NAMESPACE -selector k8s:sa:YOUR_SERVICE_ACCOUNT. - Check that the workload’s service account and namespace match the registered selectors exactly.