Back to Article List

CrowdSec firewall bouncer setup: iptables and nftables

CrowdSec firewall bouncer setup: iptables and nftables

There's a specific moment that brings people to this article. cscli decisions list shows the attacker banned, the access log shows the same IP still cheerfully probing, and the realization lands that a decision in CrowdSec's database is just a row until something enforces it. The firewall bouncer is that something for Linux servers: a small Go daemon that streams decisions from the local API into kernel packet filters.

Here's how I configure and verify it, and how to keep Docker from quietly undermining the whole thing. I'm assuming a running Security Engine; if you don't have one yet, start with my CrowdSec install guide for Debian.

Package choice: iptables mode or nftables mode

Two packages, one binary, different modes. crowdsec-firewall-bouncer-iptables drives the classic toolchain with ipset-backed match sets, while crowdsec-firewall-bouncer-nftables talks to nftables natively. Debian 12, Debian 13 and Ubuntu 24.04 all use nftables as the real packet filter, with the iptables command surviving as a compatibility shim. The firewall bouncer docs have a quick test: run iptables -V, and if the output mentions nf_tables you're on the shim. On any modern Debian or Ubuntu I install the nftables package and skip the legacy layer; iptables mode still works fine through the shim, it's just one indirection more than necessary.

sudo apt install crowdsec-firewall-bouncer-nftables

When the engine runs on the same host, the package registers itself during install (it runs cscli bouncers add on your behalf) and starts the service. sudo cscli bouncers list should immediately show it with a fresh pull timestamp.

crowdsec-firewall-bouncer.yaml walkthrough

The config lives at /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml, and the commented default on GitHub is worth reading once in full. These are the keys I actually touch:

mode: nftables
update_frequency: 10s
api_url: http://127.0.0.1:8080/
api_key: YOUR_GENERATED_KEY
deny_action: DROP
disable_ipv6: false

update_frequency is how often the bouncer polls the LAPI for new and expired decisions; 10 seconds is the shipped default and I leave it alone. deny_action takes DROP or REJECT. DROP makes scanners sit through connection timeouts, REJECT fails fast and is kinder to the rare false positive; on internet-facing boxes I stay with DROP and let attackers waste their sockets. api_key gets filled in automatically for a local engine. For a remote one, generate a key with cscli bouncers add on the engine host and point api_url at it across the network.

Mode-specific settings live below those. In iptables mode, iptables_chains lists which chains receive the match-set rule (just INPUT by default) and ipset_size caps the sets at 131072 entries. In nftables mode there's an nftables: block naming the table and chain, crowdsec and crowdsec-chain for IPv4 out of the box, plus a set-only switch for people who'd rather own their ruleset and have the bouncer manage set contents alone. I like set-only mode on boxes with hand-built nftables configs; everywhere else, managed mode is fine.

How decisions become dropped packets

The bouncer mirrors decision state into two sets: crowdsec-blacklists for IPv4 and crowdsec6-blacklists for IPv6. Each entry carries a timeout matching the decision's remaining duration, so bans expire in the kernel without the bouncer lifting a finger.

One quirk worth knowing: ipset can't store timeouts beyond 2147483 seconds, about 596 hours, so anything longer gets capped in iptables mode. Community blocklist entries and your own scenario bans land in the same sets, and a single rule per chain references each set. That's why iptables -L looks nearly empty on a box that's blocking forty thousand IPs; the list lives in the set, and the rule is one line.

Verify with a test decision

sudo cscli decisions add --ip 203.0.113.42 --duration 5m --reason "bouncer smoke test"
sudo ipset list crowdsec-blacklists | head     # iptables mode
sudo nft list ruleset | grep -A 10 crowdsec    # nftables mode
sudo cscli decisions delete --ip 203.0.113.42

Within one update_frequency tick the test IP appears in the set. If it doesn't, read /var/log/crowdsec-firewall-bouncer.log; authentication failures against the LAPI are the usual culprit and I've written up the API access forbidden fixes separately. For a true end-to-end test, ban a spare external IP you control (a phone hotspot works) and confirm new connections from it die. And if this is your first bouncer on a production machine, skim my safe rollout guide first; the confidence to test aggressively comes from knowing you can't lock yourself out.

Docker published ports and the DOCKER-USER chain

Now the trap. When Docker publishes a port it DNATs traffic in PREROUTING, and from there packets flow through FORWARD into Docker's own chains. Your bouncer's rule sits in INPUT. INPUT never sees that traffic. Containers stay reachable from banned IPs while bare-metal services are protected, which is precisely the kind of half-working state that goes unnoticed for months. Docker also rewrites its chains whenever the daemon feels like it, so anything you insert into the DOCKER chain itself gets bulldozed on restart. DOCKER-USER exists as the one chain Docker promises never to touch, evaluated before its own rules, and that's where enforcement belongs on a container host.

