Utiliser fail2ban pour autoriser des connexions temporaires

fail2ban ouvrir
Crédits photo

J’avais pour objectif d’avoir sur un serveur debian une application sur un port particulier mais disponible uniquement sur demande. C’est à dire que ce port soit fermé tout le temps à tout le monde, et que l’on puisse l’ouvrir occasionnellement (et à distance) pour une IP précise et de façon temporaire.
Il existe la méthode du Port Knocking pour cela avec différentes implémentations, mais son utilisation au quotidien n’était pas assez pratique pour mon usage.
Puis en regardant d’un peu plus près le fonctionnement de fail2ban, je me suis aperçu que l’on pouvait l’utiliser de manière inverse à son objectif premier: en plus ou au lieu de protéger mon serveur contre les attaques brute-force en interdisant automatiquement les attaquants de façon temporaire, il est possible de l’utiliser pour ouvrir un port précis à une adresse IP précise de façon temporaire et automatique. Bref: un «Sésame, ouvre-toi».

Pour mettre en place cette technique, il faut se dire qu’il y a en fait 3 étapes ou procédés à mettre en place: la configuration de fail2ban pour permettre ce comportement d’ouverture, la configuration d’iptables et la génération d’un fichier log où fail2ban ira chercher les adresses IP à autoriser.
Grosso-modo: le port est fermé dans iptables, fail2ban vérifie un fichier log où il trouve des adresses IP et leur ouvre le port en question pour une durée déterminée.

Pour expliquer le procédé, je vais prendre pour exemple l’ouverture du port 51160 (un port discret que l’on n’irait pas spécialement chercher à embêter) sur une machine dont le hostname est mamachine.dyndns.org (Dyndns est particulièrement pratique lorsque la machine a une IP dynamique).

1. La configuration de fail2ban:

J’ai édité le fichier /etc/fail2ban/jail.conf afin d’y ajouter une nouvelle section nommé galipe:


[galipe]
enabled = true
port	= 51160
banaction = galipe-allow
filter	= galiped
logpath  = /var/log/logip.txt
bantime  = 10800
maxretry = 1

enabled: active la « prison » pour ce port, port: le port sur lequel agira fail2ban, banaction: l’action spécifique à effectuer (listée dans le dossier /etc/fail2ban/action.d/), filter: le filtre permettant de déclencher l’action (listé dans /etc/fail2ban/filter.d/), logpath: le fichier log que fail2ban va analyser pour effectuer son action, bantime: le durée à laisser le port ouvert en secondes (ici 10800s = 3h), maxretry: au bout de combien d’occurrence dans le fichier log fail2ban va se déclenche l’action (ici pour autoriser: 1 seule occurrence nous suffit).

J’ai ensuite créé une action, en faisant une copie du fichier /etc/fail2ban/action.d/iptables.conf (pour ne pas avoir à tout retaper) et que j’ai renommé en /etc/fail2ban/action.d/galipe-allow.conf (« galipe-allow » étant le nom qui correspond à banaction dans le fichier /etc/fail2ban/jail.conf).
Dans ce fichier j’y modifie la ligne:

actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP

en:

actionban = iptables -I fail2ban-<name> 1 -s <ip> -j ACCEPT

et la ligne:

actionunban = iptables -D fail2ban-<name> -s <ip> -j DROP

en:

actionunban = iptables -D fail2ban-<name> -s <ip> -j ACCEPT

Au lieu de fermer le port, fail2ban va maintenant l’ouvrir.
Et j’y ai changé le nom de la chaîne iptables et le port en:

# Defaut name of the chain
#
name = galipe
# Option: port
# Notes.: specifies port to monitor
# Values: [ NUM | STRING ] Default:
#
port = 51160

Puis ensuite, j’ai créé un filtre en faisant une copie du fichier /etc/fail2ban/filter.d/sshd.conf que j’ai renommé en /etc/fail2ban/filter.d/galiped.conf («galiped» étant le nom qui correspond à filter dans le fichier /etc/fail2ban/jail.conf).
Là j’y change la ligne:

_daemon = sshd

en:

_daemon = galiped

puis pour la partie failregex, j’y mets la chaine d’expression régulière correspondant à mon futur fichier log à analyser.
Sachant que je vais construire mon fichier log simplement avec des lignes du style « Mois Jour Heure:Minute:Seconde autorisation Adresse IP ».
Exemple: Sep 14 18:51:36 autorisation 84.40.23.120

Ainsi pour ma valeur failregex, je n’ai qu’à utiliser cette ligne:

^%(__prefix_line)sautorisation <HOST>\s*$

2. La configuration d’iptables:

Sachant que fail2ban rajoute automatiquement ses règles iptables en démarrant et qu’il les modifie à la volée pour y rajouter les IP à bannir temporairement, les ports en question sont donc ouverts par défaut dans le firewall.
Or nous allons procéder de la façon inverse: fail2ban va ouvrir le port pour certaines IP, il faut donc que ce port soit fermé par défaut pour les IP ne faisant pas partie de la règle de fail2ban ou si ce dernier n’est pas lancé.
Pour cela on va rajouter une règle iptables à positionner en premier dans la table des règles fermant ce port à tout le monde. La règle de fail2ban venant se mettre devant dans l’ordre d’application des règles, donc si une IP qui tente de venir et qu’elle n’est pas dans la règle de fail2ban, elle passe au DROP: « Pan! Un drop dans la tête, le port est fermé ».

