- Authors
- Name
- Introduction
- What Is eBPF?
- Installing Cilium
- L3/L4 Network Policies
- L7 HTTP Policies — Cilium's Core Differentiator
- DNS-Based Policies
- WireGuard Transparent Encryption
- Hubble Network Observability
- Real-World Scenario: Microservice Security
- CiliumClusterwideNetworkPolicy
- Troubleshooting
- Performance Benchmarks
- Summary
Introduction
The landscape of Kubernetes network security is changing. Traditional iptables-based NetworkPolicy was limited to simple IP/port filtering at the L3/L4 level. Cilium, however, leverages eBPF to implement precise network policies up to L7 at the kernel level. Cilium 1.19, released in February 2026, brings significant improvements in security hardening, encryption, and large-scale cluster scalability.
In this post, we install Cilium and walk through configuring production-grade network policies step by step.
What Is eBPF?
eBPF (extended Berkeley Packet Filter) is a technology that allows sandboxed programs to run inside the Linux kernel. It enables injecting networking, security, and observability features without modifying the kernel.
# Check eBPF support
uname -r # 5.10+ recommended
cat /boot/config-$(uname -r) | grep CONFIG_BPF
# CONFIG_BPF=y
# CONFIG_BPF_SYSCALL=y
# CONFIG_BPF_JIT=y
eBPF vs iptables Comparison
| Aspect | iptables | eBPF (Cilium) |
|---|---|---|
| Processing | Userspace rule chains | Direct execution in kernel |
| L7 Support | Not possible | HTTP, gRPC, Kafka, etc. |
| Performance | Degrades proportionally to rule count | O(1) hashmap lookups |
| Observability | Requires separate tools | Hubble built-in |
| Encryption | Not supported | WireGuard/IPsec |
Installing Cilium
Installation via Helm
# Add Helm repo
helm repo add cilium https://helm.cilium.io/
helm repo update
# Install Cilium (kube-proxy replacement mode)
helm install cilium cilium/cilium --version 1.19.0 \
--namespace kube-system \
--set kubeProxyReplacement=true \
--set k8sServiceHost=<API_SERVER_IP> \
--set k8sServicePort=6443 \
--set hubble.enabled=true \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true \
--set encryption.enabled=true \
--set encryption.type=wireguard
Verifying the Installation
# Check Cilium status
cilium status --wait
# Run connectivity test
cilium connectivity test
Example output:
/¯¯\
/¯¯\__/¯¯\ Cilium: OK
\__/¯¯\__/ Operator: OK
/¯¯\__/¯¯\ Envoy DaemonSet: OK
\__/¯¯\__/ Hubble Relay: OK
\__/ ClusterMesh: disabled
Deployment cilium-operator Desired: 1, Ready: 1/1
DaemonSet cilium Desired: 3, Ready: 3/3
L3/L4 Network Policies
Basic CiliumNetworkPolicy
Unlike standard Kubernetes NetworkPolicy, CiliumNetworkPolicy provides more granular control.
# deny-all.yaml - Block all traffic (default deny policy)
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: deny-all-ingress
namespace: production
spec:
endpointSelector: {} # Applies to all Pods
ingress:
- {} # Empty rule = nothing allowed
kubectl apply -f deny-all.yaml
# Test: attempt access from another namespace
kubectl run test --rm -it --image=curlimages/curl -- \
curl -s --connect-timeout 3 http://app.production.svc.cluster.local
# curl: (28) Connection timed out
Label-Based Allow Policy
# allow-frontend-to-backend.yaml
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: production
spec:
endpointSelector:
matchLabels:
app: backend
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: '8080'
protocol: TCP
L7 HTTP Policies — Cilium's Core Differentiator
This is where Cilium truly shines. You can filter by HTTP path, method, and even headers.
# l7-http-policy.yaml
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: api-l7-policy
namespace: production
spec:
endpointSelector:
matchLabels:
app: api-server
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: '8080'
protocol: TCP
rules:
http:
# Allow only GET /api/products
- method: GET
path: '/api/products'
# Allow only POST /api/orders
- method: POST
path: '/api/orders'
# Allow GET /api/users/* pattern
- method: GET
path: '/api/users/[0-9]+'
L7 gRPC Policy
# l7-grpc-policy.yaml
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: grpc-l7-policy
namespace: production
spec:
endpointSelector:
matchLabels:
app: grpc-server
ingress:
- fromEndpoints:
- matchLabels:
app: grpc-client
toPorts:
- ports:
- port: '50051'
protocol: TCP
rules:
http:
- method: POST
path: '/mypackage.MyService/GetItems'
- method: POST
path: '/mypackage.MyService/CreateItem'
DNS-Based Policies
You can control access to external services by FQDN.
# dns-policy.yaml
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: allow-external-apis
namespace: production
spec:
endpointSelector:
matchLabels:
app: backend
egress:
# Allow only specific external APIs
- toFQDNs:
- matchName: 'api.stripe.com'
- matchName: 'api.github.com'
- matchPattern: '*.amazonaws.com'
toPorts:
- ports:
- port: '443'
protocol: TCP
# Allow DNS lookups (required!)
- toEndpoints:
- matchLabels:
k8s:io.kubernetes.pod.namespace: kube-system
k8s-app: kube-dns
toPorts:
- ports:
- port: '53'
protocol: UDP
rules:
dns:
- matchPattern: '*'
# Test DNS policy
kubectl exec -n production deploy/backend -- \
curl -s https://api.stripe.com/v1/charges # Success
kubectl exec -n production deploy/backend -- \
curl -s https://evil-site.com # Blocked
WireGuard Transparent Encryption
In Cilium 1.19, WireGuard-based inter-node encryption has become even more stable.
# Check encryption status
cilium encrypt status
# Enable WireGuard via Helm values
# values-encryption.yaml
encryption:
enabled: true
type: wireguard
wireguard:
userspaceFallback: false
nodeEncryption: true # Encrypt all inter-node traffic
# Verify encryption - inspect packets with tcpdump
kubectl exec -n kube-system ds/cilium -- \
tcpdump -i cilium_wg0 -c 5 2>&1 | head -10
# Confirm encrypted traffic on WireGuard interface
kubectl exec -n kube-system ds/cilium -- \
cilium-dbg encrypt status
Hubble Network Observability
Hubble is Cilium's built-in observability tool that monitors all network flows in real time.
# Install Hubble CLI
HUBBLE_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/hubble/master/stable.txt)
curl -L --remote-name-all \
https://github.com/cilium/hubble/releases/download/$HUBBLE_VERSION/hubble-linux-amd64.tar.gz
tar xzvf hubble-linux-amd64.tar.gz
sudo mv hubble /usr/local/bin/
# Port-forward Hubble
cilium hubble port-forward &
# Observe real-time flows
hubble observe --namespace production
# Filter only dropped traffic
hubble observe --namespace production --verdict DROPPED
# Filter HTTP requests only
hubble observe --namespace production --protocol http
# Traffic for a specific Pod
hubble observe --to-pod production/backend-xxx --type l7
Collecting Hubble Metrics with Prometheus
# values-hubble-metrics.yaml
hubble:
metrics:
enabled:
- dns
- drop
- tcp
- flow
- icmp
- http
serviceMonitor:
enabled: true
# Verify metrics
curl -s http://localhost:9965/metrics | grep hubble
Real-World Scenario: Microservice Security
Here is an example of applying Cilium policies across an entire architecture.
# microservice-policies.yaml
---
# 1. Allow Frontend -> API Gateway only
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: frontend-egress
namespace: production
spec:
endpointSelector:
matchLabels:
tier: frontend
egress:
- toEndpoints:
- matchLabels:
tier: api-gateway
toPorts:
- ports:
- port: '8080'
---
# 2. API Gateway -> Backend services (L7 control)
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: api-gateway-egress
namespace: production
spec:
endpointSelector:
matchLabels:
tier: api-gateway
egress:
- toEndpoints:
- matchLabels:
tier: backend
service: user-service
toPorts:
- ports:
- port: '8081'
rules:
http:
- method: GET
path: '/users/.*'
- method: POST
path: '/users'
- toEndpoints:
- matchLabels:
tier: backend
service: order-service
toPorts:
- ports:
- port: '8082'
rules:
http:
- method: GET
path: '/orders/.*'
- method: POST
path: '/orders'
---
# 3. Backend -> Database (port restriction)
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: backend-to-db
namespace: production
spec:
endpointSelector:
matchLabels:
tier: backend
egress:
- toEndpoints:
- matchLabels:
tier: database
toPorts:
- ports:
- port: '5432'
protocol: TCP
CiliumClusterwideNetworkPolicy
A policy that applies across the entire cluster.
# cluster-wide-deny-external.yaml
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
name: deny-external-by-default
spec:
endpointSelector:
matchExpressions:
- key: io.kubernetes.pod.namespace
operator: NotIn
values:
- kube-system
- ingress-nginx
egress:
# Allow cluster-internal only
- toEntities:
- cluster
# Allow DNS
- toEndpoints:
- matchLabels:
k8s:io.kubernetes.pod.namespace: kube-system
toPorts:
- ports:
- port: '53'
protocol: UDP
Troubleshooting
# Check policy application status
kubectl get cnp -A
kubectl get ccnp
# Policy status for a specific Pod
kubectl exec -n kube-system ds/cilium -- \
cilium-dbg endpoint list
# Policy debugging
kubectl exec -n kube-system ds/cilium -- \
cilium-dbg policy get -n production
# Check BPF map status
kubectl exec -n kube-system ds/cilium -- \
cilium-dbg bpf policy get --all
# Check identities
kubectl exec -n kube-system ds/cilium -- \
cilium-dbg identity list
Performance Benchmarks
# Measure network performance with iperf3
# Server Pod
kubectl run iperf-server --image=networkstatic/iperf3 -- -s
# Test from client
kubectl run iperf-client --rm -it --image=networkstatic/iperf3 -- \
-c iperf-server -t 30 -P 4
# Typical results:
# iptables CNI: ~9.2 Gbps
# Cilium eBPF: ~9.8 Gbps (approximately 6-7% improvement)
# Cilium + WireGuard: ~8.5 Gbps
Summary
| Feature | Standard NetworkPolicy | CiliumNetworkPolicy |
|---|---|---|
| L3/L4 Filtering | O | O |
| L7 HTTP Filtering | X | O |
| L7 gRPC Filtering | X | O |
| DNS-Based Policies | X | O |
| Encryption | X | WireGuard/IPsec |
| Observability | X | Hubble built-in |
| Cluster-Wide Policies | X | CCNP |
| Performance | iptables dependent | eBPF optimized |
Cilium has established itself as more than a simple CNI — it is a Kubernetes network security platform. In particular, L7 policies and Hubble observability are essential capabilities in microservice environments.
Quiz: Test Your Understanding of Cilium and eBPF (7 Questions)
Q1. What is the main reason eBPF outperforms iptables?
It executes directly within the kernel and uses hashmap-based O(1) lookups, maintaining consistent performance regardless of the number of rules.
Q2. Which field is used to apply L7 HTTP policies in CiliumNetworkPolicy?
The toPorts.rules.http field, where you specify method, path, etc.
Q3. What must be allowed alongside DNS-based egress policies?
UDP port 53 traffic to kube-dns must be allowed. FQDN policies will not work if DNS lookups fail.
Q4. What are the two transparent encryption methods supported by Cilium?
WireGuard and IPsec. WireGuard is recommended in Cilium 1.19.
Q5. What option do you use in Hubble to view only dropped traffic?
hubble observe --verdict DROPPED
Q6. What is the difference between CiliumClusterwideNetworkPolicy (CCNP) and CiliumNetworkPolicy (CNP)?
CCNP applies across the entire cluster and has no namespace, while CNP applies only to a specific namespace.
Q7. What are the benefits of installing Cilium in kube-proxy replacement mode?
iptables rule chains are eliminated, improving service routing performance and avoiding performance degradation caused by growing rule counts in large-scale clusters.