In iptables mode the fix is two lines of config, and the docs call it out explicitly:

iptables_chains:
  - INPUT
  - DOCKER-USER

Restart the bouncer and the same match-set rule appears in DOCKER-USER, covering forwarded container traffic. The manual equivalent you'll find in community threads, like the r/CrowdSec writeup on securing Docker services, inserts the rule by hand: iptables -I FORWARD -m set --match-set crowdsec-blacklists src -j DROP. It works, but it won't survive rule flushes and you have to remember it exists. I'd rather the bouncer own it. In nftables mode the shipped chain hooks the input path, so on Docker hosts check your bouncer version's config for a forward hook option, or use the iptables-mode package with the chains config above. The acquisition half of the container story is in my CrowdSec Docker Compose guide.

UFW interactions

UFW and the bouncer program the same kernel firewall and mostly ignore each other politely. The bouncer inserts its rule at the top of the chain, ahead of UFW's allow rules, which is the ordering you want: a banned IP gets dropped before ufw allow 22 ever applies. Don't replicate CrowdSec bans as UFW rules, and after a ufw reload or disable/enable cycle, restart the bouncer so its rules land back in the right position. One documented annoyance: in ipset-only mode, UFW's managed chains can confuse the bouncer's metrics parsing, so treat those counters as approximate on UFW boxes.

IPv6, set capacity and performance

Don't set disable_ipv6: true just because your v6 traffic graph is flat. If the server has an AAAA record it gets scanned over v6 too, and an attacker who finds the v4 door locked will try the other one; the crowdsec6-blacklists set costs nothing to keep active. On capacity: stacking third-party blocklists on top of the community feed can blow past the default set size, at which point the log fills with "IPSET Hash is full, cannot add more elements" and new bans silently fail. Raise ipset_size and restart the bouncer. My CrowdSec blocklists guide goes into how big those subscriptions actually get.

Performance questions come up every time someone sees tens of thousands of entries in a set. Both ipset and nftables sets are hash structures, constant-time lookup per packet regardless of size, so a huge blacklist costs the same as a tiny one. I've run sets north of 50,000 entries on 1 vCPU machines without a visible line on the CPU graph. The expensive part of CrowdSec is parsing logs. The blocking is nearly free, which is exactly why there's no good excuse to run the engine without it.

Your idea deserves better hosting

24/7 support 30-day money-back guarantee Cancel anytime
مدة الإشتراك

VPS.S1

$5.99 Save  17 %
$4.99 شهري
  • 2 vCPU AMD EPYC
  • 2 GB RAMذاكرة
  • 30 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول

VPS.S3

$14.99 Save  33 %
$9.99 شهري
  • 4 vCPU AMD EPYC
  • 6 GB RAMذاكرة
  • 70 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول

EPYC VPS.P1

$8.99 Save  22 %
$6.99 شهري
  • 2 vCPU AMD EPYC
  • 4 GB RAMذاكرة
  • 40 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول
  • نسخ احتياطي تلقائي مجانييشمل خانة نسخ احتياطي واحدة يمكنك ضبطها للتشغيل يوميًا أو أسبوعيًا أو شهريًا.

EPYC VPS.P2

$16.99 Save  24 %
$12.99 شهري
  • 2 vCPU AMD EPYC
  • 8 GB RAMذاكرة
  • 80 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول
  • نسخ احتياطي تلقائي مجانييشمل خانة نسخ احتياطي واحدة يمكنك ضبطها للتشغيل يوميًا أو أسبوعيًا أو شهريًا.

EPYC VPS.P4

$29.99 Save  23 %
$22.99 شهري
  • 4 vCPU AMD EPYC
  • 16 GB RAMذاكرة
  • 160 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول
  • نسخ احتياطي تلقائي مجانييشمل خانة نسخ احتياطي واحدة يمكنك ضبطها للتشغيل يوميًا أو أسبوعيًا أو شهريًا.

EPYC VPS.P5

$39.99 Save  25 %
$29.99 شهري
  • 8 vCPU AMD EPYC
  • 16 GB RAMذاكرة
  • 180 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول
  • نسخ احتياطي تلقائي مجانييشمل خانة نسخ احتياطي واحدة يمكنك ضبطها للتشغيل يوميًا أو أسبوعيًا أو شهريًا.

EPYC VPS.P6

