Skip to content
Published on

Kubernetes Network Security with Cilium and eBPF: From L3/L4/L7 Policies to Encryption

Authors
  • Name
    Twitter

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

AspectiptableseBPF (Cilium)
ProcessingUserspace rule chainsDirect execution in kernel
L7 SupportNot possibleHTTP, gRPC, Kafka, etc.
PerformanceDegrades proportionally to rule countO(1) hashmap lookups
ObservabilityRequires separate toolsHubble built-in
EncryptionNot supportedWireGuard/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

FeatureStandard NetworkPolicyCiliumNetworkPolicy
L3/L4 FilteringOO
L7 HTTP FilteringXO
L7 gRPC FilteringXO
DNS-Based PoliciesXO
EncryptionXWireGuard/IPsec
ObservabilityXHubble built-in
Cluster-Wide PoliciesXCCNP
Performanceiptables dependenteBPF 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.