ebfw

eBPF egress firewall for Kubernetes

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

One cgroup_skb program · no sidecars kernel ≥ 5.8 · cgroup v2 Allow Deny

How it works

One node agent. Every pod's egress.

A single cgroup_skb/egress program at the node's root cgroup sees every pod's outbound traffic. An SSL_write uprobe recovers HTTPS paths before encryption. The same in-kernel maps carry policy verdicts back down to drop denied egress.

See every connection

DNS, TLS SNI, plaintext HTTP, and new TCP connections from every pod on the node — no per-pod sidecar, no sampling.

Attributed per pod

Each event is mapped in-kernel via the originating cgroup id and enriched to namespace/name by a node-scoped Pods informer.

Allow & deny in-kernel

Enforce egress by domain / IP / CIDR / port. Denials drop at the cgroup hook and fail IPv4 connect() fast with EPERM.

Kubernetes-native

Declare egress as EgressPolicy and ClusterEgressPolicy CRDs. The agent watches them directly; a thin operator records status.

See it

Read every pod's egress, live

Text or structured JSON (EBFW_OUTPUT=json), one event per line.

kubectl -n ebfw logs -f ds/ebfw
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
DENY     10.42.0.9 -> 1.1.1.1:443  rule=block-public  pod=default/probe

Control it

Egress as Kubernetes policy

Three modes, one resource

Roll out safely: start in observe, watch verdicts in log, then flip to enforce to drop. A defaultAction: Deny plus a podSelector locks a labeled set of pods to an allowlist.

off · observe log · annotate enforce · drop

Full policy reference →

frontend-allowlist.yaml
apiVersion: ebfw.dvrkn.com/v1
kind: EgressPolicy
metadata:
  name: frontend-allowlist
spec:
  podSelector:
    matchLabels: { app: frontend }
  defaultAction: Deny        # allowlist: deny the rest
  rules:
    - action: Allow
      match: { domains: ["*.googleapis.com"] }
    - action: Allow
      match: { ports: [53] }  # DNS

Quickstart

Install with Helm

CRDs, the operator, and the per-node agent DaemonSet — one chart.

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

$ kubectl apply -f config/samples/ebfw_v1_egresspolicy.yaml
$ kubectl get egp,cegp -A

Docs

Dig in