Protéger le port SSH sur Ubuntu avec Fail2ban - Installation et configuration

Introduction

Sur les serveurs Ubuntu, ufw (Uncomplicated Firewall) est un bon outil pour appliquer des règles de parefeu sur les ports sans avoir à utiliser les commandes iptables qui sont assez difficiles.

root@vpsfrsqlpac2$ ufw status verbose

To                         Action      From
--                         ------      ----
22                         ALLOW IN    Anywhere                   # SSH
8600:8800/tcp              ALLOW IN    10.128.30.32               # MariaDB CS 
20501:20509/tcp            ALLOW IN    10.128.30.32               # PostgreSQL

Mais ce n’est pas suffisant. Pour les services SSH, on peut définir un port différent de celui par défaut (port 22), mais malheureusement certains produits, MariaDB ColumnStore par example, utilisent ssh dans leurs routines d’administration sans donner la possibilité d’utiliser un autre port que son port 22 par défaut.

Par conséquent, le port 22 doit être ouvert et dans le fichier /var/log/auth.log on note très rapidement des tentatives de connexions provenant de machines inconnues, parfois avec des intervalles de temps très courts entre les tentatives pour une même IP.

/var/log/auth.log
Feb  2 06:25:42 vpsfrsqlpac2 sshd[6764]: Disconnected from invalid user tester 186.4.184.218 port 42416 [preauth]
Feb  2 06:29:32 vpsfrsqlpac2 sshd[6830]: Invalid user tester from 123.108.35.186 port 41670
Feb  2 06:29:34 vpsfrsqlpac2 sshd[6830]: Failed password for invalid user tester from 123.108.35.186 port 41670 ssh2
Feb  2 06:29:34 vpsfrsqlpac2 sshd[6830]: Disconnected from invalid user tester 123.108.35.186 port 41670 [preauth]
Feb  2 06:35:50 vpsfrsqlpac2 sshd[6933]: Invalid user tester from 123.108.35.186 port 45178
Feb  2 06:35:52 vpsfrsqlpac2 sshd[6933]: Failed password for invalid user tester from 123.108.35.186 port 45178 ssh2

Fail2Ban est l’outil complémentaire à ufw afin de protéger un système Ubuntu de ces attaques.

  • Son installation est simple.
  • Fail2Ban gère les règles iptables pour interdire des adresses IP pour une période en suivant des conditions qui sont configurables.

Fail2Ban est un package Python et peut être installé dans un environnement virtuel Python, c’est la cas dans cet article.

L’installation doit être réalisée avec les privilèges root car cet outil gère des règles iptables.

Installation

Préparation de l’environnement virtuel Python

Python 3.8 avec virtualenv est installé dans le répertoire /opt/python/python-3.8. Le fichier /opt/python/.python-3.8 source les variables d’environnement nécessaires :

/opt/python/.python-3.8
export PYHOME=/opt/python/python-3.8
export PATH=$PYHOME/bin:$PATH
export LD_LIBRARY_PATH=$PYHOME/lib:$LD_LIBRARY_PATH
export PYTHONPATH=/opt/python/packages

L’environnement virtuel Python pour fail2ban est installé dans le répertoire /opt/monitoring/fail2ban :

root@vpsfrsqlpac2$ source /opt/python/.python-3.8
          
root@vpsfrsqlpac2$ virtualenv --system-site-packages /opt/monitoring/fail2ban

L’environnement est activé :

root@vpsfrsqlpac2$ source /opt/monitoring/fail2ban/bin/activate
(fail2ban) root@vpsfrsqlpac2$ which python3
/opt/monitoring/fail2ban/bin/python3

Package Fail2ban

Télécharger fail2ban depuis GitHub et lancer son installation :

(fail2ban) root@vpsfrsqlpac2$ cd /opt/monitoring/setup
(fail2ban) root@vpsfrsqlpac2$ wget https://github.com/fail2ban/fail2ban/archive/0.11.1.tar.gz
(fail2ban) root@vpsfrsqlpac2$ tar -xvzf 0.11.1.tar.gz
(fail2ban) root@vpsfrsqlpac2$ cd fail2ban-0.11.1
(fail2ban) root@vpsfrsqlpac2$ python3 setup.py install
…
changing mode of /opt/monitoring/fail2ban/bin/fail2ban-testcases to 755

Please do not forget to update your configuration files.
They are in "/etc/fail2ban/".

