Ceci est une ancienne révision du document !


Fail2ban et Iptables

Fail2ban logiciel me file des boutons mais je n'ai pas vraiment trouvé de remplaçant. Son principal mérite est de délayer les attaques par brute-force ; en principe notre serveur n'y est pas vulnérable, mais les principes et la sécurité… Bref, c'est aussi bien de l'avoir comme protection supplémentaire, selon le principe du tamis : si ça passe une couche, avec un peu de chance ça ne passera pas la suivante.

En principe aussi, il marche bien avec nftables, le nouveau pare-feu par défaut sur debian. Sauf que là aussi le principe se heurte à la pratique et j'ai le droit à des erreurs si je tente de l'utiliser. En plus y'a pas autant de tutos avec nftables et je trouve ce soft encore plus illisible qu'iptable, c'est dire… Comme iptable et nftables peuvent cohabiter, je vais gérer fail2ban avec iptable, et lui adjoindre ipset afin de gérer les ip qui ont un peu trop abusé.

apt install fail2ban ipset iptable iptables-persistent whois python3-systemd 

Iptable

Iptable-persistent permet de charger des règles au démarrage, en principe situées dans /etc/iptables/.

Actuellement mon routeur me coince en ipv4 (/etc/iptables/rules.v4) donc mes règles ne concernent que ça, il faudra adapter pour ipv6.

Son contenu actuel :

# source https://antoinelounis.com/informatique/securite/configuration-fail2ban-iptables-debian-raspberry/
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]

# Allow internal traffic on the loopback device
-A INPUT -i lo -j ACCEPT

# Continue connections that are already established or related to an established connection
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# Drop non-conforming packets, such as malformed headers, etc.
-A INPUT -m conntrack --ctstate INVALID -j DROP

# SSH
-A INPUT -p tcp --dport 226 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT

# HTTP + HTTPS
-A INPUT -p tcp -m multiport --dports 80,443 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT


# Email (postfix + dovecot)
# 25 = smtp, 587 = submission and 993 = IMAPS, 143 imap, 110 pop
-A INPUT -p tcp -m multiport --dports 25,587,993,110,143 -j ACCEPT

# NTP
-A INPUT -p udp --dport 123 -j ACCEPT

# Chain for preventing ping flooding - up to 6 pings per second from a single
# source, again with log limiting. Also prevents us from ICMP REPLY flooding
# some victim when replying to ICMP ECHO from a spoofed source.
-N ICMPFLOOD
-A ICMPFLOOD -m recent --name ICMP --set --rsource
#-A ICMPFLOOD -m recent --name ICMP --update --seconds 1 --hitcount 6 --rsource --rttl -m limit --limit 1/sec --limit-burst 1 -j LOG --log-prefix "iptables[ICMP-flood]>
-A ICMPFLOOD -m recent --name ICMP --update --seconds 1 --hitcount 6 --rsource --rttl -j DROP
-A ICMPFLOOD -j ACCEPT

# Permit useful IMCP packet types.
# Note: RFC 792 states that all hosts MUST respond to ICMP ECHO requests.
# Blocking these can make diagnosing of even simple faults much more tricky.
# Real security lies in locking down and hardening all services, not by hiding.
-A INPUT -p icmp --icmp-type 0  -m conntrack --ctstate NEW -j ACCEPT
-A INPUT -p icmp --icmp-type 3  -m conntrack --ctstate NEW -j ACCEPT
-A INPUT -p icmp --icmp-type 8  -m conntrack --ctstate NEW -j ICMPFLOOD
-A INPUT -p icmp --icmp-type 11 -m conntrack --ctstate NEW -j ACCEPT

# Drop all incoming malformed NULL packets
-A INPUT -p tcp --tcp-flags ALL NONE -j DROP

# Drop syn-flood attack packets
-A INPUT -p tcp ! --syn -m conntrack --ctstate NEW -j DROP

# Drop incoming malformed XMAS packets
-A INPUT -p tcp --tcp-flags ALL ALL -j DROP
COMMIT

# Usage des tables déclarées avec ipset
-A INPUT -m set --match-set blocklist_ip src -j DROP

Ipset

