IPtables est le nom communément utilisé pour désigner les outils en espace utilisateur et le framework en espace noyau Netfilter permettant de filtrer l'activité réseau sous GNU/Linux. Construit de façon modulaire, cet outil permet de réaliser facilement des pare-feux puissants et intelligents.
Le principe directeur d'IPtables est de faire cheminer chaque paquet par une "table" composée de "chaînes" filtrantes. Par défaut, les paquets passent dans la table "filter", qui contient trois chaînes prédéfinies: les paquets entrants empruntent la chaîne INPUT, les sortants OUTPUT, et les paquets devant être routés passent par la chaîne FORWARD. Chacune de ces chaînes a une politique par défaut, présidant à la destinée du paquet qui la parcourt.
Il est possible de créer de nouvelles chaînes à volonté, mais celles-ci ne pourront pas avoir de politique par défaut; si un paquet les traverse entièrement, il sera renvoyé à la chaîne qu'il avait précédemment parcouru. Chaque chaîne est une succession de rêgles, auxquelles il est possible d'ajouter une "cible" indiquant une action à effectuer sur le paquet. Tant qu'un paquet ne correspond pas à une règle, il continue sa progression le long de la chaîne; dans le cas contraire, le paquet sera traité en fonction de la "cible" définie.
La grande force d'IPtables réside dans sa modularité; ainsi, en chargeant d'autres modules, d'autres tables peuvent être crées, comme la table "nat" gérant la traduction d'adresse, ou "mangle" pour la modification des paquets. Il est également possible de traquer l'état des différentes connections (statefullness), permettant de filter les paquets suivant leur contexte.
Avant de commencer l'écriture d'un pare-feu, il est judicieux de sécuriser au maximum la couche réseau du système d'exploitation. Le noyau Linux est hautement configurable, et plusieurs options accessibles via le système de fichier /proc permettent une sécurité accrue:
La procédure d'initialisation de notre firewall commencera donc par les lignes suivantes:
La stratégie de base d'un firewall sûr est de commencer par bloquer tout le trafic, puis d'autoriser au cas pas cas les usages légitimes du réseau. Cette approche repose sur le principe qu'il vaut mieux, en cas d'erreur, bloquer du trafic légitimes, plutôt que de laisser passer une attaque.
Ceci est accompli très facilement en initialisant nos chaînes iptables avec la politique DROP. Ainsi, en l'absence de rêgles plus complexe, le comportement par défaut sera de bloquer silencieusement tous les paquets.
À partir de cette base saine, nous pouvons dès lors commencer à autoriser le trafic légitime, comme l'interface loopback (locale), le réseau local, et les connections que nous initions nous même vers l'extérieur.
Dans la suite de cet exemple, la variable $LAN correspond à l'interface réseau reliée au réseau local, et $LAN_ADDR à l'adresse IP de cette interface. $LAN_NET et $LAN_BCAST correspondent à l'adresse et au masque de sous réseau utilisés par le réseau local. De la même façon, $INET correspond à l'interface réseau reliée à internet et $INET_ADDR à son adresse IP.
Avec ces quelques règles, nous disposons d'une protection basique mais efficace; nous pouvons accéder à Internet, sans toutefois nous exposer. Notre machine rejette tout paquet non sollicité et est donc invisible aux yeux d'un attaquant.
Enregistrer les paquets rejetés peut être intéressant pour plusieurs raisons; en cas d'attaque, cela permet de collecter des informations sur la nature de l'attaque et éventuellement l'attaquant. Au quotidien, cela permet de pouvoir au besoin détecter un usage légitime bloqué afin de l'autoriser.
Pour cela, nous allons ajouter en destination finale de toutes les chaînes une cible LOG, afin d'utiliser les journaux systèmes.
$LOG_LEVEL représente le niveau de sévérité de l'enregistrement dans le journal du système. Le niveau "info" est recommandé, le bloquage d'un paquet étant un évènement courant.
Le préfixe $PRE permettra de retrouver facilement les enregistrements spécifiques aux paquets bloqués dans le journaux grâces à des outils comme grep ou sed.
Si notre machine doit servir de bouclier à un réseau local, il faut pouvoir également partager la connection aux machines protégées. IPtables permet de partager une connection très simplement, en utilisant le module "nat".
Grâce à cette fonction, les machines que nous protégeons peuvent accéder à internet, mais ne peuvent pas héberger de services. En effet, nous filtrons toutes les connections extérieures.
IPtables permet cependant de rediriger facilement le traffic arrivant sur un des ports réseau de notre machine vers la machine appropriée de notre réseau local, en utilisant la chaîne PREROUTING.
L'exemple que nous avons développé dans la première partie est simple mais limité - suffisant pour un ordinateur personnel ou une station de travail, mais complètement inadapté à un serveur. En effet, il n'est pas possible d'accéder à un quelconque service réseau hébergé sur la machine protégée. Nous allons donc voir comment accepter du trafic sur un ou plusieurs ports réseau.
Nous allons pour cela utiliser la fonctionnalité stateful d'IPtables, (module "state") afin de n'accepter que les connections légitimes:
Grâce à cette fonction, il est désormais trivial d'ouvrir un port: il suffit d'utiliser la ligne port_allow tcp 80 pour autoriser l'accès à un serveur HTTP, ou bien port_allow tcp 22 pour SSH.
Cependant, il est parfois utile de prévoir des protections supplémentaires. Par exemple, beaucoup d'administrateurs parcourant leur journaux systèmes y trouveront des traces d'attaque de type "force brute" contre leur serveur SSH. Comment y remédier? Certains optent pour une authentification uniquement cryptographique, d'autres déménagent leur serveur SSH sur un port moins fréquenté. En utilisant IPtables, il est possible de remédier à ces attaques sans devoir changer la configuration du serveur SSH.
Comment endiguer une attaque de type "force brute" ? Dans ce genre d'agression, l'attaquant se connecte au serveur un grand nombre de fois, en essayant chaque fois des codes d'accès différents. Il n'est pas possible de différencier cette attaque d'une connection légitime par le contenu du trafic, mais en revanche la fréquence des connections est particulièrement révélatrice. En limitant la fréquence des connections sur un port réseau, on peut ainsi rendre une attaque "force brute" complètement inneficace. Nous utiliserons pour cela le module "recent".
En remplaçant port_allow tcp 22 par protect_bruteforce 22, notre serveur SSH sera à l'abri de ces attaques.
Le module "recent" permet de créer d'autres dispositifs intéressants. Voyons par exemple comment dissimuler un service, en n'ouvrant son port que si la bonne combinaison de ports réseau est contactée.
Pour ce faire, nous allons créer de nouvelles chaînes; chaque fois que l'un des ports de déblocage sera contacté, nous avancerons d'une chaîne vers l'ouverture du port dissimulé:
Nous pouvons maintenant astucieusement dissimuler notre serveur SSH derrière la combinaison de 4 ports 665, 201314, 66 et 520 de la façon suivante: protect_portknocking 22 10 665 201314 66 520
J'espère que cet article vous aura permis de découvrir des aspects d'IPtables avec lesquels vous n'étiez pas familiers, et partant, vous aura permis d'en finir avec les comportements à risque. Sortons couverts.