You can also install systemd service-unit file from "build/fail2ban.service"
resp. corresponding init script from "files/*-initd".

Le package fail2ban est installé avec succès dans l’environnement virtuel /opt/monitoring/fail2ban.

Configuration de Fail2ban

Tous les fichiers de configuration sont installés dans le répertoire /etc/fail2ban.

fail2ban.conf

Le fichier de configuration principal fail2ban.conf ne contient pas beaucoup de paramètres à configurer. Dans celui-ci, on peut personnaliser la verbosité, les répertoires et noms de fichiers pour le fichier de log, le fichier pid…

/etc/fail2ban/fail2ban.conf
[DEFAULT]
loglevel = INFO
logtarget = /var/log/fail2ban.log
socket = /var/run/fail2ban/fail2ban.sock
pidfile = /var/run/fail2ban/fail2ban.pid
dbfile = /var/lib/fail2ban/fail2ban.sqlite3
dbpurgeage = 15d

Fail2ban utilise une base sqlite3. Lors de l’installation, mettre à jour le paramètre de rétention dbpurgeage à au moins 8 jours, ce sera déjà fait pour la configuration des attaques récidivistes plus tard.

jail.local

Les règles Fail2Ban sont configurées dans le fichier /etc/fail2ban/jail.local à créér. Copier le fichier /etc/fail2ban/jail.conf vers /etc/fail2ban/jail.local. Le fichier local évite de perdre toute configuration lors des mises à niveau de fail2ban :

(fail2ban) root@vpsfrsqlpac2$ cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
/etc/fail2ban/jail.local
[DEFAULT]
ignoreip = 127.0.0.1/8 10.128.31.48

# "bantime" is the number of seconds that a host is banned.
bantime  = 30m

# A host is banned if it has generated "maxretry" during the last "findtime" seconds.
findtime  = 10m

# "maxretry" is the number of failures before a host get banned.
maxretry = 3
  • définir les IPs à ignorer dans la section DEFAULT, et plus particulièrement les IPs de ses propres machines.
  • le temps de bannissement par défaut peut être ajusté : ici il est fixé à 30 minutes au lieu des 10 minutes par défaut.

À présent les règles de bannissement pour ssh sont activées :

/etc/fail2ban/jail.local
[sshd]
enabled = true
port    = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s

De nombreuses sections sont disponibles et prédéfinies dans le fichier jail.local : sshd, apache, mysql…

Tous les paramètres définis dans la section DEFAULT (bantime, ignoreip …) peuvent être personnalisés dans chaque section. Il faut faire attention au paramètre ignoreip, si des adresses IP spécifiques sont définies dans une section, cela annule la valeur par défaut des IPs définies, il n’y a pas de fusion.

Dans le "langage fail2ban", une section dans le fichier de configuration est une "jail" (prison). La jail sshd a été activée dans l’exemple ci-dessus.

Utilisation de fail2ban

Sauvegarde iptables

Sur Ubuntu, les tables IP iptables ne sont pas stockées dans un fichier, mais dans le noyau. Fail2ban va gérer iptables, aussi une sauvegarde, au cas où, devrait être réalisée avant de démarrer fail2ban :

(fail2ban) root@vpsfrsqlpac2$ iptables-save > /etc/iptables_rules.txt

Si des comportements inattendus sont observés après l’activation de Fail2ban, la restauration d’un état stable sera plus facile :

(fail2ban) root@vpsfrsqlpac2$ iptables-restore < /etc/iptables_rules.txt

Gestion de fail2ban

Tout est prêt, pour démarrer fail2ban :

(fail2ban) root@vpsfrsqlpac2$ fail2ban-server -xf start
          2020-02-07 10:08:11,078 fail2ban.server         [26518]: INFO    --------------------------------------------------
2020-02-07 10:08:11,078 fail2ban.server         [26518]: INFO    Starting Fail2ban v0.11.1
2020-02-07 10:08:11,079 fail2ban.server         [26518]: INFO    Daemon started
2020-02-07 10:08:11,080 fail2ban.observer       [26518]: INFO    Observer start...
2020-02-07 10:08:11,137 fail2ban.database       [26518]: INFO    Connected to fail2ban persistent database '/var/lib/fail2ban/fail2ban.sqlite3'
2020-02-07 10:08:11,140 fail2ban.database       [26518]: WARNING New database created. Version '4'

Pour arrêter, redémarrer le serveur ou pour recharger la configuration globale ou d’une jail uniquement :

