Skip to main content
Back to blog

Fail2ban and basic server hardening

·5 min readSecurity

The first time I checked auth logs on a fresh VPS, I was genuinely surprised. The server had been online for less than an hour and there were already dozens of failed login attempts from IPs I had never seen. Bots scan the internet constantly, hammering port 22 on every public IP they find. A default SSH configuration is an open invitation.

Here is what I do on every new server before I deploy anything.

Disable root login

The root user exists on every Linux system. Attackers know this. Brute-forcing root over SSH is the lowest-effort attack there is, and it works if you have a weak password.

Create a regular user with sudo privileges if you do not already have one:

adduser jeremy
usermod -aG sudo jeremy

Then edit the SSH daemon configuration:

sudo nano /etc/ssh/sshd_config

Find the PermitRootLogin line and change it:

PermitRootLogin no

Restart the SSH service for the change to take effect:

sudo systemctl restart sshd

From now on, root cannot log in over SSH. You log in as your regular user and use sudo when you need elevated privileges.

One note on PasswordAuthentication: ideally you would disable password-based login entirely and use SSH keys instead. I plan to cover that setup in a future post on SSH keys. For now, a strong password and the other measures in this post go a long way.

Set up UFW

UFW (Uncomplicated Firewall) is a frontend for iptables that makes firewall rules readable. It comes preinstalled on Ubuntu and Pop!_OS.

The default strategy is simple: deny everything incoming, allow everything outgoing, then poke holes for the services you actually run.

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https

Important: allow SSH before you enable the firewall. If you skip that step on a remote server, you lock yourself out.

sudo ufw enable

Check that everything looks right:

sudo ufw status

You should see something like:

Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       Anywhere
80/tcp                     ALLOW       Anywhere
443/tcp                    ALLOW       Anywhere
22/tcp (v6)                ALLOW       Anywhere (v6)
80/tcp (v6)                ALLOW       Anywhere (v6)
443/tcp (v6)               ALLOW       Anywhere (v6)

That is it. Only SSH, HTTP, and HTTPS traffic gets through. Everything else is dropped silently.

Install and configure Fail2ban

Fail2ban watches log files for repeated failed authentication attempts and bans offending IPs by adding firewall rules. It is the single most effective thing you can install against brute force attacks.

sudo apt install fail2ban

Do not edit /etc/fail2ban/jail.conf directly. It gets overwritten on updates. Create a local override instead:

sudo nano /etc/fail2ban/jail.local
[DEFAULT]
bantime = 10m
findtime = 10m
maxretry = 5
 
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 1h
findtime = 10m

Here is what those values mean:

  • maxretry = 3: three failed login attempts triggers a ban
  • findtime = 10m: those attempts must happen within a 10-minute window
  • bantime = 1h: banned IPs are blocked for one hour

I set the SSH jail stricter than the defaults. Three failed attempts in 10 minutes is not a human mistyping a password. It is a bot.

Start and enable the service:

sudo systemctl enable fail2ban
sudo systemctl start fail2ban

Check the status of your SSH jail:

sudo fail2ban-client status sshd

After a day or two, you will see banned IPs accumulating. It is a little unsettling how quickly the list grows.

Enable unattended security updates

Unpatched software is how servers get compromised. The unattended-upgrades package automatically installs security updates so you do not have to remember to do it manually.

sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades

Select "Yes" when prompted. This creates the configuration at /etc/apt/apt.conf.d/20auto-upgrades and enables automatic security updates.

By default, it only installs security patches, not full distribution upgrades. That is exactly what you want. Security fixes get applied automatically. Everything else you handle manually when you are ready.

Logs go to /var/log/unattended-upgrades/ if you ever want to check what was installed.

Checking auth logs

After a few days, check your auth logs to see what Fail2ban is protecting you from:

sudo cat /var/log/auth.log | grep "Failed password"

Or if your system uses systemd journal:

journalctl -u ssh | grep "Failed"

You will see lines like:

Failed password for root from 203.0.113.42 port 54321 ssh2
Failed password for invalid user admin from 198.51.100.7 port 12345 ssh2

Common usernames the bots try: root, admin, ubuntu, test, user, postgres, deploy. They cycle through dictionaries of passwords against each one. This happens on every server with a public IP. It is not personal. It is automated.

With root login disabled, a firewall in place, and Fail2ban banning repeat offenders, your server is no longer the easy target these bots are looking for. They move on to the next IP.

Sources

Enjoying the blog? Subscribe via RSS to get new posts in your reader.

Subscribe via RSS