Locking Out the Bots: Fail2ban and CrowdSec on a Modern Linux Server

Turning brute-force noise into a quiet log

Contents

Stand up a fresh server, give it a public IP address, and within minutes complete strangers will start trying to log in. I’ve watched it happen on a stopwatch: a brand-new box, SSH open on port 22, and the first automated login attempt landed before I’d finished configuring it. They are not people. They are tireless scripts sweeping the entire internet, guessing usernames and passwords, probing for known vulnerabilities, and hammering login forms in the hope that one attempt in a million lands. Your authentication logs fill with failed attempts from places you have never been and will never go.

This background radiation of automated attack is simply the weather of the modern internet. The question is not whether you will be probed — you will, constantly — but how cheaply you can make the probing fail. Two tools dominate the answer for a self-hoster: the venerable Fail2ban and the newer, crowd-sourced CrowdSec. This guide covers both, how each one actually works, where they differ, and why running the two together is a perfectly reasonable thing to do.

The constant background of automated attack

Advertisement

It is tempting to assume that a small, obscure server is too unimportant to attract attention. The bots disagree, because they don’t know you exist and don’t care. They do not target you specifically; they target every address that answers, indiscriminately and continuously. Scanning the entire IPv4 internet is cheap enough that a single motivated person can do it in an afternoon, and the attackers are playing a pure numbers game in which your box is one of billions of lottery tickets.

The saving grace is that the overwhelming majority of this traffic is dumb and repetitive: the same handful of common usernames (root, admin, ubuntu, test), the same lists of leaked passwords, the same exploit signatures fired blindly at every door. Because it is so mechanical, it is also eminently blockable. The defensive principle is simple to state. Watch the logs for the unmistakable pattern of an attack; when you see it, slam the door on the source address for a while. Do that automatically, around the clock, without a human in the loop, and the noise drops to near silence. Everything below is an implementation of that one idea.

How Fail2ban works

Fail2ban is a log-watching daemon with a beautifully simple model that has barely needed to change in over a decade. It tails your log files, matches lines against patterns called filters, and when a single source address trips a filter too many times within a time window, it triggers an action — almost always a firewall rule that bans the offending address. The bundle of filter, thresholds and action for a given service is called a jail.

The flow is worth spelling out because understanding it is what lets you tune it. A log line arrives — say, a failed SSH password. A filter’s regular expression recognises the line and extracts the source IP. Fail2ban increments a counter for that address. When the counter crosses maxretry attempts within findtime, the action fires and the address is banned for bantime. Once the ban expires, the slate is wiped clean and the counter resets. The genius of the design is that it needs no special integration with your services whatsoever: if a service writes its failures to a log, Fail2ban can defend it — SSH, web servers, mail servers, FTP, almost anything that keeps a log.

Installing Fail2ban and an SSH jail

Advertisement

On a Debian or Ubuntu system, installation is a single command:

1
2
sudo apt update
sudo apt install fail2ban -y

The one rule everyone learns the hard way: never edit the shipped configuration directly, because a package upgrade will overwrite your changes without asking. Fail2ban is built to be overridden. The cleanest approach on a modern system is a drop-in file under the config directory that takes precedence over the defaults:

1
2
sudo mkdir -p /etc/fail2ban/jail.d
sudo nano /etc/fail2ban/jail.d/local-overrides.conf

(The classic alternative is a single local override file alongside the shipped defaults; the drop-in directory does the same job and keeps your changes tidy and separable.) A solid starting configuration for protecting SSH looks like this:

1
2
3
4
5
6
7
8
[DEFAULT]
bantime  = 1h
findtime = 10m
maxretry = 5
backend  = systemd

[sshd]
enabled = true

This bans any address that fails SSH authentication five times within ten minutes, and keeps it banned for an hour. The backend = systemd line reads from the systemd journal, which is correct on modern distributions where SSH no longer writes to a plain-text log file. Enable and start the service:

