Defense in Depth with Spiffe/SPIRE

platform kubernetes spiffe spire
Spiffe and Spire logos over blurred dark abstract network diagram

In the previous article, we secured a Kubernetes application stack using Cilium identity aware networking. We relied on Kubernetes labels to define which workloads could communicate, and we used eBPF enforcement to ensure those rules were applied at kernel speed. That approach dramatically reduced lateral movement and transformed the cluster from a flat network into a policy driven environment.

Kubernetes labels, however, remain metadata. They are powerful and expressive, yet they do not provide cryptographic proof of identity. If the objective is to move from strong segmentation to a true zero trust architecture, identity must evolve from being declarative to being verifiable. That evolution begins with SPIFFE and SPIRE.

Establishing Cryptographic Workload Identity

After deploying SPIRE server and agents into the cluster, workloads must be registered so that they receive SPIFFE Verifiable Identity Documents automatically. In the previous article, the backend API was permitted to communicate with PostgreSQL using a label selector such as:

endpointSelector:
  matchLabels:
    role: backend-api

That selector expressed intent but did not prove identity. With SPIRE, the same logical service is bound to a cryptographic identity through a registration entry such as:

spire-server entry create \
  -spiffeID spiffe://example.org/ns/backend/sa/api-service \
  -selector k8s:ns:backend \
  -selector k8s:sa:api-service

When a pod starts with the api-service service account in the backend namespace, the SPIRE agent attests the workload and issues a short lived X.509 certificate containing that SPIFFE ID.

Inside the pod, the certificate and private key are made available through the SPIFFE Workload API socket, typically mounted as follows:

volumeMounts:
- name: spiffe-workload-api
  mountPath: /run/spire/sockets
volumes:
- name: spiffe-workload-api
  csi:
    driver: csi.spiffe.io

The workload now receives a private key and a signed certificate that are automatically rotated without manual secret management. Identity is no longer represented by a label. It is embodied in a certificate that is cryptographically bound to workload attestation.

Enforcing mTLS Between Frontend and Backend

Previously, API access was restricted using a Cilium policy that allowed ingress only from pods labeled as frontend:

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: api-frontend-only
  namespace: backend
spec:
  endpointSelector:
    matchLabels:
      role: backend-api
  ingress:
  - fromEndpoints:
    - matchLabels:
        role: frontend
    toPorts:
    - ports:
      - port: "8080"
        protocol: TCP

That configuration constrained traffic based on Kubernetes identity. By introducing SPIFFE issued certificates, the same communication path can require mutual TLS and validate identity during the handshake.

A refined policy may require TLS termination and validation against the SPIRE certificate authority:

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: api-mtls-enforced
  namespace: backend
spec:
  endpointSelector:
    matchLabels:
      role: backend-api
  ingress:
  - fromEndpoints:
    - matchLabels:
        role: frontend
    toPorts:
    - ports:
      - port: "8080"
        protocol: TCP
      tls:
        termination: true
        trustedCA: spire-ca

In this model, the backend API accepts connections only when the client presents a certificate signed by the trusted SPIRE authority. Even if an attacker manipulates labels or gains partial network access, the TLS handshake fails without a valid SPIFFE identity. Identity enforcement has shifted from metadata evaluation to cryptographic validation.

Securing Postgres with SPIFFE Certificates

Earlier, Postgres was restricted to backend API traffic using a label based endpoint selector:

endpointSelector:
  matchLabels:
    app: postgres

Ingress was limited to backend API pods with:

fromEndpoints:
- matchLabels:
    role: backend-api

That segmentation prevented unintended network paths inside the cluster. However, enterprise security standards typically prohibit configurations that allow unrestricted CIDR ranges such as 0.0.0.0/0, even when strong certificate based authentication is enforced. Cryptographic identity is powerful, but it does not replace network boundary controls. It complements them.

Within Kubernetes, there is rarely a need for broad CIDR allowances. Cilium identity based policy should remain the primary mechanism controlling which workloads may connect to Postgres:

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: postgres-restrict-ingress
  namespace: database
spec:
  endpointSelector:
    matchLabels:
      app: postgres
  ingress:
  - fromEndpoints:
    - matchLabels:
        role: backend-api
    toPorts:
    - ports:
      - port: "5432"
        protocol: TCP

This ensures that only backend API workloads have a valid network path to port 5432. Even if another pod attempts lateral movement, the packet is dropped at the eBPF layer before it reaches the database.

