Restricting server access to SSH-only connections is a great way to secure it. Although the port changed, the SSH daemon exposes itself to the internet. That way, it becomes a possible target for brute force attacks. A fate the daemon shares with all other public-facing services.

This post will show how to mitigate the risk of attacks on this vector. We will use a powerful tool called fail2ban for this. It will enforce a rate limit onto IPs and block them if they exceed it.

Scenario

This scenario assumes the following:

Boot the host and login as root.

Preparing the host

If you are running an RHEL UNIX distro, you need to enable EPEL first:

yum install epel-release

Install fail2ban

The installation of fail2ban is straightforward:

# change to match your package manager
yum install fail2ban

The next step is to change the configuration to suit our needs.

Local configuration

fail2ban keeps its configs at /etc/fail2ban. The default config is /etc/fail2ban/jail.conf. We can create a /etc/fail2ban/jail.local to overwrite the default config. This way, made changes persist, even if a package update replaces jail.conf.

Create a copy of the default config as the starting point:

cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Open jail.local and change the following values:

bantime = 15m
findtime  = 15m
maxretry = 3

This will block attackers for 15 minutes after three failed login attempts.

Configuration per service

We can add new configurations by adding them to /etc/fail2ban/jail.d/*.conf. fail2ban applies them in alphabetical order. To prevent overwriting, each service gets an encapsulated file.

Let us start with our SSH service:

vi /etc/fail2ban/jail.d/sshd.conf

And then add the following service configuration:

[sshd]

enabled = true
port = 1234 # this should be your custom ssh port
filter  = sshd # points to /etc/fail2ban/filter.d/ssdh.conf
logpath  = /var/log/secure # change to your sshd logs
action = firewallcmd-allports # remove if you don't have firewallcmd

With this configuration, we tell fail2ban to enable the protection of the SSH daemon. We point it to the logs and name the filter used to parse them for alerting lines. fail2ban will ban an IP if it exceeds the maxretry limit in the findtime amount of time. This will trigger the action. In this case, block the IP on every port. If using -allports, you can drop the port definition.

Enable fail2ban

The last step on the road to brute force protection is to enable the fail2ban daemon:

systemctl enable fail2ban
systemctl start fail2ban

To test if everything is working, we can check the status of fail2ban:

fail2ban-client status
# Status
# |- Number of jail:	1
# `- Jail list:	sshd

fail2ban-client status sshd
# Status for the jail: sshd
# |- Filter
# |  |- Currently failed:	0
# |  |- Total failed:	0
# |  `- Journal matches:	_SYSTEMD_UNIT=sshd.service + _COMM=sshd
# `- Actions
#    |- Currently banned:	0
#    |- Total banned:	0
#    `- Banned IP list:

Congratulations, you did it. You hopefully got a better understanding of fail2ban and brute force protection along the way.

Going one step further

You could improve on the topic by:

  • use netstat -tulnp to list all public-facing services
  • improve security further by using fail2ban to protect them

Let me end this excursion with a quote for your meditation

There’s no silver bullet solution with cyber security, a layered defense is the only viable defense.
— James Scott