Créer une entrée pour toutes les ip qui vont être renvoyées dans les limbes (adapter le nom blocklist_ip:

ipset create blocklist_ip hash:ip

Faire en sorte qu'iptables utilise cette liste d'ipset (appelée chaine) :

iptables -A INPUT -m set --match-set blocklist_ip src -j DROP

Et à présent pour alimenter la liste des ip définitivement bannies, ça sera ce genre de commande (remplacer $ip par l'ip) :

ipset add blocklist_ip $ip

Commandes ipset

Pour lister les ip et “chaînes” :

ipset list

Pour juste lister les chaînes :

ipset list -n

Pour enlever une des ip sur notre chaîne “bloclist_ip” :

ipset del blocklist_ip X.X.X.X

Fail2ban

On crée un fichier dans /etc/fail2ban/jail.d/custom.conf qui va contenir nos règles.

Il faudra aussi les tester, regarder les logs d'erreurs, etc… parce que c'est assez facile de croire que Fail2ban fonctionne, alors que non, il a bugué sur un truc obscur et en réalité ne fait rien du tout.

Sur ssh : il ne faut pas indiquer ssh et sftp dans les ports, juste les numéros. Et ça va mieux. J'ai galéré un moment pour que ça marche, à cause de ça…

Le principe est simple : on regarde dans /etc/fail2ban/jail.conf les prisons disponibles et les diverses options, puis dans /etc/fail2ban/jail.d/custom.conf on ne copie que ce qu'on veut changer par rapport au fonctionnement par défaut, et en particulier les “jails” qu'on va activer.

Ça pourrait probablement s'optimiser en créant une jail sur mesure avec toutes les regexp utiles, en particulier sur apache ici. À ce stade, je fais confiance aux devs de Fail2ban pour que les filtres soient efficaces contre pas mal de choses. Mais l'examen des logs montre que c'est quand même perfectible.

Voici à quoi ressemble mon custom.conf actuellement, sur un serveur mail avec un bout de site apache.

[DEFAULT]
# Envoi un mail pour voir que ça marche...
destemail = monmail@mondomaine.org
sender = root
action = %(action_mwl)s
# Cette "action" envoie un courriel avec le détail de qui et pourquoi on bannit. C'est verbeux mais j'aime bien.

# Actions par défaut. Peut être surchargé dans les jails.
# Nombre de fois où une ip peut se planter 
maxRetry = 4
# Regarder les logs sur ce temps là pour voir à quel point les ip retentent le coup
# Pas besoin de mettre trop long, ça bouffe de la mémoire et on a autre chose pour virer 
# celles qui s'acharnent
findtime = 3h
# Durée du bannissement. 
bantime  = 3d
# Si on ban quelque part, c'est partout (en principe)
banaction = iptables-multiport

## Les services surveillés, dits "jail"
[sshd]
enabled = true
port = 22,226
bantime  = 10m

[apache-auth]
enabled = true
[apache-badbots]
enabled = true
[apache-noscript]
enabled = true
[apache-overflows]
enabled = true
[apache-nohome]
enabled = true
[apache-botsearch]
enabled = true
[apache-fakegooglebot]
enabled = true
[apache-modsecurity]
enabled = true
[apache-shellshock]
enabled = true

[dovecot]
enabled = true
port = pop3,pop3s,imap2,imaps,submission,465,sieve
#filter    = dovecot

[postfix]
enabled = true
port  = pop3,pop3s,imap2,imaps,submission,465,sieve
#filter = postfix

[sieve]
enabled = true

À faire

Il faut améliorer les regexp (avec fail2ban-regex). pour virer plus large : - toute ip qui tente de se co en ssh avec un user qui n'est pas autorisé ⇒ ban définitif - toute ip qui passe des commandes à postfix qui ne sont pas “valides” ⇒ idem

La ligne postfix que j'aimerais détecter pour bannir, car trop louche :

2023-07-28T09:19:04.783707+02:00 poste postfix/smtpd[608140]: warning: non-SMTP command from scan-15n.shadowserver.org[184.105.247.252]: GET / HTTP/1.1

Bannir définitivement les IP harcelantes

Lister les occurrences d'ip bannies

cat /var/log/fail2ban.lo* | grep -v "Restore" | grep "Ban" |awk '{print $6" "$8}'|sort|uniq -c|sort -nr | awk '{gsub(/^[ \t]+/,""); print$0}'
  • cat /var/log/fail2ban.lo* : va regarder tous les fichiers de log de fail2ban
  • grep -v “Restore” : exclure les moments où on restaure les ip (ça fout le dawa dans les résultats)
  • grep “Ban” : garder, dans ce qui reste, toutes les lignes où on bannit un truc
  • awk '{print $6" "$8}' : garder seulement la 6e et 7e partie (celle avec le nom du service et celle avec l'ip)
  • sort une fois pour que les ip identiques soient les unes à la suite des autres
  • uniq -c réunit les lignes communes et affiche devant le nombre de fois qu’elles apparaissent
  • un dernier sort (avec l’option reverse et l’option number) pour faire un tri inverse par nombre
  • awk '{gsub(/^[ \t]+/,""); print$0}' : compliqué à lire celui-ci. Il enlève les espaces au début, ce qui est utile pour se servir du truc dans un script par la suite.

Très beau, très simple, fonctionnel.

Ça serait pas mal qu'un petit cron lance de temps en temps une vérification et bannisse définitivement les ip qui ont un peu trop essayé.

Sources

CC Attribution-Noncommercial-Share Alike 4.0 International Driven by DokuWiki
pratique/informatique/fail2ban.1691565469.txt.gz · Dernière modification : 09/08/2023 09:17 de Zatalyz