At the infrastructure layer, Postgres should not be exposed externally unless absolutely necessary. The service should remain a ClusterIP type, deployed in private subnets, and protected by cloud security groups or firewall rules that restrict inbound traffic to cluster node CIDRs only. If external access is required, it must be limited to specific, known corporate networks rather than any address.

For example, instead of permitting unrestricted source ranges in pg_hba.conf:

hostssl all all 0.0.0.0/0 cert clientcert=verify-full

A more defensible configuration would scope access to internal infrastructure ranges:

hostssl all all 10.0.0.0/8 cert clientcert=verify-full

or to an even narrower subnet dedicated to cluster nodes. This ensures that only traffic originating from controlled network segments can even attempt certificate authentication.

Postgres itself should be configured to require TLS and validate against the SPIRE certificate authority:

ssl = on
ssl_ca_file = '/etc/spiffe/ca.pem'
ssl_cert_file = '/etc/spiffe/server-cert.pem'
ssl_key_file = '/etc/spiffe/server-key.pem'

During the TLS handshake, the backend API presents its SPIFFE issued certificate. Postgres validates that the certificate is signed by the trusted authority and may map the SPIFFE ID to a database role. Static database passwords are removed from the system. Even if an attacker gains limited network positioning, authentication fails without a valid SPIFFE identity.

This layered approach satisfies enterprise security expectations because it answers two separate questions. Network policy determines whether a workload should be allowed to initiate a connection at all. Mutual TLS and SPIFFE determine whether the connecting workload can prove who it is.

The result is not open access secured by strong authentication. It is tightly bounded network exposure reinforced by cryptographic identity verification.

Kafka with SPIFFE Backed mTLS

In the earlier design, topic restrictions were enforced using Cilium Kafka aware policy rules such as:

rules:
  kafka:
  - role: "produce"
    topic: "orders"
  - role: "consume"
    topic: "events"

Those rules limited what a client could do once connected. By enabling TLS client authentication in the Kafka broker, identity verification begins at connection establishment.

A broker configuration may include:

listeners=SSL://:9093
ssl.keystore.location=/etc/spiffe/kafka-server-keystore.jks
ssl.truststore.location=/etc/spiffe/kafka-truststore.jks
ssl.client.auth=required

Kafka clients present SPIFFE issued certificates during the TLS handshake. The broker validates the certificate against its trust store and maps the SPIFFE ID to access control lists. Cilium continues to apply protocol level enforcement for topic access, while TLS ensures that only authenticated and verified identities can connect. A compromised client cannot impersonate another producer without possession of its private key.

Multi Cluster Trust Federation

Label based identity ends at the boundary of a single cluster. SPIFFE introduces federation between trust domains, allowing separate SPIRE deployments to exchange trust bundles so that workloads can authenticate across environments.

A federation configuration may resemble:

spire-server federation create \
  -bundleEndpointURL https://cluster-b.example.org:8443 \
  -trustDomain cluster-b.example.org

With federation established, services in different clusters authenticate each other using signed identities rather than network location or static credentials. Cilium policies continue to define permitted communication paths, but identity is portable and verifiable across clusters and environments.

Reinforcing the Enterprise Zero Trust Model

In mature environments, zero trust is not implemented through a single control. It is achieved through layered enforcement. Network segmentation limits where traffic may flow. Cilium enforces workload identity at kernel speed. SPIFFE and SPIRE provide cryptographic proof of identity. The database and application layers perform authorization.

Each layer assumes the others may fail and still enforces its own boundary. That is the posture corporate security teams expect. It replaces implicit trust in network location with explicit verification at every step, while still honoring strict perimeter and subnet restrictions.

This adjustment ensures the architecture is both cryptographically sound and operationally acceptable in regulated enterprise environments.

What Really Matters

Kubernetes labels combined with Cilium provide powerful segmentation, but they remain metadata; SPIFFE and SPIRE elevate identity into short lived, cryptographically verifiable certificates that must be validated on every connection. At the same time, strong authentication does not eliminate the need for strict network boundaries. Zero trust is achieved by layering controls: network policy determines where traffic may flow, mutual TLS proves who is communicating, and application level authorization governs what actions are permitted. When these layers reinforce one another, identity is no longer assumed based on location or labels. It is verified, constrained, and continuously enforced.

Previous Post