(fail2ban) root@vpsfrsqlpac2$ fail2ban-client stop | restart | reload [jail]

fail2ban-client et les jails

Utiliser également fail2ban-client pour gérer les jails : statuts, annulation d’un bannissement d’IP(s), start, stop…

Des IPs bannies apparaissent dans la jail sshd très rapidement après son activation :

(fail2ban) root@vpsfrsqlpac2$ fail2ban-client status
Status
|- Number of jail:      2
`- Jail list:   recidive, sshd
(fail2ban) root@vpsfrsqlpac2$ fail2ban-client status sshd
Status for the jail: sshd
|- Filter
|  |- Currently failed: 3
|  |- Total failed:     2022
|  `- File list:        /var/log/auth.log
`- Actions
   |- Currently banned: 4
   |- Total banned:     466
   `- Banned IP list:   209.141.57.211 218.92.0.145 …

Les paramètres de configuration des jails peuvent être listés et modifiés dynamiquement :

(fail2ban) root@vpsfrsqlpac2$ fail2ban-client get sshd bantime
          600
(fail2ban) root@vpsfrsqlpac2$ fail2ban-client set sshd bantime 60m
3600
fail2ban-client set met à jour les valeurs uniquement pour un serveur Fail2Ban en cours d’exécution, ne pas oublier de mettre à jour le fichier de configuration en conséquence si elles doivent être persistentes.

Première commande à connaître avec Fail2ban (quand vous vous auto-bannissez 🙂) : comment supprimer le bannissement d’une IP ?

(fail2ban) root@vpsfrsqlpac2$ fail2ban-client set sshd unbanip 90.62.100.101

De très nombreuses options existent : pour l’aide, lancer fail2ban-client --help.

fail2ban et IP Tables

Comment Fail2ban travaille avec iptables ?

Fail2ban créé pour chaque jail une chaîne utilisateur avec le nom f2b-[jail], f2b-sshd dans l’exemple ci-dessus, puis une règle est ajoutée dans cette chaîne pour le port 22 / INPUT :

(fail2ban) root@vpsfrsqlpac2$ iptables -S
-N f2b-sshd
-A INPUT -p tcp -m multiport --dports 22 -j f2b-sshd

Les IPs bannies sont ajoutées dans la chaîne :

(fail2ban) root@vpsfrsqlpac2$ iptables -L f2b-sshd
Chain f2b-sshd (1 references)
target     prot opt source               destination
REJECT     all  --  222.186.180.8        anywhere             reject-with icmp-port-unreachable
REJECT     all  --  222.186.173.142      anywhere             reject-with icmp-port-unreachable
REJECT     all  --  222.186.30.187       anywhere             reject-with icmp-port-unreachable
REJECT     all  --  222.186.173.215      anywhere             reject-with icmp-port-unreachable
REJECT     all  --  222.186.175.140      anywhere             reject-with icmp-port-unreachable

La chaîne est retirée lors de l’arrêt du serveur Fail2ban ou de l’arrêt de la jail (fail2ban-client stop sshd). Comme attendu, la chaîne n’est pas supprimée quand un serveur Fail2ban est tué violemment (kill -9).

Aucune perte lors d’un redémarrage d’un serveur Fail2ban, les chaînes iptables sont reconstruites à partir des informations stockées dans la base de données sqlite3.

(fail2ban) root@vpsfrsqlpac2$ sqlite3
sqlite> .open /var/lib/fail2ban/fail2ban.sqlite3
sqlite> .tables
bans        bips        fail2banDb  jails       logs
sqlite> select ip, jail, datetime(timeofban,'unixepochs'), bantime from bips;
222.186.173.215|sshd|2020-02-07 16:17:03|3600
222.186.30.187|sshd|2020-02-07 16:19:25|3600
222.186.173.142|sshd|2020-02-07 16:24:28|3600
…

Service fail2ban

fail2ban est installé dans un environnement virtuel Python mais cela n’empêche pas la définition d’un service pour le redémarrage automatique.

Un fichier .fail2ban est créé pour sourcer et activer l’environnement virtuel Python.

/opt/monitoring/fail2ban/.fail2ban
export F2BDIR=/opt/monitoring/fail2ban
source /opt/python/.python-3.8
source $F2BDIR/bin/activate

Un fichier modèle pour le service est disponible dans le répertoire à partir duquel l’installation a été réalisée (build/fail2ban.service).

Le service est personnalisé pour y intégrer l’appel du fichier .fail2ban :

