← Back to list

Cilium Network Policies: L7 HTTP Filtering with eBPF on Kubernetes

Traditional Kubernetes Network Policies only support L3/L4 filtering (IP addresses and ports). Cilium takes network security to the next…

Dilan Dashintha in The PickMe Engineering Blog · 2026-02-14 10:59 · 62 claps · 6.0 min read
#cilium #network-policies #kubernetes #ebpf #service-mesh
Open on Medium ↗
Wiki topics: ☁️ · DevOps & Cloud

Cilium Network Policies: L7 HTTP Filtering with eBPF on Kubernetes

Traditional Kubernetes Network Policies only support L3/L4 filtering (IP addresses and ports). Cilium takes network security to the next level by using eBPF (extended Berkeley Packet Filter) to provide L7 protocol-aware policies, enabling you to filter traffic based on HTTP paths, methods, headers, and more all enforced directly in the Linux kernel without sidecars.

Before diving deeper, let’s first understand eBPF and Cilium.

What is eBPF?

eBPF is a powerful Linux kernel technology that allows developers to run sandboxed, event-driven programs inside the Linux kernel without requiring changes to the kernel source code or loading kernel modules.

In simple terms, if you need to add additional capabilities to the operating system at runtime, you can use eBPF programs to achieve this. The OS guarantees safety and execution efficiency through a verification engine and Just-In-Time (JIT) compilation, allowing programs to run almost as if they were natively compiled kernel code.

What is Cilium?

Cilium is a cloud-native networking solution that uses eBPF to provide

  • Network connectivity between pods
  • Load balancing for services
  • Advanced network security policies with L7 protocol awareness
  • Service mesh capabilities (optional)
  • Observability with deep network visibility

In this guide, we’ll focus on Cilium’s network policy capabilities, which go far beyond traditional Kubernetes NetworkPolicy objects by enabling application-layer filtering.

Let me show you something concrete: blocking specific HTTP endpoints using Layer 7 policies, all enforced directly in the Linux kernel, with zero application changes and no sidecar containers.

What We’ll Build

In this demo, we’ll

  • Deploy a simple microservices app
  • Apply HTTP-aware network policies (blocking specific API paths)
  • Visualize everything with Hubble’s eBPF-powered observability

By the end, you’ll see exactly how Cilium uses eBPF to parse HTTP requests in the kernel and enforce policies at the application layer.

Demo — A Simple Microservices App

We’ll create a basic scenario

  • A backend service exposing several API endpoints
  • A client pod that should have limited access

The Goal: Use Cilium’s L7 policies to control access at the HTTP path level, not just the port level.

1. Install Cilium with Hubble

First, we need Cilium with Hubble enabled for observability

I’ll start by installing the Cilium CLI and then use it to install Cilium.

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 --remote-name-all https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-${GOOS}-${GOARCH}.tar.gz{,.sha256sum}
sha256sum --check cilium-${GOOS}-${GOARCH}.tar.gz.sha256sum
sudo tar -C /usr/local/bin -xzvf cilium-${GOOS}-${GOARCH}.tar.gz
rm cilium-${GOOS}-${GOARCH}.tar.gz{,.sha256sum}

After that, install Cilium

cilium install

Verify the installation

cilium status

If Cilium is correctly installed, you should see a healthy status output.

Understanding Hubble

Before enabling Hubble, let’s understand what it is.

Hubble is Cilium’s observability platform that gives you complete visibility into your Kubernetes network traffic. Built on eBPF, it captures every network flow in your cluster — from DNS queries to HTTP requests — with near-zero overhead and no application changes required.

Now, enable Hubble

cilium hubble enable --ui

Start the Hubble UI

cilium hubble ui

