Zero Trust Networking with Cilium

Introduction

Cilium Zero Trust Architecture Kubernetes Cluster Worker Node 1 Frontend API DB Cilium Agent (eBPF) – Policy Enforcement Worker Node 2 Auth Service Catalog Cache Cilium Agent (eBPF) – Policy Enforcement WireGuard Encrypted Hubble (Observability) Service Map / Flows / DNS Cilium Network Policy Identity-Based / L7 / FQDN K8s API Server CRD: CiliumNetworkPolicy Legend Workload Pods eBPF Agent Allow Flow Encrypted Metric Flow

Figure 1: Cilium Zero Trust Architecture across Kubernetes worker nodes with eBPF-based policy enforcement and WireGuard encryption

In today’s cloud-native landscape, the traditional perimeter-based security model is no longer sufficient. With microservices communicating across dynamic, ephemeral environments, the zero trust model has become the gold standard for Kubernetes security. The core principle is simple: never trust, always verify. No workload should be implicitly trusted, regardless of its network location.

Cilium
Kubernetes
eBPF
WireGuard
Hubble
Envoy

Cilium is an open-source CNI (Container Network Interface) plugin that leverages eBPF (Extended Berkeley Packet Filter) to provide highly scalable networking, security, and observability for Kubernetes. Unlike traditional iptables-based approaches, Cilium applies security policies at the kernel level with minimal overhead, making it ideal for enforcing zero trust principles at scale.

In this tutorial, you will learn how to:

  • Install Cilium on a Kubernetes cluster
  • Implement zero trust network policies using Cilium’s identity-based security model
  • Enforce Layer 7 (HTTP/gRPC) policies for fine-grained API-level control
  • Encrypt inter-node traffic with Cilium’s WireGuard integration
  • Monitor and audit network flows with Hubble
  • Apply practical zero trust patterns to a sample microservices application

📖 Prerequisites

This tutorial assumes you have a working Kubernetes cluster (v1.24+) and kubectl configured. A local cluster created with kind or minikube works fine for testing. You’ll also need the cilium CLI tool (v1.15+).

Understanding Zero Trust in Kubernetes

Zero trust architecture (ZTA) is built on three foundational principles:

  1. Verify explicitly — Always authenticate and authorize based on all available data points (identity, context, workload labels).
  2. Use least-privileged access — Limit access with just-in-time and just-enough-access (JIT/JEA).
  3. Assume breach — Segment access, encrypt all traffic, and continuously monitor for anomalies.

In a Kubernetes context, this translates to:

  • Default deny: All ingress and egress traffic is blocked unless explicitly allowed.
  • Identity-based policies: Policies are based on workload identity (labels/service accounts), not IP addresses.
  • Layer 7 awareness: Policies can inspect HTTP methods, paths, and even gRPC calls.
  • Encryption in transit: All pod-to-pod traffic is encrypted, including east-west traffic within the cluster.

Cilium excels at all of these because eBPF allows it to see and control traffic at multiple layers of the network stack without modifying application code.

Step 1: Installing Cilium on Your Cluster

Let’s start by installing Cilium. The recommended way is using the cilium-cli tool, which handles Helm chart installation and validates the setup.

1.1 Install the Cilium CLI

# Download the latest Cilium CLI
curl -L --remote-name-all https://github.com/cilium/cilium-cli/releases/latest/download/cilium-linux-amd64.tar.gz{,.sha256sum}
sha256sum --check cilium-linux-amd64.tar.gz.sha256sum
sudo tar xzvfC cilium-linux-amd64.tar.gz /usr/local/bin
rm cilium-linux-amd64.tar.gz{,.sha256sum}

Verify the installation:

cilium version
# Expected output: cilium-cli: v0.16.x (or newer)

1.2 Install Cilium on Your Cluster

Install Cilium with encryption enabled and Hubble (the observability layer):

cilium install \
  --set encryption.type=wireguard \
  --set hubble.relay.enabled=true \
  --set hubble.ui.enabled=true

# Wait for all pods to be ready
cilium status --wait

This command enables WireGuard-based encryption for all pod-to-pod traffic, implements zero trust at the transport layer, and enables Hubble for network observability.

Verify that all Cilium pods are running:

kubectl -n kube-system get pods -l k8s-app=cilium
# NAME               READY   STATUS    RESTARTS   AGE
# cilium-xxxxx       1/1     Running   0          2m