$59.99 Save  25 %
$44.99 شهري
  • 8 vCPU AMD EPYC
  • 32 GB RAMذاكرة
  • 200 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول
  • نسخ احتياطي تلقائي مجانييشمل خانة نسخ احتياطي واحدة يمكنك ضبطها للتشغيل يوميًا أو أسبوعيًا أو شهريًا.

EPYC VPS.P7

$69.99 Save  29 %
$49.99 شهري
  • 16 vCPU AMD EPYC
  • 32 GB RAMذاكرة
  • 240 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول
  • نسخ احتياطي تلقائي مجانييشمل خانة نسخ احتياطي واحدة يمكنك ضبطها للتشغيل يوميًا أو أسبوعيًا أو شهريًا.

Genoa VPS.G2

$24.99 Save  20 %
$19.99 شهري
  • 2 vCPUمعالج AMD EPYC Genoa من الجيل الرابع 9xx4 بسرعة 3.25 GHz أو ما يماثله، على معمارية Zen 4. AMD EPYC G4
  • 4 GB DDR5ذاكرة
  • 50 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول
  • نسخ احتياطي تلقائي مجانييشمل خانة نسخ احتياطي واحدة يمكنك ضبطها للتشغيل يوميًا أو أسبوعيًا أو شهريًا.

Genoa VPS.G4

$44.99 Save  22 %
$34.99 شهري
  • 4 vCPUمعالج AMD EPYC مع أنوية vCPU مخصصة، على عتاد خوادم للمؤسسات. AMD EPYC G4
  • 8 GB DDR5ذاكرة
  • 100 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول
  • نسخ احتياطي تلقائي مجانييشمل خانة نسخ احتياطي واحدة يمكنك ضبطها للتشغيل يوميًا أو أسبوعيًا أو شهريًا.

Genoa VPS.G6

$89.99 Save  22 %
$69.99 شهري
  • 8 vCPUمعالج AMD EPYC مع أنوية vCPU مخصصة، على عتاد خوادم للمؤسسات. AMD EPYC G4
  • 16 GB DDR5ذاكرة
  • 200 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول
  • نسخ احتياطي تلقائي مجانييشمل خانة نسخ احتياطي واحدة يمكنك ضبطها للتشغيل يوميًا أو أسبوعيًا أو شهريًا.

Genoa VPS.G7

$159.99 Save  22 %
$124.99 شهري
  • 8 vCPUمعالج AMD EPYC مع أنوية vCPU مخصصة، على عتاد خوادم للمؤسسات. AMD EPYC G4
  • 32 GB DDR5ذاكرة
  • 250 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا. مشمول
  • نسخ احتياطي تلقائي مجانييشمل خانة نسخ احتياطي واحدة يمكنك ضبطها للتشغيل يوميًا أو أسبوعيًا أو شهريًا.

AMD Ryzen VPS.R1

$16.99 Save  18 %
$13.99 شهري
  • 1 CPU مخصص معالج AMD Ryzen 9 7950X بسرعة 4.5 GHz أو ما يماثله، على معمارية Zen 4. vCPU
  • 4 GB DDR5ذاكرة
  • 50 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6 مشمول دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا.
  • نسخ احتياطي تلقائي مشمول

AMD Ryzen VPS.R2

$29.99 Save  17 %
$24.99 شهري
  • 2 CPU مخصصان معالج AMD Ryzen 9 7950X بسرعة 4.5 GHz أو ما يماثله، على معمارية Zen 4. vCPU
  • 8 GB DDR5ذاكرة
  • 100 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6 مشمول دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا.
  • نسخ احتياطي تلقائي مشمول

AMD Ryzen VPS.R4

$109.99 Save  18 %
$89.99 شهري
  • 8 CPU مخصصة معالج AMD Ryzen 9 7950X بسرعة 4.5 GHz أو ما يماثله، على معمارية Zen 4. vCPU
  • 32 GB DDR5ذاكرة
  • 400 GB NVMeتخزين
  • نطاق ترددي غير محدود
  • IPv4 & IPv6 مشمول دعم IPv6 غير متاح حاليًا في فرنسا وفنلندا أو هولندا.
  • نسخ احتياطي تلقائي مشمول

FAQ

What happens to my active bans when the bouncer restarts?

Nothing worth worrying about. On startup the bouncer pulls the full decision list from the local API and rebuilds its sets, so state converges within seconds. You're only exposed during the brief window between the set being recreated and repopulated, and I've never seen that matter in practice.

GPU products are in high demand at the moment. Fill the form to get notified as soon as your preferred GPU server is back in stock.