You can now view the Hubble dashboard at [http://localhost:12000/](http://localhost:12000/)

Alternative: Install Cilium with Helm

You can also install Cilium using Helm:


helm repo add cilium https://helm.cilium.io/
helm repo update

#replace `<cluster-name>` with your actual cluster name

helm install cilium cilium/cilium --version 1.14.5 \
  --namespace kube-system \
  --set cluster.name=<cluster-name> \
  --set hubble.relay.enabled=true \
  --set hubble.ui.enabled=true \
  --set hubble.metrics.enabled="{dns,drop,tcp,flow,icmp,http}"

Verify everything is running

kubectl -n kube-system rollout status deployment/cilium-operator
kubectl -n kube-system rollout status deployment/hubble-relay

2. Deploy the Demo Application

Create a new namespace in the k8s cluster

kubectl create namespace demo-app

Here’s the backend application and its service definition


apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
  namespace: demo-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: backend
        image: nginx:alpine
        ports:
        - containerPort: 8080
        command: ["/bin/sh"]
        args:
          - -c
          - |
            cat > /etc/nginx/conf.d/default.conf <<EOF
            server {
              listen 8080;
              location /api/users {
                return 200 '{"users": ["alice", "bob"]}';
                add_header Content-Type application/json;
              }
              location /api/products {
                return 200 '{"products": ["laptop", "phone"]}';
                add_header Content-Type application/json;
              }
              location /admin {
                return 200 '{"admin": "secret data"}';
                add_header Content-Type application/json;
              }
              location /health {
                return 200 'OK';
                add_header Content-Type text/plain;
              }
            }
            EOF
            nginx -g 'daemon off;'
---
apiVersion: v1
kind: Service
metadata:
  name: backend
  namespace: demo-app
  labels:
    app: backend
spec:
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
    name: http
  selector:
    app: backend

Apply the configuration

kubectl apply -f demo.yaml

Backend exposes these endpoints:

/api/users     - User data (should be accessible to client)
/api/products  - Product catalog (restricted from client)
/admin         - Admin panel (restricted from everyone except specific apps)
/health        - Health check (public)

Generating Traffic

We can visualize the traffic with the Hubble UI at [localhost:12000](http://localhost:12000/). Since this service uses ClusterIP, let's generate some traffic by creating a temporary pod in the same namespace

// create a temp pod
kubectl run client -n demo-app --image=curlimages/curl -- sleep 3600

// exec in to the pod
kubectl -n demo-app exec -it client -- sh

// then generate the traffic
curl http://backend/api/users
curl http://backend/api/products  
curl http://backend/admin

From the Hubble dashboard, you can see the client and backend resources. Each request from client to backend appears with multiple records showing different TCP flags like ACK, ACK-FIN, ACK-PSH, SYN, etc.

Now let’s restrict some endpoints using network policies in Cilium.

3. Apply L7 Network Policy

Let’s create a policy that restricts the client to only /api/users and /health

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: backend-l7-policy
  namespace: demo-app
spec:
  endpointSelector:
    matchLabels:
      app: backend
  ingress:
  - fromEndpoints:
    - matchLabels:
        app: client
    toPorts:
    - ports:
      - port: "8080"
        protocol: TCP
      rules:
        http:
        - method: "GET"
          path: "/api/users"
        - method: "GET"
          path: "/health"

The explanation is as follows

FOR pods labeled "app: backend"               ← endpointSelector
  ALLOW incoming traffic                      ← ingress
    FROM pods labeled "app: client"           ← fromEndpoints
      TO port 8080/TCP                        ← toPorts
        ONLY IF it's an HTTP GET request      ← rules.http
          TO path "/api/users" OR "/health"   ← path

After applying this policy, only /api/users and /health can be reached. Other endpoints will be blocked.

When you test the endpoints, you’ll see that calls to restricted endpoints are dropped, while calls to public endpoints return the expected response.

Note: If you try with another pod that isn’t labeled as app: client, all endpoints will be dropped.

Why This Matters - The eBPF Advantage

Let’s compare this to traditional approaches:

Traditional Network Policies

  • Only L3/L4 (IP addresses, ports) — no application-layer awareness
  • Cannot inspect HTTP paths, methods, or headers
  • Cannot filter gRPC methods, Kafka topics, or DNS queries
  • Limited to basic allow/deny rules

Service Mesh Sidecar Proxies (e.g., Istio with Envoy)

  • Can do L7 filtering, but requires sidecar containers in every pod
  • Packets processed in userspace (multiple context switches)
  • Higher resource overhead (CPU, memory per pod)
  • Adds complexity to deployment and debugging

Cilium with eBPF

  • No sidecars needed
  • Kernel-level enforcement
  • L7 protocol awareness (HTTP, gRPC, Kafka, DNS, etc.)
  • Near-zero overhead
  • Real-time observability via Hubble

The magic of eBPF is that it processes packets in the kernel without copying them to userspace. No context switching. No packet copying. Just direct kernel-level processing.

What Else Can Cilium Do?

This demo shows HTTP path filtering, but Cilium’s eBPF can do much more

HTTP Filtering

  • Headers matching
  • Host-based routing
  • Method restrictions
  • Status code inspection

Other Protocols

  • gRPC method filtering
  • Kafka topic access control
  • DNS-based policies
  • TLS SNI inspection

Conclusion

In this demo, we’ve seen how Cilium uses eBPF to bring application-layer awareness to Kubernetes network policies. What traditionally required either service mesh sidecars or was simply impossible with standard NetworkPolicy objects, Cilium achieves efficiently in the kernel.

Key Takeaways

  1. eBPF enables L7 protocol awareness without sidecars — reducing complexity and overhead
  2. Policies are enforced at the kernel level — providing security without application modifications
  3. Hubble provides comprehensive observability — capturing every flow with minimal performance impact
  4. No iptables complexity — Cilium’s eBPF datapath is more efficient and easier to debug

By leveraging eBPF, Cilium transforms Kubernetes network security, making it more powerful, more efficient, and simpler to manage.

What’s Next?

This is my first article in the Cilium series. I’ll be publishing more articles exploring advanced Cilium features, real-world use cases, and deep dives into eBPF networking.

Feel free to share your feedback, questions, or experiences with Cilium in the comments below. I especially appreciate constructive feedback; it helps us all learn and grow together.

Stay tuned for the next article in this series!


메타데이터
post_id
cfddeb8434cb
slug
cilium-network-policies-l7-http-filtering-with-ebpf-on-kubernetes-cfddeb8434cb
url
https://medium.com/pickme-engineering-blog/cilium-network-policies-l7-http-filtering-with-ebpf-on-kubernetes-cfddeb8434cb
canonical_url
https://medium.com/pickme-engineering-blog/cilium-network-policies-l7-http-filtering-with-ebpf-on-kubernetes-cfddeb8434cb
author_url
https://medium.com/@dilandashintha
status
ok
fetched_at
2026-06-23 03:48:11