Back to Technology

Cilium Track Part 1: Install & Network Policies

June 6, 2026 Wasil Zafar 36 min read

Install Cilium as the Kubernetes CNI with eBPF. CiliumNetworkPolicy for identity-based L3/L4 filtering. Replace kube-proxy with Cilium.

Table of Contents

  1. eBPF & Cilium Architecture
  2. Install Cilium
  3. CiliumNetworkPolicy
  4. Identity-Based Security
  5. kube-proxy Replacement
  6. Exercises
  7. Key Takeaways & Next Steps

eBPF & Cilium Architecture

Cilium is a Kubernetes CNI plugin that leverages eBPF (extended Berkeley Packet Filter) to provide high-performance networking, security, and observability. Unlike traditional CNIs that rely on iptables chains, Cilium programs the Linux kernel directly using eBPF bytecode.

Key Insight: Traditional Kubernetes networking uses iptables rules that grow linearly with services (O(n) lookup). Cilium uses eBPF hash maps for O(1) packet processing regardless of cluster size.

The core architectural differences from traditional CNIs:

  • Kernel-level packet processing — eBPF programs attach at tc (traffic control) and XDP (eXpress Data Path) hooks, processing packets before they hit the full network stack
  • Identity-based security — Pods get cryptographic identities based on labels rather than IP addresses, surviving pod restarts and rescheduling
  • No iptables dependency — Eliminates the iptables bottleneck for service routing, NAT, and policy enforcement
  • L7 awareness — eBPF programs can parse HTTP, gRPC, DNS, and Kafka protocols natively

Install Cilium

Cilium provides a CLI tool for installation and management. Install the CLI first, then deploy Cilium to your cluster:

# Install the Cilium CLI
CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)
GOOS=$(go env GOOS)
GOARCH=$(go env GOARCH)
curl -L --fail --remote-name-all \
  https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-${GOOS}-${GOARCH}.tar.gz
sudo tar xzvfC cilium-${GOOS}-${GOARCH}.tar.gz /usr/local/bin
rm cilium-${GOOS}-${GOARCH}.tar.gz

# Install Cilium into the cluster
cilium install --version 1.16.0

# Verify the installation
cilium status --wait
# Expected: All components show "OK"

After installation, run the connectivity test to verify end-to-end functionality:

# Run the full connectivity test suite
cilium connectivity test

# This deploys test pods and validates:
# - Pod-to-Pod connectivity (same node and cross-node)
# - Pod-to-Service connectivity
# - Pod-to-external connectivity
# - Network policy enforcement
# - DNS resolution

CiliumNetworkPolicy

CiliumNetworkPolicy extends standard Kubernetes NetworkPolicy with L7 awareness, DNS-based rules, and identity-based selectors. It uses the same label-matching approach but adds powerful filtering capabilities.

A basic L3/L4 ingress policy restricting traffic to a backend service:

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: backend
      tier: api
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: frontend
            tier: web
      toPorts:
        - ports:
            - port: "8080"
              protocol: TCP
            - port: "8443"
              protocol: TCP

Egress policy with CIDR-based rules for external access control:

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: backend-egress-policy
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: backend
  egress:
    # Allow DNS resolution
    - toEndpoints:
        - matchLabels:
            k8s:io.kubernetes.pod.namespace: kube-system
            k8s-app: kube-dns
      toPorts:
        - ports:
            - port: "53"
              protocol: UDP
    # Allow access to specific external CIDR
    - toCIDR:
        - 10.0.0.0/8
      toPorts:
        - ports:
            - port: "5432"
              protocol: TCP
    # Deny all other egress (implicit with egress rules present)

Identity-Based Security

Cilium assigns numeric identities to groups of pods sharing the same security-relevant labels. These identities are cluster-wide and survive pod IP changes, making policies resilient to rescheduling.

Security Model: When Pod A sends traffic to Pod B, Cilium's eBPF datapath includes Pod A's identity in the packet header. Pod B's eBPF program validates this identity against its policy map — no IP-based lookup required.
# View Cilium identities assigned to endpoints
cilium endpoint list

# Check identity for a specific pod
kubectl exec -n kube-system cilium-xxxxx -- cilium identity list

# Example output:
# IDENTITY   LABELS (source:key[=value])
# 1          reserved:host
# 2          reserved:world
# 4          reserved:health
# 12345      k8s:app=frontend, k8s:io.kubernetes.pod.namespace=production
# 12346      k8s:app=backend, k8s:io.kubernetes.pod.namespace=production

Policy using identity-aware selectors for cross-namespace communication:

apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: allow-monitoring-scrape
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: backend
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: prometheus
            k8s:io.kubernetes.pod.namespace: monitoring
      toPorts:
        - ports:
            - port: "9090"
              protocol: TCP

kube-proxy Replacement

Cilium can fully replace kube-proxy for service load balancing, eliminating iptables rules for ClusterIP, NodePort, and LoadBalancer services. This provides O(1) service lookup via eBPF maps and enables Direct Server Return (DSR) mode.

# Install Cilium with kube-proxy replacement enabled
cilium install --version 1.16.0 \
  --set kubeProxyReplacement=true \
  --set k8sServiceHost=${API_SERVER_IP} \
  --set k8sServicePort=${API_SERVER_PORT}

# Enable DSR mode for improved performance (skip reverse NAT on return path)
cilium install --version 1.16.0 \
  --set kubeProxyReplacement=true \
  --set loadBalancer.mode=dsr \
  --set k8sServiceHost=${API_SERVER_IP} \
  --set k8sServicePort=${API_SERVER_PORT}

# Verify kube-proxy replacement is active
cilium status | grep KubeProxyReplacement
# Expected: KubeProxyReplacement: True [eth0 (Direct Routing)]

# Remove kube-proxy DaemonSet (no longer needed)
kubectl -n kube-system delete ds kube-proxy
kubectl -n kube-system delete cm kube-proxy

Exercises

Exercise 1: Deploy a three-tier application (frontend, backend, database). Write a CiliumNetworkPolicy that allows frontend→backend on port 8080 and backend→database on port 5432, but blocks frontend→database direct access. Verify with cilium connectivity test and kubectl exec curl tests.
Exercise 2: Create a CiliumNetworkPolicy with egress CIDR rules that allows pods labeled app=external-client to reach only api.example.com (use toFQDNs for DNS-based filtering). Block all other external traffic. Test by exec-ing into the pod and running curl against allowed and blocked domains.
Exercise 3: Install Cilium with kubeProxyReplacement=true on a fresh Kind or k3s cluster. Deploy a NodePort service and verify traffic flows correctly. Compare the number of iptables rules (iptables-save | wc -l) with and without kube-proxy replacement.

Key Takeaways & Next Steps

  • Cilium uses eBPF for kernel-level packet processing, eliminating iptables overhead
  • CiliumNetworkPolicy supports L3/L4 rules with identity-based selectors and CIDR filtering
  • Identities are label-based and IP-independent, surviving pod rescheduling
  • kube-proxy replacement provides O(1) service routing with optional DSR mode
  • The connectivity test validates the entire networking stack end-to-end

Next in the Series

In Part 2: Hubble & L7 Observability, we'll enable Hubble for network flow visibility, explore L7 protocol-aware policies for HTTP and gRPC, and set up Prometheus metrics from eBPF.