1.3 Deploy the Hubble UI

cilium hubble enable --ui

# Port-forward to access the UI (in a separate terminal)
cilium hubble ui
# Opens at http://localhost:12000

💡 Tip

Hubble gives you a visual service map showing all pod-to-pod connections, policy decisions (allowed vs. denied), and DNS queries. This is invaluable for auditing your zero trust posture.

Step 2: Applying a Default Deny Policy

Before we allow any traffic, we must establish a default deny posture. CiliumNetworkPolicy (CNP) is the custom resource definition (CRD) that Cilium uses.

2.1 Default Deny Ingress on All Pods

cat <

This policy matches all pods in the default namespace (note the empty matchLabels) and applies an empty ingress rule — which means no ingress traffic is allowed.

2.2 Default Deny Egress on All Pods

cat <

After applying these two policies, all inbound and outbound traffic is blocked for every pod in the default namespace. This is the starting point for zero trust.

⚠️ Important

Do not apply this on a production cluster without first defining allow rules for essential services like kube-dns. In this tutorial, we'll add explicit allow rules step by step.

Step 3: Deploying a Sample Application

Let's deploy a three-tier microservices application to demonstrate zero trust policies in action:

  • frontend: A Node.js web app (port 8080)
  • api: A Go REST API (port 5000)
  • db: Redis cache (port 6379)
cat <

Verify the pods are running:

kubectl -n demo get pods
# NAME       READY   STATUS    RESTARTS   AGE
# frontend   1/1     Running   0          30s
# api        1/1     Running   0          30s
# db         1/1     Running   0          30s

Step 4: Implementing Zero Trust Policies with Cilium

Now let's define explicit, least-privilege policies that follow the zero trust model.

Zero Trust Network Policy Enforcement Flow 1. Default Deny Block all ingress/egress 2. Identify Identity Pod labels & service accounts 3. Define Allow Rules CiliumNetworkPolicy CRDs 4. Enforce eBPF kernel-level DNS Allow Rule Port 53 UDP to kube-dns L7 HTTP Rules GET /post only FQDN Egress Rules *.stripe.com, api.github.com Hubble Continuous Monitoring & Auditing - Every flow allowed/denied is logged and visible ✅ Frontend → API Allowed Identity-based policy match ❌ Frontend → DB Denied No matching policy rule ✅ API → DB Allowed Least-privilege access

Figure 2: Policy enforcement flow from default deny through least-privilege allow rules with Hubble monitoring

4.1 Allow DNS Egress (Essential for All Pods)

Pods need DNS resolution to discover services. We allow egress to port 53 on kube-dns using identity-based selection:

cat <

4.2 Frontend → API Policy (Identity-Based)

Allow only the frontend to reach the api on port 5000:

cat <

4.3 API → Database Policy

Allow only the api to reach db on port 6379:

cat <

4.4 Allow Ingress to Frontend

Allow ingress traffic to the frontend from within the namespace (or from an ingress controller):

cat <

Let's verify that the policies are enforced:

# Test frontend can reach api
kubectl -n demo exec frontend -- wget -qO- http://api-svc:5000/get | head -5
# ✅ Should return JSON response

# Test frontend CANNOT reach db directly
kubectl -n demo exec frontend -- wget -qO- http://db-svc:6379 2>&1
# ❌ Connection refused / timeout — policy is enforced!

# Test api CAN reach db
kubectl -n demo exec api -- wget -qO- --timeout=2 http://db-svc:6379 2>&1
# ✅ Service response (Redis hates plain HTTP but connection is established)

Step 5: Layer 7 (HTTP-Aware) Policies

Cilium's true power is Layer 7 policy enforcement. You can restrict traffic based on HTTP methods, paths, and headers without any sidecar proxies.

5.1 Enable Layer 7 Policy Enforcement

First, enable L7 proxy in Cilium:

cilium config set --restart proxy-http-enabled true

5.2 Apply an HTTP-Aware Policy

Allow the frontend to only GET /get and POST /post endpoints on the API:

cat <

Now test the Layer 7 enforcement:

# ✅ Allowed: GET /get
kubectl -n demo exec frontend -- wget -qO- http://api-svc:5000/get

# ✅ Allowed: POST /post
kubectl -n demo exec frontend -- wget -qO- --post-data='{"key":"value"}' http://api-svc:5000/post

