Linux

How To Protect SSH with Fail2Ban on Ubuntu 20.04

How To Protect SSH with Fail2Ban on Ubuntu 20.04

Introduction

SSH is the standard method for connecting to cloud servers. It is robust and extensible—new encryption standards can be used to generate new SSH keys, ensuring the protocol remains secure. However, no protocol or software is entirely foolproof. Given SSH’s widespread deployment across the internet, it represents a predictable attack surface that attackers may exploit to gain access.

Any network-exposed service is a potential target. If you review the logs of your SSH service on a high-traffic server, you’ll often see repeated, systematic login attempts that reflect brute force attacks from both users and bots. While optimizations such as disabling password authentication in favor of SSH keys can reduce the risk of successful attacks, they can still be a minor, ongoing concern.

For large-scale production environments where this risk is unacceptable, a VPN like WireGuard is often implemented in front of the SSH service. This setup prevents direct connections to the default SSH port 22 from the public internet without additional software or gateways. Although VPNs are trusted, they introduce complexity and may disrupt some automation or software hooks.

Before committing to a full VPN setup, or as an additional measure, you can use a tool like Fail2ban. Fail2ban helps mitigate brute force attacks by automatically modifying your firewall configuration to block specific IP addresses after a set number of failed login attempts. This helps your server defend itself against unauthorized access attempts with minimal intervention.

In this guide, you will learn how to install and use Fail2ban on an Ubuntu 20.04 server.

Prerequisites

To complete this guide, you will need:

An Ubuntu 20.04 server with a non-root user who has sudo privileges. You can find instructions on setting up such a user in our Initial Server Setup with Ubuntu 20.04 guide.
Optionally, a second server to connect to your first server from, which you can use to test deliberate banning.

Step 1 — Installing Fail2ban

Fail2ban is available in Ubuntu’s software repositories. Start by running the following commands as a non-root user to update your package listings and install Fail2ban:

sudo apt update
sudo apt install fail2ban

Fail2ban will automatically set up a background service upon installation. However, it is disabled by default because some of its default settings might cause unintended effects. You can verify the status of the Fail2ban service using the systemctl command:

systemctl status fail2ban.service