1
sudo systemctl enable --now fail2ban

Confirm the jail is active and watch it work:

1
sudo fail2ban-client status sshd

The output lists the number of failures seen and the addresses currently banned. To unban an address you blocked by mistake — which you will, eventually, usually your own home IP after fat-fingering a password:

1
sudo fail2ban-client set sshd unbanip 203.0.113.45

For persistent offenders, raise bantime dramatically — days rather than hours — and add a [recidive] jail that watches Fail2ban’s own log and applies much longer bans to addresses that keep coming back after their first ban expires. The repeat offenders are rarely worth a second chance.

CrowdSec: the modern evolution

Fail2ban defends one server in isolation. It only knows what its own logs have shown it, which means every server on the internet learns about every attacker the hard way, independently, one failed login at a time. CrowdSec asks the obvious better question: what if servers shared what they learned with each other?

CrowdSec keeps the log-parsing idea but rebuilds it around collaboration and a cleaner architecture. It parses logs and evaluates them against scenarios, which describe attack behaviours — a brute-force pattern, aggressive crawling, credential stuffing — rather than single lines. When a scenario fires, CrowdSec records a decision, but the enforcement is deliberately separated out into a component called a bouncer. The engine decides; the bouncer blocks. That separation is the clever bit: one detection engine can drive many enforcement points — a firewall, a web server, a reverse proxy — each running its own bouncer, all acting on the same shared intelligence.

The headline feature is the community angle. When CrowdSec instances around the network detect malicious addresses, they contribute those signals to a shared, curated community blocklist. In return, your server consumes that blocklist and pre-emptively blocks addresses that have attacked other people, before they ever knock on your door. It is herd immunity for servers: an attacker that hits one participant gets flagged for everyone.

Installing CrowdSec and a bouncer

CrowdSec provides a repository script that configures your package manager, after which installation is straightforward:

1
2
curl -s https://install.crowdsec.net | sudo sh
sudo apt install crowdsec -y

On installation it detects running services and enrols sensible collections of parsers and scenarios automatically. The engine alone only detects; to actually block anything you install a bouncer. The firewall bouncer is the natural counterpart to Fail2ban’s behaviour. Pick the flavour that matches your firewall — on a modern distribution that is usually nftables rather than the older iptables:

1
2
3
4
# modern nftables-based systems:
sudo apt install crowdsec-firewall-bouncer-nftables -y
# older iptables-based systems:
# sudo apt install crowdsec-firewall-bouncer-iptables -y

Verify the moving parts. List active decisions, inspect the metrics, and confirm the bouncer registered itself with the local API:

1
2
3
sudo cscli decisions list
sudo cscli metrics
sudo cscli bouncers list

To subscribe to the community blocklist and the central console, enrol your instance with a free account using the enrolment key it gives you:

1
sudo cscli console enroll <your-enrolment-key>

From then on your server both contributes signals and benefits from the collective, blocking known-bad addresses proactively rather than only reacting to attacks against itself. The community blocklist is the single biggest reason to bother with CrowdSec over Fail2ban.

Troubleshooting: the failure modes that catch people

Advertisement

Both tools are reliable, but a handful of problems recur often enough to plan for:

  • You banned yourself. The classic. Keep a second way in — a console via your provider, a VPN, or a whitelisted management address. Add your own network to the ignore list so a mistyped password can’t lock you out. In Fail2ban that’s ignoreip = 127.0.0.1/8 192.168.1.0/24 in the [DEFAULT] section; CrowdSec uses cscli decisions delete --ip <addr> and a whitelist parser.
  • Fail2ban isn’t banning anything. Almost always the backend or the filter. On a systemd distribution, backend = systemd is required; if it’s reading a log file that no longer exists, it silently sees nothing. Run sudo fail2ban-client status sshd and check the “currently failed” counter is moving.
  • The filter regex doesn’t match your log format. Auth log formats drift between distributions. fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf tells you exactly how many lines a filter matched, which turns guesswork into a number.
  • CrowdSec bouncer installed but nothing gets blocked. Check cscli bouncers list shows it as validated; a bouncer that failed to register is a decision engine talking to nobody. Confirm the firewall backend (nftables vs iptables) matches what your system actually runs.
  • Bans vanish after a reboot. Fail2ban rebuilds bans from its database on restart if dbfile persistence is enabled (it is, by default, on current versions) — but firewall rules themselves are ephemeral, so both tools re-apply on start rather than surviving in the kernel.