# ❌ Denied: GET / (root path)
kubectl -n demo exec frontend -- wget -qO- http://api-svc:5000/ 2>&1
# "Access denied" — Layer 7 policy enforced!

# ❌ Denied: DELETE /get (wrong method)
kubectl -n demo exec frontend -- wget -qO- --method=DELETE http://api-svc:5000/get 2>&1
# "Access denied"

📝 Note

Layer 7 policies in Cilium are enforced by a proxy (Envoy) that is automatically injected at the endpoint level. This means no sidecar containers to manage — eBPF handles the traffic interception seamlessly.

Step 6: Encrypting All Pod Traffic with WireGuard

If you installed Cilium with --set encryption.type=wireguard, encryption is already enabled. Let's verify:

# Check WireGuard status on any Cilium node pod
kubectl -n kube-system exec ds/cilium -- cilium encrypt status
# Expected: Wireguard is enabled

Cilium's WireGuard integration ensures that all pod-to-pod traffic across nodes is encrypted using WireGuard tunnels. This covers east-west traffic that traditional perimeter security often misses.

If you didn't enable it during install, you can patch the Cilium config:

cilium config set encryption-type wireguard
cilium config set encrypt-node true

With this enabled, even if an attacker gains access to the underlying network fabric, they cannot read pod-to-pod traffic — a fundamental zero trust requirement.

Step 7: Monitoring with Hubble

Hubble provides deep observability into network flows and policy decisions. Let's use it to audit our zero trust enforcement.

7.1 Using Hubble CLI

# Enable the Hubble client
cilium hubble enable

# Port forward to the Hubble relay
kubectl -n kube-system port-forward svc/hubble-relay 4245:80 &

# Watch live flows
hubble observe --namespace demo --not ip=127.0.0.1

# See flows dropped by policy
hubble observe --namespace demo --verdict DROPPED

# Observe HTTP layer data
hubble observe --namespace demo --protocol http

7.2 Using Hubble UI

Access the Hubble UI (port-forwarded earlier) at http://localhost:12000. You'll see:

  • A service map showing all connections between workloads
  • Green lines for allowed flows, red lines for denied ones
  • L7 details showing HTTP methods, paths, and response codes
  • DNS visibility — every DNS query made by pods

Step 8: Advanced Zero Trust Patterns

8.1 Cluster-Wide Default Deny with CiliumClusterwideNetworkPolicy

For production, use CiliumClusterwideNetworkPolicy (CCNP) to enforce default deny across all namespaces:

cat <

8.2 Combining with Kubernetes Service Accounts

For stronger identity, bind policies to Kubernetes service accounts instead of pod labels:

cat <

8.3 FQDN-Based Egress Policies

Allow pods to reach specific external APIs by domain name:

cat <

Verifying Your Zero Trust Posture

Use this checklist to validate your implementation:

Check Command Expected Result
Default deny enforced kubectl -n demo exec frontend -- wget http://db-svc:6379 Connection refused / timeout
Identity-based policies Allowed flows work by label, not IP Identical behavior after pod restart
L7 policies active hubble observe --verdict DROPPED HTTP-level denials visible
Encryption enabled cilium encrypt status WireGuard: Enabled
No unknown flows hubble observe --namespace demo Only expected flows visible

Conclusion

Implementing a zero trust network model in Kubernetes is no longer a luxury — it is a necessity. Cilium, powered by eBPF, provides a production-grade, high-performance foundation for zero trust that goes far beyond what standard Kubernetes NetworkPolicy can offer.

In this tutorial, you learned:

  • How to install Cilium with WireGuard encryption and Hubble observability
  • How to apply default deny policies as the foundation of zero trust
  • How to create identity-based, least-privilege allow policies
  • How to enforce Layer 7 (HTTP-aware) policies without sidecars
  • How to monitor and audit all network flows with Hubble
  • Advanced patterns including cluster-wide policies and FQDN-based egress

By adopting these patterns, your Kubernetes workloads are protected against lateral movement, data exfiltration, and unauthorized access — even if an attacker breaches the cluster perimeter.

🏫 Next Steps

Explore Cilium's official documentation for advanced topics like Cilium Service Mesh, mutual TLS (mTLS) automatic injection, and cluster mesh for multi-cluster zero trust.

Clean Up

To remove the demo resources:

kubectl delete namespace demo
kubectl delete ciliumnetworkpolicies --all
kubectl delete ciliumclusterwidenetworkpolicies --all

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top