Skip to content
Faizul Karim Fahim

Server Hardening: SSH, Firewalls, and Fail2ban

Keeping my servers secure is always a priority, and a good starting point is tightening up SSH, configuring a basic firewall, and using Fail2ban to block brute-force attempts. It's not about perfect security, but about making things harder for attackers.

Published 4 min read Security
Illustration of a server with a shield and padlock, representing server security.
On this page
  1. SSH Hardening
  2. Firewall Setup with UFW
  3. Fail2ban
  4. My setup
  5. Related reading

I spend a lot of time making sure my servers, and those of my clients, are reasonably secure. It’s a constant battle, and I don’t aim for absolute invulnerability. My goal is to make it significantly harder for attackers to get in, and to be alerted if someone does try. This post walks through a few basic steps I take for server hardening, focusing on SSH, firewalls, and Fail2ban. It’s a good starting point for anyone running Linux servers, especially those of us in Bangladesh who might be dealing with a higher volume of automated attacks.

SSH Hardening

SSH is a common target, so securing it is a must. I prefer disabling password authentication entirely and relying on SSH keys. It’s more secure and faster, too.

First, generate an SSH key pair on your local machine if you don’t already have one:

ssh-keygen -t rsa -b 4096

Then, copy your public key to the server. There are many ways to do this, ssh-copy-id is the easiest, but if you don’t have it:

cat ~/.ssh/id_rsa.pub | ssh user@server 'mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys'

Next, edit the SSH configuration file. The location varies by distribution, but it’s usually /etc/ssh/sshd_config.

nano /etc/ssh/sshd_config

Make these changes (or similar, depending on your needs):

PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
UsePAM no

After making these changes, reload the SSH service:

systemctl reload sshd  # Ubuntu/Debian
# systemctl reload sshd.service # AlmaLinux/CentOS/RHEL

If you’re using a non-standard SSH port (which I recommend), make sure to change the Port directive in sshd_config.

Firewall Setup with UFW

UFW (Uncomplicated Firewall) is a user-friendly front-end for iptables. It’s easy to set up and manage. I find it much simpler than directly configuring iptables.

First, enable UFW:

uFW enable

By default, UFW denies all incoming connections and allows all outgoing connections. Let’s allow SSH (assuming you’re still using the default port 22, or your custom port):

uFW allow 22/tcp

If you're running a web server (like Nginx or Apache), you’ll also want to allow HTTP (port 80) and HTTPS (port 443):

uFW allow 80/tcp
uFW allow 443/tcp

Check the status of UFW:

uFW status

Fail2ban

Fail2ban monitors log files for suspicious activity and automatically blocks offending IP addresses. It's great for mitigating brute-force attacks against SSH, web servers, and other services.

Install Fail2ban:

sudo apt update  # Ubuntu/Debian
sudo apt install fail2ban
# sudo dnf install fail2ban # AlmaLinux/CentOS/RHEL

Fail2ban uses “jails” to define which log files to monitor and what actions to take when suspicious activity is detected. The default configuration is usually sufficient for basic protection. You can customize jails in /etc/fail2ban/jail.local.

Start and enable Fail2ban:

systemctl start fail2ban
systemctl enable fail2ban

Check Fail2ban status:

systemctl status fail2ban

My setup

On my servers, I’m running Ubuntu 24.04 and AlmaLinux 9. I always disable password authentication for SSH and use key-based authentication. I also change the default SSH port to something random, between 1024 and 65535, to reduce the noise from automated bots. I don't bother with complex firewall rules on small VPSes; UFW's default settings are usually enough. I also find the default Fail2ban configuration works well for most services. Remember to regularly review your Fail2ban logs (/var/log/fail2ban/fail2ban.log) to ensure it's working correctly and not blocking legitimate traffic.

One gotcha I’ve run into before is forgetting to reload services after making changes to configuration files. Always double-check that your changes have taken effect. Another common issue is SELinux contexts, particularly when dealing with custom applications. Make sure your files have the correct SELinux contexts to avoid permission problems. Finally, always verify your DNS TTL (Time To Live) is reasonably low (e.g., 300 seconds) so that changes propagate quickly if you need to block an IP address.

I also regularly check my server logs for any unusual activity. Early detection is key to preventing a full-blown security breach. I frequently link back to Linux Server Administration Checklist for Ubuntu, Debian, and AlmaLinux for more thorough setup steps.

That's a basic overview of how I approach server hardening. It's an ongoing process, and you should always adapt your security measures to your specific needs and environment. Keeping your systems updated and staying informed about new security threats is important. Next up: automating some of these tasks with scripting.

Frequently asked questions

How often should I review my server hardening configuration?

At least quarterly, or whenever you update your server software. New vulnerabilities emerge regularly, so staying on top of things is important.

Is Fail2ban a silver bullet for server security?

No, it’s a good layer of defense against brute-force attacks, but it’s not a replacement for other security best practices like keeping software updated and using strong passwords.

What's the difference between a firewall and Fail2ban?

A firewall controls network traffic in and out of your server, while Fail2ban monitors log files for suspicious activity and blocks offending IP addresses.

// keep reading

Related posts

All blog posts
A stylized illustration of shipping containers layered on top of a server rack, symbolizing Docker containers for web applications.
DevOps 4 min read

Docker for Web Apps: A Practical Guide

Setting up Docker can feel overwhelming, but it’s really worth it for keeping my web apps consistent across environments. I’ll walk through the basics, from your first Dockerfile to running a simple PHP app.

Illustration of a Laravel logo being deployed to an Ubuntu server via GitHub Actions workflow.
DevOps 2 min read

Automating Laravel Deployments with GitHub Actions

Setting up automated deployments for my Laravel projects used to be a headache, but GitHub Actions has made it significantly easier. Here's how I automate deployments to Ubuntu servers using GitHub Actions, and some common gotchas I've run into along the way.