--- categories: web security title: Howto Fail2Ban ... * Documentation : * Rôle Ansible : # Howto Fail2Ban [Fail2Ban](https://www.fail2ban.org) est un service (en Python), qui scanne les logs applicatifs à la recherche d'un pattern particulier (généralement des échecs de connexions) et bannit les adresses IP responsables via le firewall. La version présente dans Debian jessie est la v0.8.13. ## Installation ~~~ # apt install fail2ban # fail2ban-server -V Fail2Ban v0.8.13 Copyright (c) 2004-2008 Cyril Jaquier, 2008- Fail2Ban Contributors Copyright of modifications held by their respective authors. Licensed under the GNU General Public License v2 (GPL). Written by Cyril Jaquier . Many contributions by Yaroslav O. Halchenko . ~~~ ## Configuration Fichiers de configuration : ~~~ /etc/fail2ban ├── fail2ban.conf ├── jail.conf ├── jail.local ├── action.d │ ├── apf.conf │ ├── badips.conf │ ├── blocklist_de.conf │ […] ├── fail2ban.d ├── filter.d │ ├── 3proxy.conf │ ├── apache-auth.conf │ ├── apache-badbots.conf │ […] └── jail.d ~~~ Fail2ban repose sur des règles de filtrage définies dans `/etc/fail2ban/filter.d/` et des actions dans `/etc/fail2ban/action.d/`. On définit ensuite des **jails**, combinaisons d'une règle de filtrage et d'une ou plusieurs actions. Voici la jail nommée _ssh_ (activée par défaut à l'installation) : ~~~{.ini} [ssh] enabled = true port = ssh filter = sshd logpath = /var/log/auth.log maxretry = 6 ~~~ Cette jail va surveiller le fichier `/var/log/auth.log` via la règle de filtrage `/etc/fail2ban/filter.d/sshd.conf` : si il détecte la correspondance 6 fois en 10 minutes (temps par défaut), il va utiliser l'action par défaut, à savoir l'action `/etc/fail2ban/action.d/iptables-multiport.conf` qui va ajouter une règle _iptables_ pour bannir le port concerné pendant 10 minutes. Les paramètres par défaut sont : ~~~{.ini} maxretry = 3 findtime = 600 banaction = iptables-multiport bantime = 600 ignoreip = 127.0.0.1/8 ~~~ Les paramètres par défaut et un certain nombre de jails (non activées à par _ssh_) sont définies dans `/etc/fail2ban/jail.conf`, si l'on veut ajouter ou modifier des paramètres ou des jails, il faut utiliser le fichier `/etc/fail2ban/jail.local`. Pour dumper la configuration courante : ~~~ # fail2ban-client -d ['set', 'loglevel', 3] ['set', 'logtarget', '/var/log/fail2ban.log'] [...] ~~~ ## Administration Fail2Ban est un démon en Python, on peut s'assurer qu'il tourne bien sur un système : ~~~ $ ps auwx | grep fail2ban root 24173 0.00.2 185048 12176 ? Sl 16:16 0:03 /usr/bin/python /usr/bin/fail2ban-server -b -s /var/run/fail2ban/fail2ban.sock -p /var/run/fail2ban/fail2ban.pid ~~~ On peut lister les jails actives : ~~~ # fail2ban-client status Status |- Number of jail: 1 `- Jail list: ssh ~~~ Et l'on doit les retrouver au niveau d'iptables : ~~~ # iptables -L -n ~~~ ### fail2ban-client De nombreuses commandes sont possibles avec `fail2ban-client` pour lister des informations ou modifier des paramètres. Pour lister l'état de la jail _ssh_ : ~~~ # fail2ban-client status ssh Status for the jail: ssh |- filter | |- File list: /var/log/auth.log | |- Currently failed: 0 | `- Total failed: 6 `- action |- Currently banned: 1 | `- IP list: 192.0.2.42 `- Total banned: 1 ~~~ Pour débannir l'adresse IP 192.0.2.42 de la jail _ssh_ : ~~~ # fail2ban-client set ssh unbanip 192.0.2.42 ~~~ Pour lister les informations de la jail _ssh_ : ~~~ # fail2ban-client get ssh maxretry 6 # fail2ban-client get ssh findtime 600 # fail2ban-client get ssh bantime 600 # fail2ban-client get ssh ignoreip These IP addresses/networks are ignored: `- 127.0.0.1/8 # fail2ban-client get ssh addaction iptables-multiport # fail2ban-client get ssh failregex The following regular expression are defined: [...] ~~~ ## Règles de filtrage Un certain nombre de règles de filtrage sont définies dans `/etc/fail2ban/filter.d/` : ~~~ 3proxy.conf apache-noscript.conf couriersmtp.conf exim.conf lighttpd-auth.conf openwebmail.conf proftpd.conf selinux-ssh.conf squid.conf webmin-auth.conf apache-auth.conf apache-overflows.conf cyrus-imap.conf exim-spam.conf mysqld-auth.conf pam-generic.conf pure-ftpd.conf sendmail-auth.conf sshd.conf wuftpd.conf apache-badbots.conf assp.conf dovecot.conf freeswitch.conf nagios.conf perdition.conf qmail.conf sendmail-reject.conf sshd-ddos.conf xinetd-fail.conf apache-common.conf asterisk.conf dropbear.conf groupoffice.conf named-refused.conf php-url-fopen.conf recidive.conf sieve.conf suhosin.conf apache-modsecurity.conf common.conf ejabberd-auth.conf gssftpd.conf nginx-http-auth.conf postfix.conf roundcube-auth.conf sogo-auth.conf uwimap-auth.conf apache-nohome.conf courierlogin.conf exim-common.conf horde.conf nsd.conf postfix-sasl.conf selinux-common.conf solid-pop3d.conf vsftpd.conf ~~~ On peut écrire ses propres règles en s'appuyant sur [les expressions régulières Python](https://docs.python.org/3/library/re.html) pour détecter la variable __. Exemple d'une règle : ~~~ [Definition] failregex = warning: \[\]: authentication failed: ignoreregex = ~~~ On peut tester sa règle avec la commande **fail2ban-regex** en précisant une date : ~~~ $ fail2ban-regex '2017-05-20 10:10:10 foo' 'warning: \[\]: authentication failed:' [...] Failregex: 0 total [...] $ fail2ban-regex '2017-05-20 10:10:10 [192.0.2.42]: authentication failed:' '\[\]: authentication failed:' [...] Failregex: 1 total |- #) [# of hits] regular expression | 1) [1] \[\]: authentication failed: [...] $ fail2ban-regex auth.log /etc/fail2ban/filter.d/evolix-test.conf [...] ~~~ ## Exemples ### Dovecot On ajoute la règle `filter.d/evolix-dovecot.conf` : ~~~ [Definition] failregex = (?: pop3-login|imap-login): .*(?:Authentication failure|Aborted login \(auth failed|Aborted login \(tried to use disabled|Disconnected \(auth failed|Aborted login \(\d+ authentication attempts).*rip=(?P\S*),.* ignoreregex = ~~~ puis on définit une jail ainsi : ~~~{.ini} [dovecot] enabled = true filter = evolix-dovecot port = pop3,pop3s,imap,imaps logpath = /var/log/mail.log ~~~ Filtre dovecot, filter.d/dovecot-pop3imap.conf ## Courier On utilise la règle _courierlogin_ prédéfinie pour la jail : ~~~{.ini} [courierauth] enabled = true port = smtp,ssmtp,imap2,imap3,imaps,pop3,pop3s filter = courierlogin logpath = /var/log/mail.log ~~~ ## Postfix SASL La règle SASL ne convient pas, il faut la modifier via une nouvelle règle `filter.d/evolix-sasl.conf` : ~~~ [Definition] failregex = (?i): warning: [-._\w]+\[\]: SASL (?:LOGIN|PLAIN|(?:CRAM|DIGEST)-MD5) authentication failed: ignoreregex = ~~~ puis l'on définit une jail ainsi : ~~~{.ini} [sasl] enabled = true port = smtp,ssmtp,submissions,imap2,imap3,imaps,pop3,pop3s filter = evolix-sasl logpath = /var/log/mail.log ~~~ ## Apache / Nginx Il est possible d'utiliser `fail2ban` sur des sites web pour ajouter une couche de protection contre les attaques, ou alors pour répondre à une attaque en cours. Les deux attaques les plus courantes sur les services HTTP sont les DDOS et les attaques en "brute force". Pour les attaques de type DDOS, la chose la plus efficace à faire est d'écrire des règles pour le serveur web (Apache, Nginx, etc.) Comme `fail2ban` a besoin d'être configuré au cas par cas, il faut utiliser des configurations différentes pour chaque plateforme web. En général, on tente d'être le plus précis possible en configurant `fail2ban` pour regarder les logs d'authentification. Cela permet d'éviter les faux positifs et d'être efficace en cas d'attaque. On peut protéger Apache des attaques DDOS simples avec les configurations suivantes. Il faut tout d'abord s'assurer qu'Apache enregistre des logs d'accès. On ajoute ensuite un filtre à `fail2ban` dans `/etc/fail2ban/filter.d/ddos-http.conf` : ~~~ failregex = ^ -.*GET ~~~ Finalement, on modifie `/etc/fail2ban/jail.local` pour ajouter une des deux règles suivantes. Avec cette règle, une personne qui accède plus de 300 fois à une page web sur notre serveur web en 5 minutes va être bannie pendant la période de temps par défaut: ~~~{.ini} [ddos-http-apache] enabled = true port = http,https filter = ddos-http logpath = /var/log/apache2/access.log maxretry = 300 findtime = 300 ~~~ ~~~{.ini} [ddos-http-nginx] enabled = true port = http,https filter = ddos-http logpath = /var/log/nginx/access.log maxretry = 300 findtime = 300 ~~~ ### webapps #### Wordpress sans plugin Il existe plusieurs options pour configurer `fail2ban` pour Wordpress. Cela découle du fait qu'il n'y a pas de moyen par défaut de logger les authentifications erronées sous Wordpress, et que la page d'authentification ne renvoie pas d'erreur HTTP particulière non plus. ["Une proposition à cet effet"](https://core.trac.wordpress.org/ticket/25446) a cependant été faite et risque éventuellement d'être implémentée. Cette option est la plus simple des trois car elle ne nécessite pas de modifier l'installation Wordpress. Elle a cependant le désavantage d'être très générale et peu créer des faux positifs. C'est celle qui est actuellement utilisée par Évolix. Il faut tout d'abord s'assurer que notre serveur web enregistre des logs d'accès. On ajoute ensuite un filtre à `fail2ban` dans `/etc/fail2ban/filter.d/apache-wp.conf`: ~~~ [Definition] failregex = -.*"POST.*/wp-login.php HTTP.* 200 -.*"POST.*/xmlrpc.php.* ~~~ Finalement, on modifie `/etc/fail2ban/jail.local` pour ajouter une règle. Avec cette règle, une personne qui accède plus de 5 fois à la page `/wp-login.php` ou à `/xmlrpc.php` dans une période d'une minute va être bannie pendant la période de temps par défaut: ~~~{.ini} [apache-wp] enabled = true port = http,https filter = apache-wp logpath = /var/log/apache2/access.log maxretry = 5 findtime = 60 ~~~ #### Wordpress avec plugin simple Une seconde option est d'utiliser un plugin Wordpress très simple pour envoyer une erreur HTTP 401 en cas d'erreur d'authentification. Cette méthode est plus fine et créé moins de faux positifs, mais nécessite de toucher à l'installation Wordpress. Elle est cependant peu invasive car elle fait installe le plugin dans [wp-content/mu-plugins](https://codex.wordpress.org/Must_Use_Plugins), un dossier spécial pour les plugins de ce genre. Typiquement, les mu-plugins ne sont pas vu à travers l'interface d'administration web. Il faut tout d'abord s'assurer que notre serveur web enregistre des logs d'accès. Par la suite, on installe le plugin dans `wp-content/mu-plugins/401-on-login-fail.php`: ~~~{.php} .*POST.*(wp-login\.php|xmlrpc\.php).* 401 ~~~ Finalement, on modifie `/etc/fail2ban/jail.local` pour ajouter une règle. Avec cette règle, une personne qui accède plus de 5 fois à la page `/wp-login.php` ou à `/xmlrpc.php` dans une période d'une minute va être bannie pendant la période de temps par défaut: ~~~{.ini} [apache-wp] enabled = true port = http,https filter = apache-wp logpath = /var/log/apache2/access.log /home/user/log/access.log maxretry = 5 findtime = 60 ~~~ #### Wordpress avec plugin Fail2Ban La dernière solution utilise le [le plugin Wordpress fail2ban](https://wordpress.org/plugins/wp-fail2ban/) pour enregistrer les authentification dans un fichier de log. `fail2ban` vérifie par la suite ce fichier pour bannir les gens effectuant des attaques. Parce qu'elle nécessite l'installation et la mise à jour régulière d'un plugin, elle ne devrait être utilisée que par les personnes administrant le site web en question. On commence tout d'abord par [installer le plugin](https://wordpress.org/plugins/wp-fail2ban/). Une fois que cela est fait, on ajoute les deux filtres suivants, respectivement dans `etc/fail2ban/filter.d/wordpress-hard` et `etc/fail2ban/filter.d/wordpress-soft`: ~~~{.ini} # Fail2Ban configuration file hard # # Author: Charles Lecklider # [INCLUDES] # Read common prefixes. If any customizations available -- read them from # common.local before = common.conf [Definition] _daemon = (?:wordpress|wp) # Option: failregex # Notes.: regex to match the password failures messages in the logfile. The # host must be matched by a group named "host". The tag "" can # be used for standard IP/hostname matching and is only an alias for # (?:::f{4,6}:)?(?P[\w\-.^_]+) # Values: TEXT # failregex = ^%(__prefix_line)sAuthentication attempt for unknown user .* from ( via XML-RPC)?$ ^%(__prefix_line)sBlocked authentication attempt for .* from ( via XML-RPC)?$ ^%(__prefix_line)sBlocked user enumeration attempt from $ ^%(__prefix_line)sPingback error .* generated from $ # Option: ignoreregex # Notes.: regex to ignore. If this regex matches, the line is ignored. # Values: TEXT # ignoreregex = ~~~ ~~~{.ini} # Fail2Ban configuration file soft # # Author: Charles Lecklider # [INCLUDES] # Read common prefixes. If any customizations available -- read them from # common.local before = common.conf [Definition] _daemon = (?:wordpress|wp) # Option: failregex # Notes.: regex to match the password failures messages in the logfile. The # host must be matched by a group named "host". The tag "" can # be used for standard IP/hostname matching and is only an alias for # (?:::f{4,6}:)?(?P[\w\-.^_]+) # Values: TEXT # failregex = ^%(__prefix_line)sAuthentication failure for .* from $ ^%(__prefix_line)sXML-RPC authentication failure from $ # Option: ignoreregex # Notes.: regex to ignore. If this regex matches, the line is ignored. # Values: TEXT # ignoreregex = ~~~ Finalement, on modifie `/etc/fail2ban/jail.local` pour ajouter les règles suivantes. Avec cette règle, une personne qui tente de se connecter avec un compte inexistant ou qui n'arrive pas à se connecter 5 fois sera bannie pendant la période de temps par défaut: ~~~{.ini} [wordpress-hard] enabled = true filter = wordpress-hard logpath = /var/log/auth.log maxretry = 1 port = http,https [wordpress-soft] enabled = true filter = wordpress-soft logpath = /var/log/auth.log maxretry = 5 port = http,https ~~~ #### ownCloud Pour faire fonctionne ownCloud avec fail2ban, il faut tout d'abord modifier le `config.php` pour enregistrer les informations d'authentification: ~~~ 'loglevel' => '2', 'log_authfailip' => true, 'logfile' => '/var/log/owncloud.log', ~~~ On ajoute ensuite un filtre à `fail2ban` dans `/etc/fail2ban/filter.d/owncloud.conf`: ~~~ [Definition] failregex={"app":"core","message":"Login failed: user '.*' , wrong password, IP:","level":2,"time":".*"} ~~~ Finalement, on modifie `/etc/fail2ban/jail.local` pour ajouter une règle. Avec cette règle, une personne qui n'arrive pas à se connecter 5 fois dans une période de 10 minutes sera bannie pendant la période de temps par défaut: ~~~{.ini} [owncloud] enabled = true filter = owncloud port = http,https logpath = /var/log/owncloud.log maxrety = 5 findtime = 600 ~~~ #### Joomla Il faut tout d'abord s'assurer que notre serveur web enregistre des logs d'accès. On ajoute ensuite un filtre à `fail2ban` dans `/etc/fail2ban/filter.d/apache-joomla.conf`: ~~~{.ini} [Definition] failregex = -.*"POST.*/administrator/index.php.* ~~~ Finalement, on modifie `/etc/fail2ban/jail.local` pour ajouter une règle. Avec cette règle, une personne qui accède plus de 5 fois à la page `/administrator/index.php` dans une période d'une minute va être bannie pendant la période de temps par défaut: ~~~{.ini} [apache-joomla] enabled = true port = http,https filter = apache-joomla logpath = /var/log/apache2/access.log maxretry = 5 findtime = 60 ~~~ #### Prestashop Il faut tout d'abord s'assurer que notre serveur web enregistre des logs d'accès. On ajoute ensuite un filtre à `fail2ban` dans `/etc/fail2ban/filter.d/apache-prestashop.conf`: ~~~{.ini} [Definition] failregex = -.*"POST.*/login.* ~~~ Finalement, on modifie `/etc/fail2ban/jail.local` pour ajouter une règle. Avec cette règle, une personne qui accède plus de 10 fois à la page de connexion pour un compte utilisateur dans une période d'une minute va être bannie pendant la période de temps par défaut: ~~~{.ini} [apache-prestashop] enabled = true port = http,https filter = apache-prestashop logpath = /var/log/apache2/access.log maxretry = 10 findtime = 60 ~~~ ## FAQ ### Fail2Ban supporte l'IPv6 ? Non, Fail2Ban ne supporte pas encore l'IPv6, c'est prévu pour la version 0.10 (encore expérimentale). ### Attaque via un utilisateur Un utilisateur local peut générer des logs vers _syslog_ (par exemple avec la commande **logger**), il faut donc bien avoir en tête que toute jail s'appuyant sur des logs _syslog_ (comme `/var/log/auth.log`) pourra être activée par un utilisateur logué en SSH ou via PHP, CGI, etc.