Blocking IPs for failed ssh logins to jails on FreeBSD

I recently got sslh working on my home FreeBSD server. What made this more complicated is that I wanted ssh to go to one jail and https to go to a different jail.

My jail host is 172.27.1.2, the target for ssh is 172.27.1.4, the target for https is 172.27.1.5. The https jail runs a reverse nginx proxy to forward to various other jails and devices on my home network.

Something that I used to use on linux hosts was fail2ban to block the constant brute force ssh attempts against my system. This time around I decided to try something a little simpler. I found this wiki article describing how to use ipfw and a cron job to update a blocklist.

I had to configure ssh on 172.27.1.4 listen on both 22 and 8022, if I tried to just use 22 I could not reach that jail from my local network.

Below are the pieces that make this all work.

My /usr/local/etc/sslh.conf

# This is a basic configuration file that should provide
# sensible values for "standard" setup.

verbose: false;
foreground: false;
inetd: false;
numeric: false;
transparent: true;
timeout: 2;
user: "root";
pidfile: "/var/run/sslh.pid";


# Change hostname with your external address name.
listen:
(
        { host: "172.27.1.2"; port: "443"; }
);

protocols:
(
        { name: "ssh"; service: "ssh"; host: "172.27.1.4"; port: "8022"; fork: true;},
        { name: "ssl"; host: "172.27.1.5"; port: "443"; log_level: 0; }
);

My /etc/ipfw/sslh.rules

#!/bin/sh

ipfw add 20000 fwd 172.27.1.2,443 log tcp from 172.27.1.5 443 to any out
ipfw add 30000 fwd 172.27.1.2,443 log tcp from 172.27.1.4 8022 to any out

My /root/sshd-fwscan.sh

#!/bin/sh

# based on http://www.freebsdwiki.net/index.php/Block_repeated_illegal_or_failed_SSH_logins
# Copyright (c) 2004,2005 RPTN.Net,
# Copyright (c) 2005 DaveG.ca,
# Copyright (c) 2006 Bob (kba at ats32.ru)
# You may use this code under the GPL, version 2 or newer.
# Updates for IPF by Sasha.by
# Copyright (c) 2018 Matt Okeson-Harlow, https://technomage.net
# 2018-04-29

# changed rule number to 10000 to not impact sslh rules at 20000
# exclude 172.27.1 as that is the home network
# exclude remote hosts that I control
# lowered the count to 3 from 5
# use find to locate auth.log.*.bz2 files modified in the last 2 days

PATH=/bin:/usr/bin:/sbin:/usr/sbin
LOGDIR=/usr/jails/kotilo/var/log
RULE=10000

# exclude local network, yoda and leia
EXCLUDE='172[.]27[.]1|71[.]19[.]155[.]130|71[.]19[.]157[.]86'

# log the current rules before we clear them
ipfw show ${RULE} | logger -p local0.notice -t sshd-fwscan

# clear the current rules
if ipfw show | awk '{print $1}' | grep -q ${RULE} ; then
    ipfw delete ${RULE}
fi

# This catches repeated attempts for both legal and illegal users
# No check for duplicate entries is performed, since the rule
# has been deleted.

(find ${LOGDIR} -mmin -$((60*24*2)) -name "auth.log.*.bz2" -exec bzcat {} \;; cat ${LOGDIR}/auth.log) |
awk '/sshd/ && (/Invalid user/ || /authentication error/) {try[$(NF)]++}
END {for (h in try) if (try[h] > 3) print h}' | egrep -v ${EXCLUDE} |
while read ip
do
        ipfw -q add ${RULE} deny tcp from $ip to any in
done

I added the following to my /etc/crontab

#
# sshd-fwscan to block failed logins
*/10    *   *   *   *   root    /root/sshd-fwscan.sh