Output
○ fail2ban.service – Fail2Ban Service
Loaded: loaded (/lib/systemd/system/fail2ban.service; disabled; vendor preset: enabled
Active: inactive (dead)
Docs: man:fail2ban(1)

You can enable Fail2ban right away, but first, let’s review some of its features.

Step 2 – Configuring Fail2ban

The Fail2ban service keeps its configuration files in the /etc/fail2ban directory. There is a default configuration file named jail.conf. Navigate to that directory and print the first 20 lines of the file using:

head -20 jail.conf

cd /etc/fail2ban
head -20 jail.conf

Output
#
# WARNING: heavily refactored in 0.9.0 release. Please review and
# customize settings for your setup.
#
# Changes: in most of the cases you should not modify this
# file, but provide customizations in jail.local file,
# or separate .conf files under jail.d/ directory, e.g.:
#
# HOW TO ACTIVATE JAILS:
#
# YOU SHOULD NOT MODIFY THIS FILE.
#
# It will probably be overwritten or improved in a distribution update.
#
# Provide customizations in a jail.local file or a jail.d/customisation.local.
# For example to change the default bantime for all jails and to enable the
# ssh-iptables jail the following (uncommented) would appear in the .local file.
# See man 5 jail.conf for details.
#
# [DEFAULT]

As you’ll notice, the first several lines of the file are commented out with # characters, indicating that they are documentation rather than settings. These comments instruct you not to modify this file directly. Instead, you have two options: either create individual profiles for Fail2ban in multiple files within the jail.d/ directory or consolidate all your local settings in a jail.local file. The jail.conf file will be periodically updated with Fail2ban updates and will serve as the source of default settings unless you have created overrides.

In this tutorial, you will create a jail.local file. Start by copying jail.conf:

sudo cp jail.conf jail.local

Now you can begin making configuration changes. Open the jail.local file in nano or your preferred text editor:

sudo nano jail.local

As you scroll through the file, this tutorial will review some options you might want to update. The settings under the [DEFAULT] section near the top of the file will apply to all services supported by Fail2ban. Additionally, you’ll find headers for [sshd] and other services throughout the file. These sections contain service-specific settings that will override the defaults.

/etc/fail2ban/jail.local
[DEFAULT]
. . .
bantime = 10m
. . .

The bantime parameter defines the duration, in seconds, for which a client will be banned after a failed authentication attempt. By default, this is set to 10 minutes.

/etc/fail2ban/jail.local
[DEFAULT]
. . .
findtime = 10m
maxretry = 5
. . .

The next two parameters are findtime and maxretry. These work together to establish the conditions under which a client is found to be an illegitimate user that should be banned.

The maxretry variable sets the number of tries a client has to authenticate within a window of time defined by findtime, before being banned. With the default settings, the fail2ban service will ban a client that unsuccessfully attempts to log in 5 times within a 10 minute window.

/etc/fail2ban/jail.local
[DEFAULT]
. . .
destemail = root@localhost
sender = root@
mta = sendmail
. . .

If you need to receive email alerts when Fail2ban takes action, consider configuring the destemail, sendername, and mta settings:

The destemail parameter specifies the email address that should receive ban notifications.
The sendername parameter sets the value for the “From” field in the email.
The mta parameter configures the mail service used to send emails. By default, this is set to sendmail, but you may opt for Postfix or another mail solution if preferred.

/etc/fail2ban/jail.local
[DEFAULT]
. . .
action = $(action_)s
. . .

This parameter configures the action that Fail2ban takes when instituting a ban. The value action_ is defined in the file shortly before this parameter. By default, the action is to update your firewall configuration to reject traffic from the offending host until the ban time elapses.

There are other action_ scripts provided by default, which you can use by replacing $(action_) with one of the following options:

/etc/fail2ban/jail.local

# ban & send an e-mail with whois report to the destemail.
action_mw = %(action_)s
%(mta)s-whois[sender=”%(sender)s”, dest=”%(destemail)s”, protocol=”%(protocol)s”, chain=”%(chain)s”]

# ban & send an e-mail with whois report and relevant log lines
# to the destemail.
action_mwl = %(action_)s
%(mta)s-whois-lines[sender=”%(sender)s”, dest=”%(destemail)s”, logpath=”%(logpath)s”, chain=”%(chain)s”]

# See the IMPORTANT note in action.d/xarf-login-attack for when to use this action
#
# ban & send a xarf e-mail to abuse contact of IP address and include relevant log lines
# to the destemail.
action_xarf = %(action_)s
xarf-login-attack[service=%(__name__)s, sender=”%(sender)s”, logpath=”%(logpath)s”, port=”%(port)s”]

# ban IP on CloudFlare & send an e-mail with whois report and relevant log lines
# to the destemail.
action_cf_mwl = cloudflare[cfuser=”%(cfemail)s”, cftoken=”%(cfapikey)s”]
%(mta)s-whois-lines[sender=”%(sender)s”, dest=”%(destemail)s”, logpath=”%(logpath)s”, chain=”%(chain)s”]

For example:

action_mw performs the action and sends an email.
action_mwl performs the action, sends an email, and includes logging.
action_cf_mwl performs all of the above actions and additionally updates the Cloudflare API associated with your account to ban the offender there as well.

Individual Jail Settings

Next, you’ll configure settings for individual services, specified by section headers like [sshd].

Each of these sections must be enabled individually by adding an enabled = true line under the header along with their other settings.

/etc/fail2ban/jail.local
[jail_to_enable]
. . .
enabled = true
. . .

By default, the SSH service is enabled, while all other services are disabled.

Some settings you’ll find here include:

filter: This specifies the filter used to determine whether a log entry indicates a failed authentication attempt.
logpath: This tells Fail2ban where to find the logs for the specific service.
The filter value is a reference to a file located in the /etc/fail2ban/filter.d directory, with its .conf extension removed. These files contain regular expressions used to identify failed authentication attempts in the logs. Although these files are complex and predefined settings typically cover appropriate log lines effectively, you can explore available filters by looking into that directory.

ls /etc/fail2ban/filter.d

If you come across a file related to a service you’re using, you should open it with a text editor. Most of these files are well-commented, which should help you understand the types of conditions they are designed to guard against. Many filters have corresponding sections in the jail.conf file that are disabled by default but can be enabled in the jail.local file if needed.

For example, if you’re running a website with Nginx and notice that a password-protected section is receiving a lot of login attempts, you can configure Fail2ban to use the nginx-http-auth.conf file to monitor the /var/log/nginx/error.log file for such conditions.

This configuration is already set up in a section called [nginx-http-auth] in your /etc/fail2ban/jail.conf file. You just need to add the enabled = true parameter in the jail.local file to activate it.

/etc/fail2ban/jail.local
. . .
[nginx-http-auth]

enabled = true
. . .

When you’ve finished editing the file, save and close it. Next, enable the Fail2ban service so that it starts automatically. Use the following command:

sudo systemctl enable fail2ban

Then, start the Fail2ban service manually for the first time with:

sudo systemctl start fail2ban

You can verify that Fail2ban is running with:

sudo systemctl status fail2ban

Output
● fail2ban.service – Fail2Ban Service
Loaded: loaded (/lib/systemd/system/fail2ban.service; enabled; vendor preset: enab>
Active: active (running) since Tue 2022-06-28 19:29:15 UTC; 3s ago
Docs: man:fail2ban(1)
Main PID: 39396 (fail2ban-server)
Tasks: 5 (limit: 1119)
Memory: 12.9M
CPU: 278ms
CGroup: /system.slice/fail2ban.service
└─39396 /usr/bin/python3 /usr/bin/fail2ban-server -xf start

 

In the next step, you’ll demonstrate Fail2ban in action.

Step 3 — Testing the Banning Policies (Optional)

From another server, which won’t need to log in to your Fail2ban server in the future, you can test the rules by attempting to get that second server banned. After logging into the second server, try to SSH into the Fail2ban server using a nonexistent username:

ssh blah@your_server

Enter random characters into the password prompt and repeat this a few times. Eventually, the error should change from “Permission denied” to “Connection refused.” This indicates that your second server has been banned by Fail2ban.

On your Fail2ban server, you can view the new rule by checking your iptables output. iptables is a command for managing low-level port and firewall rules. If you used DigitalOcean’s guide for initial server setup, you’ll be using ufw to manage firewall rules at a higher level. Running the following command will show you all the firewall rules that ufw has created:

sudo iptables -S

Output
-P INPUT DROP
-P FORWARD DROP
-P OUTPUT ACCEPT
-N f2b-sshd
-N ufw-after-forward
-N ufw-after-input
-N ufw-after-logging-forward
-N ufw-after-logging-input
-N ufw-after-logging-output
-N ufw-after-output
-N ufw-before-forward
-N ufw-before-input
-N ufw-before-logging-forward
-N ufw-before-logging-input
-N ufw-before-logging-output

If you pipe the output of iptables -S to grep to search for the string f2b, you can view the rules added by Fail2ban:

sudo iptables -S | grep f2b

Output
-N f2b-sshd
-A INPUT -p tcp -m multiport –dports 22 -j f2b-sshd
-A f2b-sshd -s 134.209.165.184/32 -j REJECT –reject-with icmp-port-unreachable
-A f2b-sshd -j RETURN

The line containing REJECT –reject-with icmp-port-unreachable will have been added by Fail2ban and should reflect the IP address of your second server.

Thank you for visiting our site, you can check out our other related articles from the links below 🙂

How to Install OpenSSL on Ubuntu Linux

How to Install htop on Linux: A Comprehensive Guide

If you would like to improve yourself in server management, you can purchase a server from our site, experiment and improve yourself in an affordable and reliable environment. I wish you good luck.

Main Page

Conclusion

You should now be able to configure banning policies for your services. Fail2ban is an effective tool for protecting any service that requires authentication.

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button