Install & run

Requirements

Build

make docker            # build the container image (BPF + Go compiled inside)
make build             # native: bin/ebfw  (needs clang, libbpf-dev; Linux)
make test              # unit tests (TLS SNI parser); runs anywhere, no eBPF

Deploy

ebfw is installed with the Helm chart in helm/ebfw — CRDs, the operator, and the per-node agent DaemonSet.

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

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

# watch the agent on a node:
kubectl -n ebfw logs -f ds/ebfw

Generate egress from any pod, then watch it attributed:

kubectl run probe --rm -it --image=nicolaka/netshoot --restart=Never -- \
  curl -4 --http1.1 https://example.com/foo/bar

The agent DaemonSet runs privileged + hostNetwork + hostPID (the last is needed to find each container’s libssl via /proc). Fine-grained capabilities instead of privileged (kernel-dependent): CAP_BPF, CAP_PERFMON, CAP_NET_ADMIN, CAP_SYS_ADMIN.

Visibility only (no enforcement, no operator):

helm install ebfw ./helm/ebfw -n ebfw --create-namespace \
  --set operator.enabled=false --set agent.enforceMode=off

See configuration.md for every chart value and agent env var, and egresspolicy.md for the policy CRD reference.

Run standalone (container hosts, no Kubernetes)

ebfw is Kubernetes-native, but it isn’t tied to Kubernetes: the agent is a single self-contained binary that watches and enforces egress for every container on a plain container host (Docker / containerd) via the same root-cgroup eBPF hooks — no orchestrator, API server, CRDs, or operator. Policy comes from a YAML file instead of the CRDs. This is the right fit for a Docker/containerd VM, an edge node, or trying enforcement out before wiring up the chart.

You can run ebfw either as the published agent image (the container-native way) or as a host binary.

As a container — the agent image is just the static binary, so run it privileged with host networking, host PID (to find each container’s libssl), and the cgroup + policy mounts:

docker run -d --name ebfw \
  --privileged --network host --pid host \
  -v /sys/fs/cgroup:/sys/fs/cgroup:ro \
  -v "$PWD/config.yaml:/etc/ebfw/config.yaml:ro" \
  -v "$PWD/policy.yaml:/etc/ebfw/policy.yaml:ro" \
  -e EBFW_CONFIG=/etc/ebfw/config.yaml \
  -e EBFW_ENFORCE_MODE=enforce -e EBFW_POLICY=/etc/ebfw/policy.yaml \
  ghcr.io/dvrkn/ebfw:latest

As a host binary — build it (no clang needed; compiled in Docker) and run it as root with a filter config and, optionally, a policy file:

docker build --target bin --output type=local,dest=out .   # -> out/ebfw
                                                            # (or `make build` on Linux w/ clang + libbpf-dev)

# Visibility only — print every container's (and host process's) egress:
sudo EBFW_CONFIG=config.yaml ./out/ebfw

# With enforcement from a YAML policy (file is the default source):
sudo EBFW_CONFIG=config.yaml \
  EBFW_ENFORCE_MODE=enforce \
  EBFW_POLICY=policy.yaml \
  ./out/ebfw

Validate a policy offline first — no kernel, no root:

./out/ebfw policy test --policy policy.yaml \
  --flow 'dst=203.0.113.5 port=443 domain=api.example.com' \
  --flow 'domain=evil.com port=443'

Everything else (env vars, filter config, metrics) is identical to the in-cluster agent — see configuration.md.

Images

Published multi-arch by CI on main/tags after the test jobs pass:

Tests

Unit, envtest, and the host + k3d end-to-end suites — and what each asserts — are documented in tests.md.

Repository layout

main.go                    agent entrypoint (config + signals; runs both data sources)
internal/config/           YAML config + env toggles (inspection/output/metrics) + Filter
internal/egress/           cgroup_skb/egress program + loader (gen.go -> bpf2go)
internal/sslsnoop/         SSL_write uprobe + libssl auto-discovery (gen.go -> bpf2go)
internal/attr/             pod attribution: cgroup-path parser, id->path index, k8s informer
internal/output/           Event model + text/json sinks (single emit chokepoint)
internal/metrics/          Prometheus collectors + /metrics server
internal/l7/               shared HTTP request parser (headers; body is a stub)
internal/tlsparse/         TLS ClientHello -> SNI extractor (+ unit test)
internal/policy/           pure policy model + engine + file source + CRD aggregation
internal/crdsource/        PolicySource backed by the EgressPolicy CRDs (in-process informer)
internal/controller/       thin status-only reconcilers for the two CRDs
api/v1/                    EgressPolicy + ClusterEgressPolicy types (CRD spec + ToPolicy)
cmd/operator/              the control-plane operator (manager) entrypoint
config/                    kubebuilder kustomize tree (generated CRDs, RBAC, manager, samples)
helm/ebfw/                 Helm chart: CRDs + operator + agent DaemonSet
examples/policy.yaml       example file-based egress policy (also the `policy test` fixture)
bpf/egress.bpf.c           the cgroup_skb/egress program
bpf/sslsnoop.bpf.c         the SSL_write uprobe program
test/e2e.sh                host e2e (domain / ssl / paths / headers / filter / metrics / enforcement)
test/crd.sh                k3d e2e (Helm + attribution + CRD enforcement, lifecycle, policy merge)
Dockerfile                 agent image: multi-stage (BPF + Go), or `--target bin` host binary
Dockerfile.operator        operator image (pure Go, no eBPF)