PATTERN Cited by 1 source
cGroup-scoped egress firewall (eBPF)¶
cGroup-scoped egress firewall (eBPF) is the pattern of applying a per-process-set outbound network policy on a host by (1) placing the target processes into a dedicated Linux cGroup, (2) attaching eBPF programs to that cGroup, and (3) having those programs consult userspace-compiled policy (via eBPF maps) to allow, drop, or rewrite outbound connections. The rest of the host — including other processes on the same NIC / same network namespace / same IP — is unaffected.
Canonical production instance: GitHub's deployment-safety firewall against deploy-path circular dependencies (Source: sources/2026-04-16-github-ebpf-deployment-safety).
Core shape¶
userspace kernel
┌────────────────────┐ ┌─────────────────────┐
│ policy compiler │ ── eBPF maps ──►│ cGroup-attached │
│ (blocklist, rules) │ │ eBPF programs │
└────────────────────┘ │ - CGROUP_SKB │
│ - CGROUP_SOCK_ADDR│
└────────┬───────────┘
│ attached via
│ link.AttachCgroup
▼
┌─────────────────────┐
│ /sys/fs/cgroup/foo │
│ - process A (in) │
│ - process B (in) │
│ ... (out-of-cgroup │
│ processes unaffected)│
└─────────────────────┘
Control plane (userspace) + data plane (cGroup eBPF programs + eBPF maps) is a textbook CP/DP split inside a single process boundary.
Three eBPF building blocks¶
-
BPF_PROG_TYPE_CGROUP_SKB— egress (and ingress) packet filter at the cGroup boundary. Sees__sk_buff. Return 0 to drop, 1 to allow. Operates on IPs/ports, not hostnames. -
BPF_PROG_TYPE_CGROUP_SOCK_ADDR— hooksconnect4/connect6/bind/sendmsgsyscalls. Can rewrite the destination IP + port before the kernel proceeds with the connection. Paired withCGROUP_SKBto compose name-aware policy (see patterns/dns-proxy-for-hostname-filtering). -
eBPF maps — the policy interchange contract between userspace (writes policy) and kernel programs (read policy per-event). Typical shapes:
BPF_MAP_TYPE_HASH(blocked-IP set),BPF_MAP_TYPE_LRU_HASH(recently-blocked DNS TXIDs),BPF_MAP_TYPE_ARRAY(simple counters).
Why cGroup scope rather than host / interface scope¶
- Co-tenanted hosts. Stateful or customer-serving processes on the same box legitimately need network egress that a newly-deployed deploy script or maintenance task must be blocked from. Host-wide iptables / XDP over-blocks.
- Same network namespace. Container-level namespace isolation would require containerising the target process — a huge commitment just for egress filtering. cGroup attachment is lighter.
- Per-connection attribution. Because the eBPF programs
run in the context of the originating process, helpers like
bpf_get_current_pid_tgid()work — you can log which process triggered a drop, not just that something did.
Why stateful-host scoping rather than "bake a dedicated VM"¶
GitHub's post names the force: "Blocking github.com entirely would impact their ability to handle production requests" — the hosts are stateful and serve customer traffic during rolling deploys, drains, restarts. The alternatives (spin a fresh VM for the deploy script, dockerize the deploy, move to a separate deploy host) all:
- Add orchestration + bootstrapping cost for a script that runs for seconds.
- Change the rootfs / tooling model that the deploy expects.
- Don't give per-process attribution back to the owning team.
cGroup-scoping preserves the existing deploy-script model and just layers policy on top.
Bonus: resource limits come free¶
A cGroup can enforce CPU + memory + PID caps on the contained process. GitHub cites this as a second-order benefit: a runaway deploy script can't starve the customer-serving processes on the same host. "Use the cGroups to enforce CPU and memory limits on deploy scripts, preventing runaway resource usage from impacting workloads."
Relationship to other wiki patterns¶
- patterns/dns-proxy-for-hostname-filtering — the specific mechanism that elevates a cGroup-scoped firewall from IP-based to hostname-based policy.
- patterns/two-stage-evaluation — the cGroup eBPF programs (kernel, cheap per-packet) + userspace DNS proxy / policy engine (rich per-query evaluation) compose as a two-stage pipeline on the same kernel primitive as Datadog's FIM.
- patterns/approver-discarder-filter — Datadog-FIM-specific shape of the eBPF-map policy payload; a cGroup-scoped firewall could use the same shape.
- patterns/shared-kernel-resource-coordination — multi- vendor eBPF coexistence on shared kernel resources; any cGroup-scoped firewall must coordinate with other eBPF tools on the host (e.g. Cilium CNI, Datadog Workload Protection) on TC priorities and program ordering.
- Alternative: concepts/egress-sni-filtering at the VPC / middlebox layer (AWS Network Firewall) filters all traffic out of a subnet based on TLS ClientHello SNI, without host involvement. Trade-offs: no per-process attribution, doesn't require touching hosts, works across heterogeneous workloads. cGroup-scoped firewall is narrower + gives better forensic data; SNI filtering is simpler ops and infra-uniform.
Caveats¶
- Verifier + kernel-version constraints. Same operational costs as any other eBPF production deployment — see concepts/ebpf-verifier and sources/2026-01-07-datadog-hardening-ebpf-for-runtime-security for the full pitfall surface.
- Policy is only as good as cGroup discipline. If a deploy
script spawns a sibling process outside the cGroup, that
sibling isn't policed. The orchestrator must place all
deploy-related processes in the cGroup (via
cgroup.procswrites on spawn, or by launching through a cGroup-aware wrapper). - IP-based policy can't keep up. Hostname-based policy
needs patterns/dns-proxy-for-hostname-filtering or an
SNI-inspection layer; a pure
CGROUP_SKB-only implementation devolves into an ever-out-of-date IP blocklist. - Fail-open vs fail-closed when the userspace policy engine (DNS proxy, rule compiler) crashes needs explicit design. Not detailed in the GitHub post.
- Not a silver bullet for circular dependencies. GitHub closes the post with "Are there ways for circular dependencies to still trip things up? You bet" — the firewall catches one class of circular dependency (outbound-network-to-self), not all.
Seen in¶
- sources/2026-04-16-github-ebpf-deployment-safety — GitHub uses this pattern as a structural fix for deployment-path circular dependencies. Rolled out over six months; live.