Si vous faites ça en ligne de commande:

iptables -I INPUT 1 p tcp --dport 51160 -j DROP

Personnellement, je préfère encore pour l’instant utiliser Webmin pour configurer iptables, c’est plus visuel et plus clair pour moi.

Appliquer la configuration de Iptables après avoir rajouté votre règle avec la commande iptables-save (ou sinon via webmin avec le bouton « Apply Configuration »).

Redémarrer fail2ban: /etc/init.d/fail2ban restart
Car les règles de fail2ban qui sont juste en mémoire volatile dégagent lors du rechargement d’iptables.

3. La génération du fichier log à analyser:

C’est là où la méthode peut varier et devient beaucoup plus personnelle, l’objectif est de ne pas aller sur la machine cible directement pour lui faire ouvrir le port mais d’utiliser une machine tierce*.
J’avais pensé à plusieurs méthodes comme utiliser un bot jabber (mais je n’ai pas encore trouvé mon bonheur dans ce domaine), un bot irc, récupérer un flux xml (par exemple issu de twitter) mais ce n’est pas immédiat (il faut faire tourner un cronjob régulier pour rapatrier le flux xml) et il faut croire en l’uptime absolu du service (ce qui est le point noir de twitter). Bref plein de méthodes sont possibles et imaginables.

* car mon objectif est de ne pas avoir de ports ouverts au public, la machine étant à la maison derrière un routeur, elle doit rester muéte comme s’il n’y avait rien d’ouvert sur ma connexion ADSL. Mais pour ceux qui ont un accès ssh ou un serveur web accessible à tous sur cette machine, il est possible de faire cela plus simplement.

Je me suis concocté une solution personnelle peut-être un peu tordue, mais plus en rapport avec ce que je sais faire: le PHP.
Comme je dispose d’un hébergement web chez 1and1, j’y ai placé une page web anodine en php qui demande un mot de passe.
De l’autre côté sur ma machine (mamachine.dyndns.org), j’ai ouvert un autre port sur mon routeur qui pointe sur un virtualhost d’Apache. Ce port de ma machine n’est ouvert (en dur dans iptables) que pour le serveur web de 1and1 et pour personne d’autre. Sur cet hôte virtuel d’apache, une autre page en php qui se charge de créer le log.

Lorsque je me rend sur la page web chez 1and1 et que j’entre le mot de passe correct, elle appelle la page située chez moi sur mamachine.dyndns.org via la fonction Simplexml (simplexml disponible pour php à partir de la version 5), lui indiquant mon IP. La page php sur mamachine.dyndns.org traite (via un regex pour s’assurer que c’est bien une adresse IP) et insère l’IP dans le fichier /var/log/logip.txt sous la forme suivante
Sep 14 18:51:36 autorisation 84.40.23.120
et retourne en xml le signal que c’est bon. Et la page de chez 1and1 me dit que c’est ouvert.

Voici les sources de mes fichiers php: fail2ban_allow.zip
– knock.php à placer sur le serveur web qui est accessible publiquement
– logme.php à placer sur votre machine locale sur le virtualhost
à noter l’utilisation de @ devant les fonctions simplexml_load_file, fsockopen, fclose pour ne pas révéler dans un éventuel message d’erreur l’adresse de la machine distante qui ouvrira ses portes si la connexion échoue.
Il est possible et même conseillé d’utiliser la méthode du fichier .htaccess pour protéger votre page web par mot de passe, plutôt qu’un simple mot de passe dans le fichier php, mais là j’ai fait comme celà juste pour tester la faisabilité du concept.

Notes:
– Pour vérifier les règles d’iptables courantes (même celles créées à la volée par fail2ban): iptables -L

Je suis ouvert à toute proposition d’amélioration de cette technique 😉

3 thoughts on “Utiliser fail2ban pour autoriser des connexions temporaires”

  1. Pingback: Seagate Dockstar en déstockage, un Plug Computer très abordable. « Galipe
  2. Trackback: Seagate Dockstar en déstockage, un Plug Computer très abordable. « Galipe
  3. Jerome says:

    Bonjour,

    Merci pour cet article trés intéressant qui montre comment « détourner » l’usage initial de fail2ban pour en faire un système d’authentification basique.

    Seul point noir si vous être derrière un proxy (d’entreprise) vous autorisez alors temporairement l’accès à l’ensemble des personnes utilisant ce proxy… le risque me parait acceptable suivant l’application… mais il en faut en être conscient 🙂

    Jérôme.

  4. tof says:

    Le système d’authentification je l’ai poussé un peu plus loin en me concoctant une application Facebook et à l’aide d’un robot XMPP avec gestion par token. Ça me permet de ne laisser aucun port ouvert, même pas pour le serveur web.

    Mais c’est vrai que la limite et le risque est bel et bien si la personne qui s’authentifie passe via un proxy, voire un réseau d’entreprise ou encore un hotspot wifi, ça ouvre le port pour tous les gens derrière ce proxy, dans le réseau d’entreprise ou ceux connectés au même hotspot.
    Après il faut voir vers quels types de services mène l’ouverture du port, si c’est du ssh, openvpn, etc… pas de problème. Si c’est des données sensibles sans sécurité aucune alors là oui ça peut être problématique.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *