The simple blog of a humble hacker

Preventing brute-force attacks on SSH with SSHGuard

Mark Veidemanis

Ever looked at the SSH logs for a newly provisioned box? Horrifying, isn't it? So many failed attempts, in so little time.

Let's set up SSHGuard to slam the door on the noses of anyone who tries this. Doesn't really add any security if you only allow public key authentication. It's pretty cool though, so let's do it!

SSHGuard will watch over the SSH logs and look for repeated failed authentication attempts, and block the originating IP addresses when the score of the IP matches or exceeds a predefined threshold. Usually, the score increases by 10 per failed attempt. A threshold of 30 gives you three attempts before you're locked out for a certain amount of time, defaulting to 2 minutes. If you keep trying, the time you are locked out for will increase by a factor of 1.5 each time.

Install SSHGuard with apk add sshguard on Alpine, and pkg_add sshguard on OpenBSD. You'll also want to enable it on startup. It's rc-update add sshguard on Alpine and rcctl enable sshguard on OpenBSD.

Next, it's time to make SSHGuard play nice with your firewall.

On Linux, UFW is a common choice for a no-bullshit firewall interface. To make it work with SSHGuard, add the following to /etc/ufw/before.rules, after the # allow all on loopback section:

# hand off control for sshd to sshguard
:sshguard - [0:0]
-A ufw-before-input -p tcp --dport 22 -j sshguard

If you use IPv6, do the same for /etc/ufw/before6.rules, but add this instead:

:sshguard - [0:0]
-A ufw6-before-input -p tcp --dport 22 -j sshguard

Change 22 to your SSH port if you've changed it.

If you're using OpenBSD, simply add this to /etc/pf.conf:

table <sshguard> persist
block in quick proto tcp from <sshguard>

It doesn't matter where in the file you put these. As we're using the quick keyword, no more rules are evaluated after this one, if the packet being processed is one from any of the addresses in the sshguard table.

Remember to reload the rules with pfctl -f /etc/pf.conf.

Now, let's get into the SSHGuard configuration. It's a simple shell script located at /etc/sshguard.conf. If it doesn't exist, copy over the example configuration. You'll need to configure the following values for SSHGuard to work:

#### REQUIRED CONFIGURATION ####
# Full path to backend executable (required, no default)
BACKEND="/usr/libexec/sshg-fw-iptables"

# Space-separated list of log files to monitor. (optional, no default)
FILES="/var/log/auth.log"

# IP addresses listed in the WHITELIST_FILE are considered to be
# friendlies and will never be blocked.
WHITELIST_FILE=/etc/whitelist

Make the following changes for OpenBSD:

BACKEND="/usr/local/libexec/sshg-fw-pf"
FILES="/var/log/authlog

Stick a common IP you use to connect to the server into /etc/whitelist so you don't inadvertently lock yourself out.

You may also want to customise these options:

#### OPTIONS ####
# Block attackers when their cumulative attack score exceeds THRESHOLD.
# Most attacks have a score of 10. (optional, default 30)
THRESHOLD=30

# Block attackers for initially BLOCK_TIME seconds after exceeding THRESHOLD.
# Subsequent blocks increase by a factor of 1.5. (optional, default 120)
BLOCK_TIME=120

# Remember potential attackers for up to DETECTION_TIME seconds before
# resetting their score. (optional, default 1800)
DETECTION_TIME=1800

# Colon-separated blacklist threshold and full path to blacklist file.
# (optional, no default)
BLACKLIST_FILE=90:/etc/blacklist

The comments do a good job of explaining what the options do, however the last one isn't so clear. The blacklist file is where IPs get added when they reach the blacklist threshold, which is 90 in this case, or 3 sets of 3 failed attempts. Once added, packets from these IPs are permanently blocked, until the IPs are manually removed from the blacklist file. Also, do make sure to create the blacklist file if you want to use the option.

You're all set. Start the service with service sshguard start on Alpine and rcctl start sshguard on OpenBSD.