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