Auto-blocking attackers with BGP — an IDS that announces black-hole routes
The cleanest place to drop an attacker isn't the server — it's the edge of the network, before the packets cost you anything. Suri Defender does exactly that: it turns "this IP is attacking us" into a BGP announcement that black-holes the source at the borders and the MikroTik, while the core router never carries a single blackhole route.
The idea: detection → a route
Blocking with iptables works on one box. But when you run an autonomous system
(AS 48756 here), the right primitive is RTBH — Remote-Triggered Black Hole.
You announce the offending /32 over iBGP tagged with a black-hole community
(48756:666); every border router that hears it drops traffic to that
destination. One announcement, dropped everywhere, at line rate.
So the whole design is a pipeline that ends in a BGP announce:
SSH / nginx / syslog / FortiGate logs
│ │
CrowdSec scenarios fail2ban jails
│ decisions │ file-only action
▼ ▼
source-of-truth DB + prefix files
│ union − whitelist
▼
ExaBGP route-injector → FRR (bgpd) → borders / MikroTik
announce /32 · community 48756:666
- CrowdSec parses the logs and runs detection scenarios, exposing a decision stream.
- fail2ban handles its own jails (SSH, VPN, nginx, mail) and appends banned
/32s to a flat file — a deliberately dumb, file-only action, novtysh. - A small FastAPI + SQLite service holds the source of truth, applies TTLs, and regenerates the prefix file.
- ExaBGP reads the union of those files (minus a whitelist) and emits
announce/withdrawto FRR, which reflects the routes onward.
Why a route-injector beats a reconciler
The first version kept blackholes as static routes plus a prefix-list and
redistribute static, with a reconciler trying to keep them in sync. Under
FRR 10.6 that fought the management daemon constantly: prefix-list trie desyncs,
phantom routes, datastore-lock contention, a 2.6 MB frr.conf.
Moving to an ExaBGP route-injector removed the local routing state entirely — the core holds no blackhole routes now. "Blocked" and "announced" became the same fact by construction, not two states a reconciler has to chase. That's the kind of invariant that makes a system stop surprising you at 3 a.m.
Lighter, too
It replaced a heavy Suricata + OpenSearch + Dashboards + Filebeat + EveBox stack — about 2.7 GB of RAM — with a handful of small processes totalling a few hundred megabytes. Same job, an order of magnitude less weight, and a single web UI to see what's currently black-holed and why.
Detection is a solved-ish problem; the interesting part is the response — and for a network, the most elegant response is a route.
Transparency: most posts here are drafted with AI assistance, and the non-English versions are machine-translated by a local LLM and then reviewed. Spotted an error? Please let me know.