DEV Community

ANKUSH CHOUDHARY JOHAL
ANKUSH CHOUDHARY JOHAL

Posted on • Originally published at johal.in

How to Secure Service-to-Service Communication with Cilium 1.16 and SPIFFE 0.8 in K8s 1.32

How to Secure Service-to-Service Communication with Cilium 1.16 and SPIFFE 0.8 in K8s 1.32

Service-to-service (S2S) communication is the backbone of modern microservices architectures, but unsecured traffic between workloads poses significant risk. Kubernetes 1.32, Cilium 1.16, and SPIFFE 0.8 provide a powerful, identity-driven stack to encrypt and authorize S2S traffic without relying on traditional network perimeter controls.

What is Service-to-Service Communication Security?

Unsecured S2S traffic is vulnerable to eavesdropping, tampering, and impersonation. Securing it requires two core capabilities: encryption (to protect data in transit) and strong workload identity (to ensure only authorized services communicate). Traditional approaches use static credentials or IP-based policies, which are brittle and hard to scale. Cilium 1.16 and SPIFFE 0.8 replace these with dynamic, cryptographically verified workload identities and transparent mutual TLS (mTLS).

Why Cilium 1.16 and SPIFFE 0.8?

Cilium is a cloud-native networking and security tool that uses eBPF to enforce policies at the kernel level. Cilium 1.16 adds native integration with SPIFFE-compliant identity providers, eliminating the need for manual certificate management. SPIFFE 0.8 (via SPIRE) issues short-lived, workload-specific SVIDs (SPIFFE Verifiable Identity Documents) that Cilium uses to authenticate and authorize traffic.

Kubernetes 1.32 enhances this stack with improved eBPF support, stable SPIFFE workload identity APIs, and better integration with CNI plugins like Cilium.

Prerequisites

  • A running Kubernetes 1.32 cluster with eBPF support (kernel 5.10+ recommended)
  • kubectl configured to access the cluster
  • Helm 3.14+ installed
  • Basic understanding of Kubernetes workloads, CNI plugins, and mTLS

Step 1: Deploy Kubernetes 1.32 Cluster

If you don’t have a K8s 1.32 cluster, use a tool like kubeadm or kind to deploy one. For kind, use the following configuration to enable K8s 1.32:

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: cilium-spiffe-cluster
nodes:
  - role: control-plane
    image: kindest/node:v1.32.0
  - role: worker
    image: kindest/node:v1.32.0
  - role: worker
    image: kindest/node:v1.32.0
Enter fullscreen mode Exit fullscreen mode

Create the cluster with kind create cluster --config kind-config.yaml. Verify the cluster version with kubectl version --short.

Step 2: Install SPIRE 0.8 (SPIFFE Runtime Environment)

SPIRE is the reference implementation of SPIFFE. Install SPIRE 0.8 using Helm:

helm repo add spire https://spiffe.github.io/spire-helm-chart/
helm repo update
helm install spire spire/spire --version 0.8.0 \
  --namespace spire \
  --create-namespace \
  --set global.trustDomain=cluster.local \
  --set agent.workloadAttestor.k8s.enabled=true
Enter fullscreen mode Exit fullscreen mode

Verify SPIRE components are running: kubectl get pods -n spire. You should see spire-server and spire-agent pods in Running state.

Step 3: Install Cilium 1.16 with SPIFFE Integration

Install Cilium 1.16 via Helm, enabling SPIFFE integration and disabling default kube-proxy replacement if needed (adjust based on your cluster):

helm repo add cilium https://helm.cilium.io/
helm repo update
helm install cilium cilium/cilium --version 1.16.0 \
  --namespace kube-system \
  --set spiffe.enabled=true \
  --set spiffe.trustDomain=cluster.local \
  --set spiffe.spireAgentSocketPath=/run/spire/sockets/agent.sock \
  --set ebpf.masquerade=true \
  --set kubeProxyReplacement=disabled
Enter fullscreen mode Exit fullscreen mode

Wait for Cilium pods to start: kubectl get pods -n kube-system -l k8s-app=cilium. Validate Cilium health with cilium status --wait (install the Cilium CLI first if needed).

Step 4: Configure SPIFFE Identities for Workloads

Create a SPIRE registration entry for a sample workload (e.g., a nginx service and a curl client). First, label the namespaces:

kubectl create namespace backend
kubectl create namespace frontend
kubectl label namespace backend spiffe.io/spiffe-id=true
kubectl label namespace frontend spiffe.io/spiffe-id=true
Enter fullscreen mode Exit fullscreen mode

Create a SPIRE entry for the backend nginx workload:

kubectl exec -n spire -it spire-server-0 -- \
  /opt/spire/bin/spire-server entry create \
  -spiffeID spiffe://cluster.local/ns/backend/sa/default \
  -parentID spiffe://cluster.local/spire/agent/k8s/cluster.local/kubelet \
  -selector k8s:ns:backend \
  -selector k8s:sa:default \
  -ttl 3600
Enter fullscreen mode Exit fullscreen mode

Repeat for the frontend namespace to issue SVIDs to the curl client workload.

Step 5: Enable Mutual TLS (mTLS) for Service-to-Service Traffic

Cilium 1.16 uses SPIFFE SVIDs to automatically provision mTLS certificates for workloads. Enable transparent mTLS for all traffic between backend and frontend namespaces:

cat <
Enter fullscreen mode Exit fullscreen mode

`This policy enforces that only SPIFFE-authenticated workloads from the frontend namespace can access backend port 80, with mTLS automatically handled by Cilium. ## Step 6: Define Cilium Identity-Based Network Policies Beyond mTLS, Cilium enforces identity-based network policies using SPIFFE IDs. Create a policy to restrict backend access to only the frontend service:

cat <

`` This cluster-wide policy ensures only pods with the `app: curl-frontend` label (which has a valid SPIFFE SVID) can access the nginx backend. ## Step 7: Validate Secure Communication Deploy test workloads to validate mTLS and policy enforcement:

# Deploy backend nginx kubectl run nginx-backend -n backend --image=nginx:latest --labels="app=nginx-backend" # Deploy frontend curl client kubectl run curl-frontend -n frontend --image=curlimages/curl:latest --labels="app=curl-frontend" -- sleep 3600

Test communication from the frontend client to the backend:

kubectl exec -n frontend -it curl-frontend -- curl -v http://nginx-backend.backend.svc.cluster.local:80

Cilium will automatically terminate mTLS, so the curl request will succeed if policies are correctly applied. To verify mTLS is in use, check Cilium flow logs:

kubectl exec -n kube-system -it cilium-xxxx -- cilium monitor --type l7

You should see SPIFFE identity information in the flow logs, confirming mTLS is active. ## Troubleshooting Tips * Check SPIRE agent logs for SVID issuance errors: `kubectl logs -n spire -l app=spire-agent` * Validate Cilium SPIFFE integration: `kubectl exec -n kube-system -it cilium-xxxx -- cilium spiffe status` * Verify network policies are applied: `kubectl get ciliumnetworkpolicies -A` * Check mTLS certificate rotation: SPIRE issues short-lived SVIDs, so ensure agents are refreshing certificates correctly. ## Conclusion Combining Kubernetes 1.32, Cilium 1.16, and SPIFFE 0.8 provides a seamless, scalable way to secure service-to-service communication. By using cryptographically verified workload identities and transparent mTLS, you eliminate reliance on static credentials and IP-based policies, reducing operational overhead and improving security posture. This stack is ideal for production microservices environments requiring zero-trust networking. ``

`

Top comments (0)