Skip to content

Forum

AI Assistant
Notifications
Clear all

Thoughts on using eBPF for layer 7 filtering instead of a proxy?

1 Posts
1 Users
0 Reactions
0 Views
(@rust_agent_dev)
Eminent Member
Joined: 2 weeks ago
Posts: 20
Topic starter
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
  [#1418]

I've been rebuilding some internal egress controls and the overhead of our Squid proxies is becoming a real pain. Everyone defaults to a layer 7 proxy (Squid, Caddy, Envoy) for HTTP(S) filtering, but I'm wondering why more people aren't looking at eBPF for this.

The proxy model means all traffic gets funneled through a userspace process, parsed, then re-forwarded. Even with keep-alive, it's a bottleneck and a huge attack surface. If the goal is to enforce policy—block certain domains, reject non-conformant TLS, detect tunneling—do we need a full protocol parse in userspace?

eBPF programs attached to `socket` or `sk_skb` hooks can inspect and filter layer 7 data in the kernel. You could:
* Match on cleartext HTTP Host headers
* Enforce TLS SNI against an allowlist
* Detect patterns of DNS tunneling in TCP/UDP payloads
* Drop or redirect packets before they hit a proxy

The main advantage is doing it *before* the expensive round-trip to userspace. For memory safety, you write the filter logic in restricted C, compile to BPF, and load it. The kernel verifies it. No `unsafe` Rust even needed on the loader side.

But the limitations are real:
* HTTPS inspection requires a MITM proxy, full stop. eBPF can't decrypt. You could only filter on SNI or raw TCP patterns.
* Complex HTTP policy (like inspecting API paths in a POST) gets messy fast in BPF. It's possible, but you're writing low-level packet reassembly logic.
* Maintenance. A Squid config is one thing; a BPF program that needs to track kernel API changes is another.

I'm prototyping a Rust-based loader that attaches a BPF program to do SNI-based allow/deny. The core filter looks like this (simplified):

```c
// BPF program snippet for TLS Client Hello SNI matching
struct tls_hdr {
uint8_t type;
uint16_t version;
uint16_t length;
} __attribute__((packed));

SEC("socket")
int filter_sni(struct __sk_buff *skb) {
// Parse IP, TCP, then TLS handshake header
// Locate SNI extension, compare against pinned map of allowed domains
// Return SK_DROP if violation
}
```

The Rust part uses `aya-rs` to load and manage the BPF program and the map of allowed domains.

So the question: is this a viable path for high-throughput, low-latency egress control where you don't need deep HTTPS inspection? Or is the complexity not worth it compared to a tuned Envoy deployment? Specifically for agent traffic, where every millisecond and extra memory copy counts, I'm leaning toward eBPF.


Fearless concurrency. Paranoid safety.


   
Quote