Skip to content

dvrkn/ebfw

Repository files navigation

ebfw

ebfw

eBPF egress firewall for Kubernetes

See exactly what every pod talks to — domains, paths, headers, IPs — then allow or deny it.
Attributed per pod. Enforced in the kernel.

Website e2e Go Report Card Go ghcr.io Kubernetes Linux kernel ≥ 5.8 License: MIT

Install · Configuration · Egress policies · Roadmap · Images


ebfw is a single, node-level eBPF agent that shows — and enforces — what every pod on a Kubernetes node is allowed to reach. One cgroup_skb program at the node's root cgroup sees every pod's outbound DNS, TLS SNI, HTTP, and new TCP connections; an SSL_write uprobe recovers HTTPS request paths before encryption. Every event is attributed to the originating pod (namespace/name). The same in-kernel hooks then allow or deny egress per pod by domain / IP / CIDR / port, driven by Kubernetes-native EgressPolicy CRDs. It also sees HTTP paths and headers and lets policy match on method and path (evaluated today; L7 enforcement is on the roadmap).

How it works

  • One cgroup_skb/egress program at the node's root cgroup v2 sees egress from every pod on the node — DNS, TLS ClientHello SNI, plaintext HTTP, and new TCP connections — no per-pod sidecar.
  • An SSL_write uprobe reads HTTPS request plaintext before encryption, recovering paths the packet layer can't see. Auto-discovered per container's libssl, live (no sampling).
  • Attributed per pod in-kernel via the originating cgroup id, enriched to namespace/name by a node-scoped Pods informer. The same maps carry policy verdicts back to the kernel for enforcement.
flowchart LR
    pods["Pods on node"]

    subgraph kernel["in-kernel (eBPF)"]
        egress["cgroup_skb/egress<br/>DNS, TLS SNI, HTTP, CONNECT"]
        uprobe["SSL_write uprobe<br/>HTTPS path + headers"]
    end

    agent["ebfw agent (Go)<br/>attribute + policy engine"]
    informer["k8s Pods informer"]
    out["text / JSON<br/>Prometheus /metrics"]

    pods --> egress
    pods --> uprobe
    egress -- "+ cgroup id" --> agent
    uprobe -- "+ cgroup id" --> agent
    informer -- "namespace/name" --> agent
    agent --> out
    agent -. "allow / deny verdicts" .-> kernel
Loading

See it

DNS      10.42.0.9 ? example.com (TypeA)  pod=default/probe
TLS      10.42.0.9 -> example.com  (104.20.23.154:443)  pod=default/probe
HTTP     10.42.0.9 -> GET example.com/foo/bar  pod=default/probe
HTTPS  [pid=2550689 curl] GET example.com/secret/path?token=abc123  pod=default/probe
CONNECT  10.42.0.9 -> 104.20.23.154:443  pod=default/probe

Or structured JSON (EBFW_OUTPUT=json, one object per line):

{"ts":"2026-06-28T09:14:01Z","kind":"https","domain":"example.com","method":"GET","path":"/secret/path","pid":2550689,"comm":"curl","pod":{"namespace":"default","name":"probe","uid":"be31e934-…","container":"ad68e835…","qos":"besteffort","node":"k3d-ebfw-server-0"}}

Control it

Declare egress as Kubernetes resources — EgressPolicy (namespaced) and ClusterEgressPolicy (cluster-wide) — and the agent enforces them in three modes: off (observe), log (annotate verdicts, no drops), or enforce (drop denied egress). A defaultAction: Deny plus a podSelector locks a labeled set of pods to an allowlist. See docs/egresspolicy.md.

Quickstart

helm install ebfw ./helm/ebfw -n ebfw --create-namespace \
  --set agent.enforceMode=log        # observe verdicts first; flip to enforce when ready

kubectl apply -f config/samples/ebfw_v1_egresspolicy.yaml

Full guide → docs/install.md.

How is this different from Cilium?

Cilium is a CNI — it owns your cluster's network. ebfw rides alongside whatever you already run. It attaches at the node's root cgroup, so you can drop the DaemonSet on any node (any CNI, or no Kubernetes at all), watch egress in log mode, and pull it back out with zero effect on connectivity — it never touches the dataplane.

The headline difference: ebfw reads HTTPS request paths and headers from an SSL_write uprobe — before encryption, with no proxy and no TLS MITM. Cilium needs a terminating Envoy and injected certs to see the same thing.

ebfw is the lightweight egress firewall + per-pod L7 visibility layer; Cilium is the full networking platform (and does L7 enforcement today, which ebfw doesn't yet). Full feature-by-feature breakdown → docs/comparison.md.

Container hosts (without Kubernetes)

ebfw isn't tied to Kubernetes. The same agent — as a privileged container or a single host binary — watches and enforces egress for every container on a plain Docker / containerd host, driven by a YAML policy file instead of the CRDs (same visibility, same node-wide enforcement, no API server or operator). See Run standalone.

License

MIT © dvrkn.


Built with the eBPF skill for hook/map/verifier guidance and cilium/ebpf (ebpf-go, MIT) for the loader.

About

eBPF egress firewall for Kubernetes — see and enforce what every pod talks to (domains, HTTP paths, headers, IPs), attributed per pod, enforced in the kernel. No sidecar, no proxy, no TLS MITM.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors