Fail2Ban - Un cane da guardia per NGINX

Fail2Ban è un software che utilizza iptables per bloccare automaticamente i client che continuano a fallire i tentativi di accesso, che provano a sfruttare vulnerabilità dei servizi web, e molto altro. Vediamo insieme come implementarlo sulla nostra infrastruttura.

Condividi
Fail2Ban - Un cane da guardia per NGINX

Introduzione

Nelle guide precedenti abbiamo visto come implementare NGINX per svolgere varie funzioni, come:

Ma nell'articolo di introduzione ad NGINX, ho specificato che un software di questo tipo ci permette anche di aumentare notevolmente la sicurezza dell'infrastruttura.

Come può farlo, di preciso?

Innanzitutto, facendo passare tutto il traffico da NGINX è possibile approfittare di questo accentramento per concentrare sui nodi NGINX tutte le configurazioni di sicurezza a livello di firewall e regole di ispezione del traffico.

In questa guida, vedremo come implementare una soluzione open source che utilizza il firewall iptables e criteri di lettura dei log per rilevare e bloccare i tentativi di intrusione: Fail2Ban

Installazione

(testato su Ubuntu Server 24.04)

Fail2Ban si installa con pochi e semplici comandi, vediamoli insieme.

Installate fail2ban ed iptables

sudo apt install fail2ban iptables

Controllate lo stato del servizio di fail2ban

fail2ban-client status

Create il file jail.local partendo dal file di template jail.conf

cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
💡
d'ora in poi, modificate solo il file .local che abbiamo appena creato, perchè è questo he Fail2Ban andrà a leggere

Con questo si conclude lo step di installazione.

Andiamo ora a vedere come configurare Fail2Ban per abilitare le regole che ci servono e personalizzarne il comportamento.

Configurazione

