Security — 10 min read

fail2ban and the art of locking down SSH without locking yourself out

Key-only auth, a firewall that keeps SSH open, and fail2ban banning brute force are three layers that reinforce each other. The recurring danger is doing any of them in the wrong order and cutting your own access. Here is the order that keeps you in.

Published
October 12, 2026
Reading time
10 min read
Servor Team
Security
Tags
fail2ban · SSH
Lire cet article en français

The threat is boring, and that is the point

Any SSH port on the public internet gets hammered. Not by a targeted adversary — by a constant background tide of automated password guessing against rootand a dictionary of common usernames. Most of it is noise, and most of it is harmless against a properly configured server. The danger is that “properly configured” is a chain of steps, and every one of those steps has a way to lock out the administrator instead of the attacker.

So this piece is less about fail2ban than it looks. fail2ban is one layer of three — key-only authentication, a firewall that keeps SSH reachable, and fail2ban banning the brute-forcers — and each reinforces the others. What ties them together is a single discipline: never take an action that could sever your own connection without a safety net in place first. Get the order wrong and you are the one banned, from a machine you can no longer reach.

Before you touch anything: prove you can still get in

The most dangerous assumption in server hardening is that you have working key-based access. Reconnaissance here is not optional; it is what stops the whole exercise from ending in a locked door.

  • Are there any authorized keys at all? ls ~/.ssh/authorized_keys /root/.ssh/authorized_keys. This is the single most important check, because the next section disables password login — and disabling passwords with zero keys installed is how people lock themselves out permanently.
  • What does the SSH config say now? grep -E '^(PermitRootLogin|PasswordAuthentication|Port)' /etc/ssh/sshd_config shows the current posture before you change it — and whether someone already hardened this box.
  • Is the current config even valid? sshd -t before you begin, so you are not stacking a change on top of a file that already fails to parse.

1. Key-only auth: the layer that actually stops the tide

fail2ban gets the attention, but the layer that ends password brute-forcing outright is turning passwords off. The move is to back up sshd_config first — cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.$(date +%s) — then set PermitRootLogin no and PasswordAuthentication no, keeping key authentication as the only way in.

Two rules govern this step, and both are non-negotiable:

  • Refuse to disable passwords if there are no authorized keys. With no key installed, turning off password auth removes every way to log in. This is a guardrail, not a suggestion: the step should abort rather than run.
  • Always sshd -t before you reload. It validates the config before the running daemon swaps to it. Only then systemctl reload ssh (or sshd) — a reload, which keeps your current session alive, never a blind restart that could drop you against a bad file.

Keep a second SSH session open the entire time. Make the change in one terminal, then open a new connection in another and confirm it works before you close the first. If the new session fails, you still have the old one to roll back with — cp /etc/ssh/sshd_config.bak.* /etc/ssh/sshd_config && systemctl reload ssh. The backup is what turns a lockout into an inconvenience.

2. The firewall: allow SSH before you deny everything

A default-deny firewall is the right baseline, and it is also the single easiest way to lock yourself out — because if you enable it before allowing SSH, the rule that drops incoming traffic drops your traffic too, mid-command. The order is not a preference:

  1. Allow SSH first. ufw allow OpenSSH before anything else, and confirm the rule is present before proceeding.
  2. set the defaults — ufw default deny incoming and ufw default allow outgoing — so only what you explicitly permit gets in;
  3. only then ufw --force enable, with ufw --force reset as the rollback if a rule turns out to be wrong.

The rule to burn into memory: never enable the firewall without SSH already allowed. Open the ports your services actually need on top of that — but SSH comes first, always, because it is the port you are standing on.

3. fail2ban: banning the noise, on top of the real defences

Now, and only now, fail2ban earns its place. It watches the auth log and temporarily bans an IP that fails to authenticate too many times — maxretry attempts inside a window earns a bantime. The install is ordinary: apt-get install -y fail2ban, then systemctl enable --now fail2ban, and the default ships with an sshd jail already watching SSH.

The honest verification is not that the service is active — it is fail2ban-client status sshd printing the jail, its filter, and its current ban list. That proves fail2ban is actually watching SSH, not merely running. A daemon that is up but has no jail attached to the port you care about is a false sense of security.

What fail2ban does not do

fail2ban is a rate limiter for log-visible failures, and understanding its limits is what keeps it in proportion. It is not a substitute for key-only authentication — it slows brute force, it does not make a guessable password safe. It can only ban what appears in the logs, so an attack that never trips the filter never gets banned. A distributed attacker rotating through thousands of IPs slips under any per-IP threshold. And it does absolutely nothing against a validcredential — a stolen or leaked key sails straight through, because from the log's point of view that is a successful, legitimate login. fail2ban trims the noise; it is the weakest of the three layers, not the foundation.

The check that proves you are locked down, not locked out

In order, and this order matters: sshd -t returning cleanly after the config change; a brand-new SSH connection tested and working before the old session is closed; ufw status showing SSH allowed and the firewall active; fail2ban-client status sshd showing the jail attached and watching; and the backup file present so a rollback is one command away. The final proof of an SSH lockdown is always a working new login — never assume, always reconnect.

Where Servor fits

Servor ships SSH hardening, the UFW baseline, and fail2ban as recipes — part of 150+ vetted playbooks — and the recipe shape is exactly what a task this easy to get catastrophically wrong needs. None of them is a script fired blind: each starts with reconnaissance, reading the current SSH config, whether any authorized keys exist, and which ports are listening, so the run adapts to the real machine. Every step carries its own verify — the sshd -t, the ufw status, the fail2ban-client status sshd — so the lockdown is proven, not asserted, and the playbooks are idempotent: an already-hardened box is reported, not re-hardened blindly.

The guardrails are written in because the defaults are dangerous: the SSH recipe refuses to disable password login when there are no authorized keys, the firewall recipe always allows SSH before it enables, and both keep a rollback ready. Because these steps touch your own access, they are treated as the destructive operations they are — proposed, explained, and approved by a human, with a backup taken first.

And the execution never leaves the zero-knowledge path: every command is signed in your browser with a key derived from your vault key, relayed verbatim, and verified by the agent on the machine before it runs. Crucially, the agent reaches the control plane over an outbound connection — so tightening the very SSH port and firewall you are hardening never cuts your management path, even if you did lock yourself out of SSH itself. The deeper walk through SSH hardening is in the SSH-hardening piece, why the platform cannot run a command you did not approve is in the signed-execution piece, and the loop around each command is in the Plan-Execute-Verify piece. Browse the rest of the recipe catalogue, or see the operational picture in our server operations guide.

Next step

Run it, don't just read it.

Free for two servers, no card. Every command is signed in your browser before it runs — our servers relay it, they cannot forge it.

Continue reading

Archive →