Running a Linux server without proper protection is like leaving the front door wide open. In this guide we’ll walk you through setting up a robust firewall using UFW and adding an intrusion‑prevention layer with Fail2Ban. By the end you’ll have a hardened server that blocks unwanted traffic, throttles brute‑force attacks, and still lets legitimate users work unhindered.
What You’ll Need
- A fresh or existing Ubuntu/Debian‑based server with sudo access.
- Basic familiarity with the command line.
- An SSH client (e.g., OpenSSH, PuTTY).
- Root or sudo privileges to install packages and edit config files.
- Internet connectivity to fetch updates and packages.
Step 1: Install and Enable UFW (Uncomplicated Firewall)
UFW is the default firewall tool on Ubuntu and many Debian derivatives. It abstracts iptables syntax into easy‑to‑read commands. Start by installing it (if it isn’t already) and enabling the service:
sudo apt update && sudo apt install ufw -y
sudo ufw status verbose # Should show "inactive"
sudo ufw enable # Prompts for confirmation, then activates the firewall
When you enable UFW for the first time, it automatically adds a rule to allow all outgoing traffic and blocks incoming connections, which is a safe default.
Step 2: Define Default Policies
Before opening any ports, explicitly set the default policies. This makes the intent crystal clear and prevents accidental exposure.
# Deny all inbound traffic by default
sudo ufw default deny incoming
# Allow all outbound traffic (most services need this)
sudo ufw default allow outgoing
Run sudo ufw status verbose again to verify the policies have been applied.
Step 3: Open Essential Ports
Now we’ll allow only the services you actually need. Common ports include:
- 22/tcp – SSH (remote admin)
- 80/tcp – HTTP (web server)
- 443/tcp – HTTPS (secure web)
Add them one by one:
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
If you run other services (e.g., MySQL on 3306), add those as well, but keep the list minimal. After adding, reload the firewall to apply changes:
sudo ufw reload Check the rule set:
sudo ufw status numbered You should see each rule with its corresponding number, which is handy for later edits.
Step 4: Harden SSH Access
SSH is the most common attack vector. We’ll tighten it by changing the default port, disabling root login, and limiting login attempts.
- Change the port. Edit
/etc/ssh/sshd_configand set a non‑standard port, e.g., 2222.
sudo sed -i 's/^#Port 22/Port 2222/' /etc/ssh/sshd_config After saving, open the new port in UFW and close the old one:
sudo ufw allow 2222/tcp
sudo ufw delete allow 22/tcp - Disable root login. Still in
sshd_configsetPermitRootLogin no.
sudo sed -i 's/^#PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config - Use key‑based authentication. Generate a key pair on your workstation (
ssh-keygen) and copy the public key to~/.ssh/authorized_keyson the server.
Finally, restart SSH to apply the changes:
sudo systemctl restart sshd Test the new connection before closing your current session:
ssh -p 2222 user@your_server_ip If it works, you’re safe to log out of the old session.
Step 5: Install Fail2Ban
Fail2Ban monitors log files for suspicious patterns (e.g., repeated failed SSH logins) and automatically adds temporary firewall bans. Install it with:
sudo apt install fail2ban -y By default, Fail2Ban creates a jail.local file that inherits settings from jail.conf. We’ll create a custom jail.local to avoid overwriting defaults during package upgrades.
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local Open the new file for editing:
sudo nano /etc/fail2ban/jail.local Scroll to the [DEFAULT] section and adjust the following values:
[DEFAULT]
# Ban IP for 10 minutes
bantime = 600
# Unban after 1 hour if no further offenses
findtime = 600
# Trigger ban after 5 failed attempts
maxretry = 5
Save and exit (Ctrl+X, then Y).
Step 6: Configure Fail2Ban Jails
A “jail” is a set of rules for a particular service. The most important jail for a new server is sshd. Add or modify it in jail.local:
[sshd]
enabled = true
port = 2222 # Match the custom SSH port you set earlier
logpath = %(sshd_log)s
backend = systemd
If you run a web server, enable the apache-auth or nginx-http-auth jails similarly. For example, for Nginx:
[nginx-http-auth]
enabled = true
port = http,https
logpath = /var/log/nginx/*error.log
After editing, restart Fail2Ban so it reads the new configuration:
sudo systemctl restart fail2ban Verify that the SSH jail is active:
sudo fail2ban-client status sshd You should see a list of currently banned IPs (likely empty) and the configuration summary.
Step 7: Test Your Setup
Testing ensures you didn’t accidentally lock yourself out.
- Firewall test. From a remote machine, try connecting to a closed port (e.g., 25/tcp). You should receive a timeout or connection refused.
- Fail2Ban test. Simulate failed SSH logins:
ssh -p 2222 fakeuser@your_server_ip Repeat the failed login five times (or the maxretry you set). After the fifth attempt, Fail2Ban should ban your IP. Confirm with:
sudo fail2ban-client status sshd You’ll see your IP listed under “Banned IPs”. Wait the bantime you configured (10 minutes) and then try again; the ban should lift automatically.
Finally, verify that legitimate traffic still works—browse to http://your_server_ip and ensure the web page loads.
Common Mistakes to Avoid
Even seasoned admins slip up. Here are pitfalls that can render your hardening ineffective:
- Forgetting to open the new SSH port. If you change
Portinsshd_configbut don’t add a corresponding UFW rule, you’ll lock yourself out. - Using
allowinstead ofdenyfor default policies. Settingdefault allow incomingdefeats the purpose of a firewall. - Editing
jail.confdirectly. Package upgrades overwritejail.conf, wiping custom settings. Always usejail.local. - Leaving root login enabled. Attackers often target
rootbecause it bypasses sudo prompts. - Not reloading after changes. Forgetting
ufw reloadorsystemctl restart fail2banmeans your edits never take effect.
Tips and Tricks
Take your security a step further with these optional enhancements:
- Limit SSH to specific IP ranges. Use
ufw allow from 203.0.113.0/24 to any port 2222if you have a static office IP. - Enable rate limiting. UFW can throttle repeated connections:
sudo ufw limit ssh/tcpThis adds a rule that blocks an IP after 6 connections in 30 seconds.
- Log blocked packets. Add
LOGGING=yesin/etc/ufw/ufw.confto keep a record in/var/log/ufw.log. - Use a whitelist for trusted services. If you run a monitoring agent, allow its IP before the generic deny rule.
- Automate updates. Schedule
sudo apt update && sudo apt upgrade -yviacronto keep Fail2Ban and its regexes current.
Frequently Asked Questions
What if I lock myself out after a misconfiguration?
Most cloud providers (AWS, DigitalOcean, etc.) offer a web‑based console or “recovery mode”. Use it to revert the ufw rules or edit sshd_config. As a safety net, always keep a second user with sudo rights and a separate port open before making changes.
Can I use firewalld instead of UFW?
Yes. firewalld is the default on CentOS/RHEL and offers a similar zone‑based approach. The concepts (default deny, allow specific services) remain the same; just replace ufw commands with firewall-cmd equivalents.
How often should I update Fail2Ban rules?
Fail2Ban’s core package receives updates when new attack patterns emerge. Run sudo apt update && sudo apt upgrade fail2ban at least monthly. If you write custom filters, review them after any major service upgrade (e.g., a new OpenSSH version).
Conclusion
Securing a Linux server doesn’t have to be a black‑box operation. By combining a straightforward firewall (UFW) with an automated ban system (Fail2Ban), you create a layered defense that stops most automated attacks while keeping legitimate traffic flowing. Follow the steps, avoid the common pitfalls, and sprinkle in the extra tips for a hardened environment you can trust. Happy securing!
Photo by Kevin Horvat on Unsplash