/lib/systemd/system/fail2ban.service
[Unit]
Description=Fail2Ban Service
Documentation=man:fail2ban(1)
After=network.target iptables.service firewalld.service ip6tables.service ipset.service nftables.service
PartOf=iptables.service firewalld.service ip6tables.service ipset.service nftables.service

[Service]
Type=simple
ExecStartPre=/bin/mkdir -p /run/fail2ban
ExecStart=/bin/bash -c "source /opt/monitoring/fail2ban/.fail2ban; /opt/monitoring/fail2ban/bin/fail2ban-server -xf start"
ExecStop=/bin/bash -c "source /opt/monitoring/fail2ban/.fail2ban; /opt/monitoring/fail2ban/bin/fail2ban-client stop"
ExecReload=/bin/bash -c "source /opt/monitoring/fail2ban/.fail2ban; /opt/monitoring/fail2ban/bin/fail2ban-client reload"
PIDFile=/run/fail2ban/fail2ban.pid
Restart=on-failure
RestartPreventExitStatus=0 255

[Install]
WantedBy=multi-user.target

Le service est ensuite activé :

root@vpsfrsqlpac2$ cd /etc/systemd/system
root@vpsfrsqlpac2$ ln -fs /lib/systemd/system/fail2ban.service fail2ban.service
root@vpsfrsqlpac2$ systemctl enable fail2ban
Created symlink /etc/systemd/system/multi-user.target.wants/fail2ban.service → /lib/systemd/system/fail2ban.service.
root@vpsfrsqlpac2$ systemctl start fail2ban
root@vpsfrsqlpac2$ systemctl status fail2ban
● fail2ban.service - Fail2Ban Service
   Loaded: loaded (/lib/systemd/system/fail2ban.service; enabled; vendor preset: enabled)
   Active: active (running) since Fri 2020-02-09 17:23:29 CET; 23h ago
     Docs: man:fail2ban(1)
  Process: 4240 ExecStop=/bin/bash -c source /opt/monitoring/fail2ban/.fail2ban; /opt/monitoring/fail2ban/bin/fail2ban-clien
  Process: 4250 ExecStartPre=/bin/mkdir -p /run/fail2ban (code=exited, status=0/SUCCESS)
 Main PID: 4259 (bash)
    Tasks: 8 (limit: 4587)
   CGroup: /system.slice/fail2ban.service
           ├─4259 /bin/bash -c source /opt/monitoring/fail2ban/.fail2ban; /opt/monitoring/fail2ban/bin/fail2ban-server -xf s
           └─4264 /opt/monitoring/fail2ban/bin/python3 /opt/monitoring/fail2ban/bin/fail2ban-server -xf start

Jail pour les récidivistes

Une jail très intéressante à activer : la jail recidive

/etc/fail2ban/jail.local
[recidive]
enabled = true
logpath  = /var/log/fail2ban.log
banaction = %(banaction_allports)s
bantime  = 1w
findtime = 1d

Lors de l’activation de cette jail, Fail2ban scanne son propre fichier de log et y recherche les IPs récurrentes qui ont été bannies par les règles définies dans les autres jails.

Les IPs sont alors bannies pour une période plus longue (ici 1 semaine) et ceci pour TOUS les ports d’entrée. C’est pourquoi le paramètre de rétention dbpurgeage a été fixé à 15 jours, sinon la jail recidive ne fonctionnerait pas très bien.

root@vpsfrsqlpac2$ iptables -S
…
-N f2b-recidive
-A INPUT -p tcp -j f2b-recidive
…
-A f2b-recidive -s 222.186.180.17/32 -j REJECT --reject-with icmp-port-unreachable
-A f2b-recidive -s 222.186.175.167/32 -j REJECT --reject-with icmp-port-unreachable
…

Conclusion

D’autres jails semblent très intéressantes, notamment les jails pour les tentatives de floods et injections à travers Apache / PHP, mais cela doît être étudié plus en profondeur.

Les premières jails ouvertes (sshd, recidive) sont largement suffisantes pour le moment pour protéger au mieux le port sshd d’un serveur Linux Ubuntu localisé dans le réseau internet.

À propos des performances : fail2ban configuré avec les jails sshd et recidive a besoin d’environ 450 - 650 Mb. Son utilisation CPU est très faible (moins de 0,2% en moyenne sur une machine 1 core 2 GHz). Le nombre de règles créé dans iptables ne devrait pas dégrader les performances.