qf eBPF host firewall
eBPF control plane

Autonomous control plane for eBPF host-firewall — no Kubernetes, no etcd

Central control, distributed enforcement: one control plane compiles and signs policy, per-host eBPF agents enforce it in the kernel. The control plane is never in the packet path.

what the architecture is

A control plane plus lightweight agents

qf splits into two parts: a central control plane and one lightweight agent per host. The control plane compiles label-selector policies into BPF-map data and pushes signed bundles over an mTLS gRPC stream; each agent enforces them in the kernel. Control is centralized, enforcement is distributed across hosts.

The control plane is autonomous — no Kubernetes, no etcd, no service mesh. Its entire state lives in PostgreSQL plus a PKI directory, so it runs as a single process under systemd or a container. There is no extra control plane to babysit, and nothing to phone home.

the flow

From a policy change to enforcement in the kernel

Every step is code-grounded: compile, sign, push by generation number, enforce.

  1. 01

    Define policy

    Operators declare label-selector policies and object groups (ipset / portset / hostset) via REST or the UI. Policy is the source of truth, not the host.

  2. 02

    Compile + cascade

    A change triggers cascade recompute of affected hosts. The control plane builds an effective rule set and compiles it into BPF-map data per host.

  3. 03

    Sign + push

    Each bundle is Ed25519-signed for policy authenticity and pushed to live agents over the bidirectional mTLS gRPC stream, tracked by an incrementing generation number.

  4. 04

    Enforce in kernel

    The agent verifies the signature, translates the bundle into BPF-map writes, and applies it to the running eBPF datapath at the TC hook — no iptables dependency.

  5. 05

    Stream back

    Agents stream verdict, system and flow events plus counters back to the control plane, which partitions them into PostgreSQL and fans live updates to the UI.

The control plane is not in the packet path

If the control plane is unreachable, agents keep enforcing the last bundle from their offline cache and buffer events until they reconnect. A datapath outage cannot be caused by the control plane being down.

components

What the system is made of

01 · control plane

Autonomous control plane

A single Go process: policy compiler, PKI/CA, gRPC server for agents, REST/UI. State is PostgreSQL plus a PKI directory — no Kubernetes, no etcd. From 1 vCPU / 256 MB RAM; HA is 2 replicas behind a TCP balancer sharing PKI and one master key.

02 · agents

Lightweight per-host agents

A small Go binary that loads the eBPF objects, manages the BPF maps and attaches to the interface. It enforces the last known bundle even while offline and consumes minimal resources.

03 · gRPC stream

Signed bundles over gRPC

Bidirectional mTLS stream between agent and control plane. New bundles are pushed by generation number; a separate unary service handles one-shot enrollment. Events and counters flow back on the same connection.

04 · datapath

Kernel-adaptive attach

TCX on kernels ≥6.6 (coexists with Cilium), classic TC (clsact + cls_bpf) on <6.6 without Cilium. Two BPF builds — up to 2048 rules on ≥5.17, a bounded 32-rule variant for 5.15–5.16 — picked at load time by kernel capability.

validation

It fits the architecture you already run

qf is a host firewall, not another overlay or orchestrator — it coexists rather than takes over.

Coexists with Cilium

On kernel ≥6.6 qf attaches via TCX and runs alongside Cilium without fighting over the qdisc. On <6.6 with Cilium already attached, qf refuses to start rather than cause a conflict — an honest, explicit limit.

No iptables to untangle

Enforcement is eBPF at the TC hook, not netfilter rules. qf does not rewrite your iptables or nftables chains; it runs the L3/L4 filter in the kernel and leaves the rest of your stack alone.

No new orchestrator

The control plane needs no Kubernetes, no etcd and no service mesh. If you do not want to operate another distributed system just to manage a firewall, there is nothing extra to babysit.

Runs in a closed contour

No phone-home. Enrollment is registry-mirror plus an out-of-band CA-fingerprint pin, and agents keep enforcing offline — so the same architecture works air-gapped.

fact numbers

Scale, and the honest limits

1 → 5000
one control plane per agent fleet
0
Kubernetes / etcd dependencies
≥ 5.15
minimum Linux kernel
256 MB
control plane from 1 vCPU / 256 MB

Fan-out is sequential, not linearly scalable: 5–15 s across 500 hosts, 30–90 s across 5000. Validated to 1000 hosts; 5000 is a projected estimate. On kernels <6.6 with Cilium already attached, qf refuses to start.

questions

Frequently asked

Why no Kubernetes or etcd?

Because a host firewall should not require you to run another distributed system to manage it. The control plane is a single Go process whose state is PostgreSQL plus a PKI directory. It runs standalone under systemd or as a container, and HA is just two replicas behind a TCP balancer.

What happens if the control plane goes down?

Enforcement continues. The control plane is not in the packet path — agents keep applying the last signed bundle from their offline cache and buffer events on disk until they reconnect. This is fail-open by design: a forgotten policy or an unreachable control plane will not cut a host off.

Can qf coexist with Cilium?

On kernel ≥6.6 qf attaches via TCX and coexists with Cilium. On <6.6 without Cilium it uses classic TC. On <6.6 with Cilium already attached, qf refuses to start rather than fight over the qdisc — an explicit, documented limit rather than a silent failure.

How does it scale, and where are the limits?

One control plane serves up to 5000 agents. Fan-out is sequential — 5–15 s across 500 hosts, 30–90 s across 5000 — so it is not linearly scalable. The datapath has been validated to 1000 hosts; 5000 is a projected estimate. Rule capacity is up to 2048 on kernels ≥5.17 and 32 on 5.15–5.16.

See how qf fits your fleet