En raison de la complexité à manipuler les règles iptables dans un script shell, on a décidé de doter bulbe (et ensuite les nœuds Tor) d'un pare-feu à l'interface un peu moins rustique, les contraintes principales étant:

  • le support pour IPv6
  • la possibilité de modifier le comportement du pare-feu à l'aide d'une commande et/ou de fichiers de configuration simples.

Parmi les candidats, c'est ferm qui a été retenu et installé.

Fichiers

/etc/default/ferm
Ce fichier est sourcé par le script de démarrage. Ses valeurs par défaut conviennent dans la plupart des cas.
/etc/ferm/ferm.conf
Il s'agit du fichier de configuration principal, celui qu'il faut modifier pour obtenir un réglage sur mesure. D'autres fichiers peuvent être créés dans ce répertoire et appelés à partir du fichier principal à l'aide de la directive @include 'fichier.ferm'; par exemple.
/etc/ferm/*.ferm
Fichiers de configuration pouvant être appelés depuis ferm.conf au moyen de la fonction interne @include fichier.ferm. Ces fichiers peuvent par exemple contenir des fonctions un peu longues qu'on ne veut pas mettre dans le fichier principal pour des raisons de lisibilité, ou des définitions de variables dont la valeur est spécifique à l'hôte, etc.
/usr/share/doc/ferm/examples/*.ferm
Exemples de fichiers de configuration complets et pouvant être utilisés tels quels, en fonction de l'utilisation de la machine (workstation, fileserver, dmz_router, etc). Un exemple intéressant est antiddos.ferm.

Configuration

Le fichier /etc/ferm/ferm.conf est en fait un script. ferm n'est ni plus ni moins qu'un langage interprété par un programme lui-même écrit en Perl, et adapté à sa tâche: la simplification de l'écriture de règles complexes pour netfilter.

Politique

bulbe.nos-oignons.net

Trafic entrant (chaîne INPUT)
  1. Configuration en liste blanche: tout ce qui n'est pas explicitement autorisé est interdit et mis silencieusement à la poubelle (DROP).
    Le trafic autorisé est celui qui permet d'accéder aux services de bulbe et le traffic ICMP (incluant ping).
  2. Les services exposés le sont au travers d'un filtre limitant le nombre de connexions par seconde et par client.
Trafic sortant (chaîne OUTPUT)
Actuellement tout est autorisé en sortie, à l'exception des paquets invalides (le plus souvent en raison d'un débordement de la mémoire, ou d'erreurs ICMP).
Trafic redirigé (chaîne FORWARD)
Aucun trafic ne peut être redirigé.

Nœuds Tor

Mode stateless
Afin de ne pas surcharger le noyau, on ne tient pas compte du critère temporel dans le filtrage des paquets.
Trafic entrant (chaîne INPUT)
  1. Configuration en liste noire: tout ce qui n'est pas explicitement interdit est autorisé.
  2. Sur le port SSH, un ensemble restreint d'adresses IP est autorisé.
Trafic sortant (chaîne OUTPUT)
Tout est autorisé.
Trafic redirigé (chaîne FORWARD)
Aucun trafic ne peut être redirigé.

Moyens

Ferm(8) est scriptable. Il offre la possibilité d'utiliser et de combiner entre eux:

  • des blocs et sous-blocs imbriqués ({... {...} })
  • des ensembles associatifs, ou arrays (domain (ip ip6) protocol (tcp udp)...)
  • des structures de contrôle conditionnelles (@if... @else...)
  • des variables prédéfinies ($DOMAIN, $CHAIN)
  • des variables de l'user (@def $FOOBAR = ...;)
  • des fonctions internes (@resolve(adresse.com), @basename(path))
  • des fonctions de l'user (@def &FOOBAR(a, b) = {...})
  • des substitutions de commandes du shell

De ce point de vue, il est facile à prendre en main, et permet d'écrire à peu de frais des règles faciles à maintenir. Par exemple, domain (ip ip6) {...} permet d'écrire un ensemble de règles pour IPv4 et IPv6, et une modification de ces règles sera prise en compte par les deux protocoles, ce qui permet de rester toujours homogène.

Configuration avancée

Déni de service

Sur le trafic entrant, plusieurs règles dites anti déni de service sont appliquées sur les ports ssh (22), http (80) et https (443), domain (53), smtp (25).

Elles font appel au module recent dont le but est de garder en mémoire des informations sur les paquets précédents (nombre, provenance, timestamp) de manière à pouvoir appliquer une politique particulière aux nouveaux paquets en fonction des informations recueillies sur leurs prédécesseurs.

Concrètement, sur bulbe, cela donne:

Si plus de N paquets en provenance d'une même IP ont été traités en t secondes, alors on refuse les suivants (en provenance de cette même IP) pendant un temps T. Si un paquet se présente durant cette période de bannissement, non seulement il est jeté à la poubelle, mais la période de bannissement T est réinitialisée. Ainsi les paquets en provenance de l'IP bannie ne peuvent être acceptés qu'après un temps de silence complet au moins égal à T, ce qui rend très difficile pour un attaquant de savoir quand il peut revenir à la charge.

Pour pouvoir utiliser ce type de règles avec des valeurs de N assez élevées, appliquées à suffisamment d'IP différentes pour oser parler de déni de service distribué, il a aussi été nécessaire de modifier les paramètres ip_list_tot et ip_pkt_list_tot du module recent (dont les valeurs par défaut sont respectivement 100 et 20), en créant un fichier /etc/modprobe.d/xt_recent.conf avec le contenu suivant:

options xt_recent ip_list_tot=8096 ip_pkt_list_tot=128

Intégration de fail2ban

Sur bulbe, fail2ban est intégré à ferm à l'aide de deux hooks dans /etc/ferm/ferm.conf :

@hook post "if fail2ban-client ping >/dev/null; then fail2ban-client reload; fi";
@hook flush "if fail2ban-client ping >/dev/null; then fail2ban-client reload; fi";

Cela permet de recharger automatiquement les règles produites par fail2ban quand on redémarre ou qu'on arrête ferm.

Les commandes qui vont bien

sudo ferm --interactive /home/libellule/test.ferm

Permet d'appliquer les règles de /home/libellule/test.ferm et ensuite seulement, de demander confirmation. Si cette confirmation n'est pas obtenue dans les trente secondes, ferm revient aux règles précédentes. C'est pratique quand les règles qu'on veut tester risquent de nous laisser enfermés dehors.

sudo ferm --noexec --lines --slow /home/libellule/test.ferm

Permet de vérifier la validité (syntaxique) du script donné en argument et d'afficher l'ensemble des commandes iptables et ip6tables correspondantes.

sudo service ferm restart

Permet d'actualiser les règles après une modification d'un fichier de configuration.

Procédures