Information Technology Grimoire

Version .0.0.1

IT Notes from various projects because I forget, and hopefully they help you too.

Fail2Ban Ubuntu 22.04

What is Fail2Ban?

https://www.digitalocean.com/community/tutorials/how-to-protect-ssh-with-fail2ban-on-ubuntu-22-04

Fail2Ban is a utility that monitors logs and if suspicious activity is detected, it bans the IP using iptables. It is highly customizable for many default products and custom products. It runs as service on startup and can keep track of bad actors over many months of their attack.

Why is this Important?

(1:1444)# ./count_auth_types.sh
Range Sep 25 20:47:16 - Oct 8 07:06:17
Count   Reason
13375   Invalid User
525     Unable to negotiate
1371    Timeout before authentication
0       Invalid user password
3629    Connection closed
0       Accepted Password
12      Accepted Public Key

So, in a 6 day period we had over 35k different attacks on ssh.

Here is the script that parsed out that info:

#!/bin/bash

# Default start date from auth.log
start_date=$(head -1 /var/log/auth.log | awk '{print $1,$2,$3}')
end_date=$(tail -1 /var/log/auth.log | awk '{print $1,$2,$3}')

# Check if there are any gzipped logs, if yes, then update the start date
if [ -n "$(ls /var/log/auth.log.*.gz 2>/dev/null)" ]; then
    gz_start_date=$(zcat /var/log/auth.log.*.gz | head -1 | awk '{print $1,$2,$3}')
    if [ "$gz_start_date" ]; then
        start_date=$gz_start_date
    fi
fi

echo "Range $start_date - $end_date"
echo -e "Count\tReason"

# Function to compute and print the count for a given regex pattern and reason
count_pattern() {
    local pattern=$1
    local reason=$2

    # Count in current log
    local count=$(grep -E "$pattern" /var/log/auth.log | wc -l)

    # Count in gzipped logs, if they exist
    if [ -n "$(ls /var/log/auth.log.*.gz 2>/dev/null)" ]; then
        local gz_count=$(zgrep -E "$pattern" /var/log/auth.log.*.gz | wc -l)
        count=$((count + gz_count))
    fi

    printf "%d\t%s\n" "$count" "$reason"
}

count_pattern 'Invalid user' "Invalid User"
count_pattern 'Unable to negotiate' "Unable to negotiate"
count_pattern 'Timeout before authentication' "Timeout before authentication"
count_pattern 'Invalid user password' "Invalid user password"
count_pattern 'Connection closed' "Connection closed"
count_pattern 'Accepted password' "Accepted Password"
count_pattern 'Accepted publickey' "Accepted Public Key"

Install Fail2Ban

apt update
apt install fail2ban

Configure Basics

You want to edit the /etc/fail2ban/jail.local by making a copy of /etc/fail2ban/jail.conf first.

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

Every service is enabled = false by default and then you add an enabled = true for the services you want to monitor.

The [DEFAULT] section includes things like timers and then you configure the other services as needed. The disabled services can be deleted from your conf.

Start by updating the following, but read through the rest for other options:

[DEFAULT]
bantime.increment = true
bantime.rndtime = 300
bantime.maxtime = 604800
bantime = 60m
findtime = 10m
maxretry = 1

Configure Services

DEFAULT will not actually protect you though. Next you need to enable services. I use sshd, and want it protected, so I am going to modify the sshd seciont and add a custom service at the same time:

SSH

[sshd]

# To use more aggressive sshd modes set filter parameter "mode" in jail.local:
# normal (default), ddos, extra or aggressive (combines all).
# See "tests/files/logs/sshd" or "filter.d/sshd.conf" for usage example and details.
#mode   = normal
port    = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s

Here is the custom service:

[sshd-not22]
enabled  = true
port     = 0:65535
excludeports = 22,80,443
filter   = sshd-not22
logpath  = /var/log/auth.log
maxretry = 1

We want a scanner to be banned for touching any port that isn’t 22,80,443. what about icmp or udp ?

I need to define this service, and it’s done here:

vim /etc/fail2ban/filter.d/sshd-not22.conf
[Definition]
failregex = ^.*Did not receive identification string from <HOST> port [0-9]*$
           ^.*sshd\[.*\]: Invalid user .* from <HOST> port [0-9]*$
           ^.*sshd\[.*\]: Unable to negotiate with <HOST> port [0-9]*: no matching host key type found. Their offer: ssh-rsa,ssh-dss \[preauth\]$
           ^.*sshd\[.*\]: fatal: Timeout before authentication for <HOST> port [0-9]*$
ignoreregex =

NGINX

I am using 2 of the default NGINX protections, the brute force guesser and the fuzzer.

[nginx-http-auth]
enabled = true
port    = http,https
logpath = %(nginx_error_log)s

[nginx-botsearch]
enabled  = true
port     = http,https
logpath  = %(nginx_error_log)s
maxretry = 2

Other Services

I’m not using any other services, so I did not add “enabled = true” to anything else.

Fail2ban Service Manipulation

Enable Service on Reboot

You might want to test everything before making it permanent on every boot:

systemctl enable fail2ban

Restart the Service

systemctl restart fail2ban

Service Status

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

View Custom Service

The status of some service must match the name you called it when creating the custom service. In my example sshd-not22 was the custom service name.

(1:1404)# fail2ban-client status sshd-not22
Status for the jail: sshd-not22
|- Filter
|  |- Currently failed: 0
|  |- Total failed:     0
|  `- File list:        /var/log/auth.log
`- Actions
   |- Currently banned: 0
   |- Total banned:    

Display Banned IP

In my settings the default ban is for 60 minutes, but this doubles if they try again within a 10 minute window up to the max. So these are timers that will fall off eventually. You can see the current bans looking at iptables:

(1:1449)# iptables -S | grep f2b
-N f2b-sshd
-N f2b-sshd-not22
-A INPUT -p tcp -m multiport --dports 0:65535 -j f2b-sshd-not22
-A INPUT -p tcp -m multiport --dports 22 -j f2b-sshd
-A f2b-sshd -s 115.95.180.244/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd -s 24.199.119.46/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd -s 128.199.182.19/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd -s 146.190.60.250/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd -s 182.16.245.79/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd -s 165.154.147.47/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd -s 43.155.163.36/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd -s 170.106.196.12/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd -s 62.99.74.174/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd -s 165.22.127.104/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd -s 170.64.187.53/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd -s 170.64.174.82/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd -j RETURN
-A f2b-sshd-not22 -s 115.95.180.244/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd-not22 -s 24.199.119.46/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd-not22 -s 128.199.182.19/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd-not22 -s 182.16.245.79/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd-not22 -s 165.154.147.47/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd-not22 -s 43.155.163.36/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd-not22 -s 170.106.196.12/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd-not22 -s 62.99.74.174/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd-not22 -s 76.21.196.191/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd-not22 -j RETURN

View Ban Logs

Ban logs explain a bit more of what the attacker was doing that triggered the ban.

cat /var/log/fail2ban.log 
  • Found = we saw you do something, but haven’t banned you yet
  • Ban = you just got banned
  • Retore Ban = service restarted, so we are reloading all bans