SPIFFE and SPIRE in Kubernetes
2024-05-08
TL;DR
We’ll deploy SPIRE on a local kind cluster, generate SPIFFE IDs for workloads, and use mutual TLS between two pods. This is how to go from zero to zero trust in Kubernetes.
What is SPIFFE/SPIRE?
- SPIFFE (Secure Production Identity Framework For Everyone): defines a framework for workload identity.
- SPIRE (SPIFFE Runtime Environment): the production-ready implementation of SPIFFE.
Why You Should Care (Real World)
- Legacy apps don’t rotate secrets.
- TLS certs are static or manually managed.
- You can’t verify “who” a service is at runtime.
- SPIFFE solves this by issuing short-lived X.509 certificates tied to workload identity.
+---------------------+ | SPIRE Server (K8s) | +---------------------+ | | gRPC (registration, trust bundles) v +---------------------+ +----------------------+ | SPIRE Agent (DaemonSet) |<----->| Workload A (sidecarless) | +---------------------+ +----------------------+ | | | | v v /spire-agent.sock /spire-agent.sock (Unix socket) (shared via volumeMount)
- SPIRE Server runs as a StatefulSet
- SPIRE Agents run as DaemonSet, connect securely to server
- Workloads use a Unix socket to get certs
What We’ll Build
- Setup kind cluster
- Install SPIRE Server and Agent
- Deploy two workloads: app-a and app-b
- Secure their communication using mTLS via SPIFFE
Pre-reqs:
brew install kind kubectl helm
Setup Kind:
cat <<EOF | kind create cluster --name spire --config=- kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 name: spire nodes: - role: control-plane extraPortMappings: - containerPort: 30443 hostPort: 443 EOF
Get the Spire repo
git clone https://github.com/spiffe/spire-examples.git cd spire-examples/k8s/simple_psat
Deploy SPIRE Server and Agent
kubectl apply -k .
This applies:
- spire-server.yaml
- spire-agent.yaml
- RBAC and namespace (via kustomize)
Register workloads
Use spire-server running inside the cluster:
kubectl exec -n spire deploy/spire-server -- \ /opt/spire/bin/spire-server entry create \ -spiffeID spiffe://example.org/ns/default/sa/default \ -parentID spiffe://example.org/spire/agent/k8s_psat/my-cluster/node \ -selector k8s:ns:default \ -selector k8s:sa:default \ -selector k8s:pod-label:app:app-a \ -ttl 60
Repeat for app-b.
You’ll find more registration examples in:
examples/k8s/advanced/register-example.sh
Verify Secure Communication
You’ll see app-a sending requests with a SPIFFE cert, app-b validating it.
kubectl logs -l app=app-b
Output should show:
Verified SPIFFE ID: spiffe://example.org/ns/default/sa/default
Diagram – Simplified
+---------+ mTLS +---------+ | app-a |----------------->| app-b | | | SPIFFE ID | | | spiffe://.../app-a | spiffe://.../app-b +---------+ +---------+
Real-World Use Cases
- Zero-trust networking with Linkerd/Istio (replace cert-manager)
- Secretless app deployments (SPIFFE cert as identity token)
- Automatic rotation of certs every few minutes
Bonus: Integrate with Istio or Linkerd
You can configure your mesh to trust SPIFFE identities instead of using static certs. Example (Istio):
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
spec:
mtls:
mode: STRICT
Then use the SPIFFE trust domain in your AuthorizationPolicies.
Conclusion
- SPIRE gives you automated workload identity via X.509 or JWT-SVID
- SPIFFE ID is portable across clusters, clouds, workloads
- You no longer rely on long-lived secrets or static TLS certs