Fail2ban versus CrowdSec

Neither tool is simply better than the other; they sit at different points on a curve. Fail2ban’s strengths are its simplicity, its ubiquity and its decades of battle-testing. It is a single daemon, configured with plain-text files, present in every distribution’s repositories, and understood by every sysadmin who has ever run a server. For a single box that needs SSH and a web server protected, it is hard to beat for sheer directness, and it has no external dependencies, no account and no phone-home.

CrowdSec’s strengths are architecture and scale. The split between detection and enforcement suits fleets of servers and layered defences, and the shared blocklist means you benefit from attacks that never even reached you. It scales far more gracefully across many machines and brings a polished console for visibility across the lot. The cost is a little more conceptual overhead, a few more moving parts, and — for the community features — an account and a dependence on an external service you don’t control.

A reasonable rule of thumb: a lone hobby server is perfectly well served by Fail2ban; a growing fleet, or anyone who values pre-emptive community intelligence, will appreciate CrowdSec. And there is no rule against running both, with Fail2ban handling a specific local jail while CrowdSec provides the broader, crowd-sourced shield.

Pairing with SSH hardening

Automated banning is one layer, not the whole castle, and it works best sitting on top of a properly hardened SSH configuration. The single most effective change is to disable password authentication entirely and rely on keys, which removes the very thing the brute-force bots are trying to guess. Generate a modern key (ssh-keygen -t ed25519, not the old RSA default), install it, and then in /etc/ssh/sshd_config:

1
2
3
PasswordAuthentication no
PermitRootLogin no
KbdInteractiveAuthentication no

With password login disabled, the relentless guessing attempts cannot succeed regardless of how many the bots throw at you. Fail2ban or CrowdSec then exists chiefly to evict the noisy probers and keep your logs readable, rather than being your genuine last line of defence. Layer the measures: key-only authentication closes the door, a banning tool stops the pointless knocking, and the community blocklist keeps known troublemakers off the street entirely. If you’ve already worked through my guide to hardening a Linux server and going key-only for SSH, these tools are the natural companion to it, automating the punishment for attacks that hardening has already rendered futile.

Banning is also only the prevention half of the story. Knowing what actually happened on a box after the fact is the other half, and for that you want proper accounting — I covered that in tracking who did what with the Linux audit framework, which pairs neatly with everything here.

Is it worth it?

For any server with a public IP, yes, without hesitation — this is close to non-negotiable. The constant probing is not a sign that something is wrong; it is simply the ambient condition of being online, and the right response is not alarm but automation. Fail2ban watches your logs and bans the obvious offenders with a configuration you can read at a glance in five minutes. CrowdSec extends that idea into a collaborative defence where every server’s bad day quietly makes every other server safer.

Who is this not for? Nobody, really — but if you run a single hobby box and want the least fuss, install Fail2ban with the SSH jail above and stop there; it’ll handle the great mass of the noise. If you run several machines, or you like the idea of blocking attackers before they reach you, add CrowdSec on top. Either way, pair it with key-only SSH, and the brute-force racket that fills a fresh server’s logs fades to a quiet, well-defended hum — which is exactly what a log should be.

Advertisement
Advertisement
Smarc
Written by Smarc

Founder and editor of vo.rs. A lifelong tinkerer who self-hosts far more than is sensible, hardens Linux boxes for fun, and prods the latest AI tools to see what they can really do. The how-to guides here are the notes Smarc wishes had existed the first time round.