Come funziona la configurazione

  • Il file di configurazione che abbiamo appena creato (jail.local) contiene una lista di regole che Fail2Ban applicherà leggendo i file di log che andremo ad indicare al suo interno.
  • Ogni regola che andremo a creare diverrà una jail (dall'inglese, prigione). Un client che soddisfa i requisiti indicati nella jail (ad esempio, 3 tentativi di accesso non riusciti) verrà messo nella jail corrispondente a quella regola.
  • Dentro ad ogni regola è anche specificata una lista di azioni da intraprendere. Dal più banale "blocca questo client sul firewall" ad "invia una notifica telegram", è possibile indicare varie azioni da far compiere a Fail2Ban
  • Un client che viene spedito in prigione, verrà bloccato esclusivamente utilizzando le regole specificate in quella jail (ad esempio, se ho una regola per SSH ed una per un server web, ed un client sbaglia l'autenticazione SSH, potrà comunque utilizzare il server web)

Creare delle eccezioni

Prima di andare avanti con la configurazione, è il caso di censire alcune eccezioni su Fail2Ban per evitare di bloccarci da soli durante i nostri test.

Modificate il file jail.local

nano /etc/fail2ban/jail.local 

Andate alla riga del parametro ignoreip ed aggiungete le subnet e/o gli indirizzi che Fail2Ban non deve mai bannare

Per esempio, per ignorare 192.168.120.1 e tutta la rete 192.168.0.0:

ignoreip = 192.168.0.0/24,192.168.120.1

Creare una regola per l'autenticazione SSH

In questo esempio diremo a Fail2Ban di bloccare per 4 ore tutti i client che sbagliano 3 volte la password di SSH nell'arco di 30 secondi.

Modifichiamo il file jail.local

nano /etc/fail2ban/jail.local 

Impostiamo la sezione [sshd] relativa ad SSH come segue:

[sshd]
enabled = true
filter = sshd
maxretry = 3
port    = ssh
backend = systemd
findtime = 30
bantime = 4h
  • enabled → indica se la jail è attiva o meno
  • filter → fa riferimento al filtro presente in /etc/fail2ban/filter.d/ relativo ad sshd. Questo file contiene informazioni relative a come interpretare i file di log di sshd
  • maxretry → il numero massimo di tentativi errati, in questo caso relativi all'autenticazione tramite SSH
  • port → la porta da usare per la regola di firewall, che viene creata nel caso in cui un client debba essere bloccato. Indicando ssh come in questo esempio, verrà usata la porta di default di SSH (22)
  • backend → indica che per leggere i log di sshd, Fail2Ban dovrà utilizzare systemd (perchè sshd è un servizio di systemd)
  • findtime → il tempo massimo (in questo caso, essendo senza unità di misura è in secondi) che deve passare tra i tentativi massimi (in questo caso 3) affinchè la regola sia soddisfatta
  • bantime → dopo quante ore cancellare il blocco sul firewall per ciascun client che viene bannato

Come avrete notato, in questo esempio non è specificata alcuna azione da intraprendere alla soddisfazione della regola. Quando non specificata, l'azione che viene intrapresa è quella indicata al parametro action_ nel file jail.local, sotto alla sezione [DEFAULT]

Di default, questa azione crea una regola su iptables che rifiuta le connessioni del client da bloccare, sulla porta indicata nella regola.


Ora che abbiamo configurato la nostra prima regola, riavviamo il servizio di fail2ban per testarne il funzionamento

sudo systemctl restart fail2ban.service
systemctl status fail2ban.service

Provate, da un client che non sia stato configurato nel parametro ignoreip, ad autenticarvi tramite SSH alla macchina sbagliando la password più volte. Dopo tre tentativi, dovreste perdere la connessione.

Potete monitorare il funzionamento in due modi:

  • Leggendo in diretta il log di Fail2Ban
tail -f /var/log/fail2ban.log
  • Controllando lo status della Jail sshd
fail2ban-client status sshd

Dato che si tratta di un test, potete ora sbannare manualmente il vostro client

sudo fail2ban-client set %nomejail% unbanip %indirizzoIP%

Creare una regola per NGINX

Se state usando NGINX come reverse Proxy, può esservi molto utile configurare alcune jail relative ad NGINX. In questo esempio ne configureremo due:

  • nginx-http-auth che monitora i vostri webserver in reverse proxy che espongono autenticazione http, e ci permetterà di bannare i client che falliscono l'autenticazione troppe volte
  • nginx-bad-clients (che andremo ad importare in quanto regola personalizzata) che blocca gran parte dei bot che cercano di rilevare le vulnerabilità dei vostri servizi web
💡
I seguenti passaggi devono essere eseguiti sulla macchina in cui è installato NGINX

Prima di tutto, modifichiamo il file jail.local

nano /etc/fail2ban/jail.local 

Impostiamo la regola [nginx-http-auth]

[nginx-http-auth]

enabled  = true
port     = http,https
logpath = /var/log/nginx/error.log

Poi creiamo la regola [nginx-bad-clients] (vi consiglio di farlo vicino alle altre regole relative ad NGINX, per comodità)

[nginx-bad-clients]
port     = http,https
filter   = nginx-bad-clients
logpath  = /var/log/nginx/*access.log
maxretry = 3
findtime = 30s
bantime  = 4h
enabled  = true

Notate come il valore di logpath sia diverso per le due regole. Questo perchè:

  • nginx-http-auth controlla esclusivamente i tentativi di accesso falliti, che vengono loggati in error.log
  • nginx-bad-clients controlla invece i comportamenti dei client che accedono (anche se con successo) a vari path dei vostri servizi web usando dei pattern sospetti

nginx-bad-clients quini agisce anche quando il client non sta facendo di per sé qualcosa di sbagliato, ma sta palesemente curiosando in giro

Come ho specificato poco fa, il filtro nginx-bad-clients in realtà non esiste di default su Fail2Ban, perché si tratta di un filtro custom. Andiamo a crearlo

Create il filtro nginx-bad-clients

nano /etc/fail2ban/filter.d/nginx-bad-clients.local

Incollate il seguente contenuto

# Fail2Ban filter to match web requests for misbehaving clients
#
 
[INCLUDES]
 
# Load regexes for filtering
before = botsearch-common.conf
 
[Definition]
 
failregex = ^<HOST> - \S+ \[\] "[^"]*" 429 .+$
            ^<HOST> - \S+ \[\] "[^"]*" 404 .+$
 
ignoreregex = 
 
datepattern = {^LN-BEG}%%ExY(?P<_sep>[-/.])%%m(?P=_sep)%%d[T ]%%H:%%M:%%S(?:[.,]%%f)?(?:\s*%%z)?
              ^[^\[]*\[({DATE})
              {^LN-BEG}
 
# DEV Notes:
# Based on nginx-botsearch filter
# 
# Author: Dan Langille

Una volta salvato, riavviate fail2ban e controllate che non abbia segnalato errori

sudo systemctl restart fail2ban.service
systemctl status fail2ban.service

Come indicato per sshd, potete controllare lo stato di fail2ban e delle nostre nuove jail tramite i seguenti comandi:

  • Leggendo in diretta il log di Fail2Ban
tail -f /var/log/fail2ban.log
  • Controllando lo status della Jail nginx-bad-clients e nginx-http-auth
fail2ban-client status nginx-bad-clients
fail2ban-client status nginx-http-auth

Come inviare notifiche Telegram

Nell'introduzione alla configurazione, ho menzionato la possibilità di inviare notifiche Telegram quando Fail2Ban banna (o sbanna) un client.

Integrazioni come questa, e molte altre, sono possibili grazie al grado di personalizzazione che Fail2Ban ci offre.

In questo esempio, vediamo come configurare l'invio notifiche tramite Telegram.

Prima di tutto, creiamo la cartella /etc/failban/scripts dove tenere questo ed i nostri altri script personalizzati

sudo mkdir /etc/fail2ban/scripts

Creare il file /etc/fail2ban/scripts/fail2ban-telegram.sh col seguente contenuto (modificando il bot token ed i chat id):

sudo nano /etc/fail2ban/scripts/fail2ban-telegram.sh
#!/bin/bash

# Sends text messages using Telegram
# to alert webmaster of banning.

# Require one argument, one of the following
# start
# stop
# ban
# unban
# Optional second argument: Ip for ban/unband


# Display usage information
function show_usage {
  echo "Usage: $0 action <ip>"
  echo "Where action start, stop, ban, unban"
  echo "and IP is optional passed to ban, unban"
  exit
}


# Send notification
function send_msg {
  apiToken=<bottoken>
  chatId=<chatid>
  url="https://api.telegram.org/bot$apiToken/sendMessage"

  curl -s -X POST $url -d chat_id=$chatId -d text="$1"
  exit
}


# Check for script arguments
if [ $# -lt 1 ]
then
  show_usage
fi


# Take action depending on argument
if [ "$1" = 'start' ]
then
  msg='Fail2ban+just+started.'
  send_msg $msg
elif [ "$1" = 'stop' ]
then
  msg='Fail2ban+just+stopped.'
  send_msg $msg
elif [ "$1" = 'ban' ]
then
  msg=$([ "$2" != '' ] && echo "Fail2ban+just+banned+$2+on+service+$3." || echo 'Fail2ban+just+banned+an+ip+on+service+$3.' )
  send_msg $msg
elif [ "$1" = 'unban' ]
then
  msg=$([ "$2" != '' ] && echo "Fail2ban+just+unbanned+$2+on+service+$3." || echo "Fail2ban+just+unbanned+an+ip+on+service+$3." )
  send_msg $msg
else
  show_usage
fi

Creare il file /etc/fail2ban/action.d/telegram.conf col seguente contenuto:

sudo nano /etc/fail2ban/action.d/telegram.conf
# Fail2Ban configuration file
#
# Author: MushaGH
#
#

[Definition]

# Option:  actionstart
# Notes.:  command executed once at the start of Fail2Ban.
# Values:  CMD
#
actionstart = /etc/fail2ban/scripts/fail2ban-telegram.sh start

# Option:  actionstop
# Notes.:  command executed once at the end of Fail2Ban
# Values:  CMD
#
actionstop = /etc/fail2ban/scripts/fail2ban-telegram.sh stop

# Option:  actioncheck
# Notes.:  command executed once before each actionban command
# Values:  CMD
#
actioncheck =

# Option:  actionban
# Notes.:  command executed when banning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
# Tags:    See jail.conf(5) man page
# Values:  CMD
#
actionban = /etc/fail2ban/scripts/fail2ban-telegram.sh ban <ip> <name>

# Option:  actionunban
# Notes.:  command executed when unbanning an IP. Take care that the
#          command is executed with Fail2Ban user rights.
# Tags:    See jail.conf(5) man page
# Values:  CMD
#
actionunban = /etc/fail2ban/scripts/fail2ban-telegram.sh unban <ip> <name>

[Init]

init = 123

Riavviare Fail2Ban e controllare che non dia errore relativo al modulo telegram

sudo systemctl restart fail2ban.service
systemctl status fail2ban.service

Se non viene segnalato alcun errore, possiamo implementare l'azione "telegram" sulle nostre jail

Modifichiamo il file jail.local

sudo nano /etc/fail2ban/jail.local

Ed aggiungiamo a tutte le jail dove vogliamo abilitare le notifiche telegram il parametro action

action = iptables[name=SSH,port=22,protocol=tcp]
         telegram

In questo esempio, la action è relativa alla jail sshd. Dobbiamo specificare una azione per riga:

  • iptables[name,port,protocol] crea una regola firewall su iptables che blocca tutte le comunicazioni sulla porta 22 TCP per l'IP del client da bannare
  • telegram invece invia una notifica telegram usando i parametri impostati in precedenza

Ripetete questa configurazione su tutte le jail dove abilitare le notifiche Telegram, modificando name, port e protocol a seconda del servizio in questione

Riavviate infine Fail2Ban e controllate che non ci siano errori segnalati

sudo systemctl restart fail2ban.service
systemctl status fail2ban.service

Adesso quando un client viene bannato, riceverete anche una notifica su Telegram!