Filtrage réseau et pare-feu avec Netfilter et iptables

Présentation de ce cours
Rubrique : Administration de GNU/Linux
Niveau : ***
Audience : Administrateur réseau GNU/Linux.
Lectures préalables :
But : Donner les moyens de configurer un pare-feu Linux en filtrant notamment les ports.

Généralités | (Haut de page) |

Depuis la version 2.4, Linux contient un module destiné au filtrage réseau, Netfilter. Il se configure au moyen d'un outil appelé iptables.

Le filtrage réseau consiste en l'examen des paquets réseaux et à prendre des décisions sur le traitement à leur appliquer. C'est ce que fait un pare-feu ou, en anglais, firewall. Avec un système GNU/Linux, pour configurer des règles de pare-feu, il faudra donc simplement utiliser Netfilter à l'aide d'iptables.

Cet article s'interesse uniquement au filtrage réseau IPV4 (qui est probablement celui que vous utilisez si cela ne vous évoque rien). Cela est possible pour d'autres protocoles et les principes exposés ici resteront pour la plupart valides.

Présentation de Netfilter | (Haut de page) |

Netfilter travaille sur des paquets réseaux. Il s'agit de parties des informations transmises. Pour, par exemple, télécharger un fichier, celui-ci est découpé en plusieurs paquets avant de transiter sur le réseau. Chacun de ces paquets comporte en plus des données, des informations ajoutées par les couches réseaux. Ce sont sur ces informations que s'effectueront les tests de filtrage.

La couche réseau Linux présente plusieurs points d'accès (en anglais hook). Netfilter dispose de fonctions de rappel (callback). Celles-ci sont des suites d'instructions qui précisent ce qui doit être fait lorsque survient un événement.

Concrètement, lorsqu'un paquet réseau atteint un de ces points d'accès, il est passé à Netfilter par l'intermédiaire de sa fonction de rappel. Il est alors examiné pour prendre une décision concernant son traitement futur.

Netfilter se comporte comme un automate qui compare le paquet successivement à plusieurs règles. Et selon le résultat du test, le paquet est traité ou transmis au test suivant.

Les tables, chaînes et cibles | (Haut de page) |

Aux points d'accès sont associées des chaînes de traitement. C'est dans celles-ci que sont effectués les tests. Elles sont regroupées par tables selon le type de traitement.

Les tables sont ajoutées par des modules. Il en existe 3 principales pouvant être utilisées. Voici ci-après leurs noms et la tâche à laquelle elles sont destinées.

Tables principales de Netfilter
TableDescription
filterCette table permet de filtrer les paquets. Typiquement ce sera pour les accepter ou non.
natAvec cette table, on peut réaliser des translations d'adresse (ou de ports). Ceci sera notamment utile pour partager une connexion.
mangleElle sert pour modifier les en-têtes des paquets. On la rencontrera parfois pour marquer des paquets afin que d'autres applications puissent les reconnaître.

A l'intérieur d'une table, on peut trouver plusieurs chaînes. Ce sont elles qui contiendront les règles à appliquer aux paquets. Ces règles seront évaluées séquentiellement. On trouve deux types de chaînes.

Tout d'abord celles qui sont associées aux différents points d'entrées existants. Un paquet atteignant un de ces points sera envoyé vers la chaîne associée. Ce sont les fonctions de rappel evoquées précédemment qui réalisent cela. Elles effectuent les uns après les autres les tests que contient la chaîne. Ces chaînes sont en nombre fini et ne sont pertinentes que pour certaines tables. Le tableau suivant les liste, en indiquant quelle table a une chaîne de ce type.

Chaînes de Netfilter
ChaîneTableDescription
PREROUTINGnat, manglePar cette chaîne passeront les paquets entrant dans la machine avant routage.
INPUTfilterCette chaîne traitera les paquets entrants avant qu'ils ne soient passées aux couches supérieures (les applications).
FORWARDfilterCe sont les paquets uniquement transmis par la machine sans que les applications n'en aient connaissance.
OUTPUTfilter, nat, mangleCette chaîne sera appelée pour des paquets envoyés par des programmes présents sur la machine.
POSTROUTINGnatLes paquets prêts à être envoyés (soit transmis, soit générés) seront pris en charge par cette chaîne.

On trouve aussi des chaînes utilisateurs. Elles peuvent librement être créées mais ne seront pas associées à un point d'accès. En fait on ne pourra rentrer dans une de ces chaînes que si une règle d'une des chaînes existantes dirige le paquet vers celle-ci.

Enfin, le dernier élément important est la notion de cible. Il s'agit du traitement que l'on décide d'appliquer au paquet. C'est la cible qui se chargera de faire les opérations nécessaires. En plus de celles prédéfinies, il est possible d'indiquer comme cible une chaîne utilisateur. Cela permet d'imbriquer différents tests et traitements.

Chaque chaîne peut être vue comme un ensemble de tests, chacun ayant pour résultat l'envoi du paquet vers la cible spécifiée si la condition est vérifiée. Si ce n'est pas le cas, on passe à la suivante. En arrivant à la fin d'une des chaînes du tableau précédent, une cible par défaut est utilisée. A la fin d'une chaîne utilisateur, si aucune décision n'a été prise, on revient à la chaîne appelante.

Voici les cibles prédéfinies les plus courantes :

Cibles prédéfinies
CibleDescription
ACCEPTLes paquets envoyés vers cette cible seront tout simplement acceptés et pourront poursuivre leur cheminement au travers des couches réseaux.
DROPCette cible permet de jeter des paquets qui seront donc ignorés.
REJECTPermet d'envoyer une réponse à l'émetteur pour lui signaler que son paquet a été refusé.
LOGDemande au noyau d'enregistrer des informations sur le paquet courant. Cela se fera généralement dans le fichier /var/log/messages (selon la configuration du programme syslogd).
MASQUERADECible valable uniquement dans la chaîne POSTROUTING de la table nat. Elle change l'adresse IP de l'émetteur par celle courante de la machine pour l'interface spécifiée. Cela permet de masquer des machines et de faire par exemple du partage de connexion.
SNATEgalement valable pour la chaîne POSTROUTING de la table nat seulement. Elle modifie aussi la valeur de l'adresse IP de l'émetteur en la remplaçant par la valeur fixe spécifiée.
DNATValable uniquement pour les chaînes PREROUTING et OUTPUT de la table nat. Elle modifie la valeur de l'adresse IP du destinataire en la remplaçant par la valeur fixe spécifiée.
RETURNUtile dans les chaînes utilisateurs. Cette cible permet de revenir à la chaîne appelante. Si RETURN est utilisé dans une des chaînes de base précédente, cela est équivalent à l'utilisation de sa cible par défaut.

Présentation d'iptables | (Haut de page) |

iptables est l'outil qui est fourni à l'administateur pour agir sur tous les concepts vus précédemment et pour modifier les règles de filtrage donc.

La première option à connaître est -t qui permet de spécifier le nom de la table sur laquelle porteront les autres paramètres. Si cette option n'est pas spécifiée, ce sera par défaut la table filter.

On peut aussi demander à iptables de charger un module particulier avec l'option -m. Ce module peut ajouter de nouvelles tables ou de nouvelles manières de tester les paquets.

Il faut ensuite indiquer une commande pour indiquer par exemple qu'une nouvelle règle doit être ajoutée dans la chaîne spécifiée. Ci-après la liste des options les plus courantes pour spécifier une commande. Une seule à la fois peut être présente, et toutes devront être suivies du nom de la chaîne à prendre en compte.

Options d'iptables
OptionRôle
-LAffiche toutes les règles de la chaîne indiquée.
-FSupprime toutes les règles de la chaîne. Si aucune chaîne n'est spécifiée, toutes celles de la table sont vidées.
-NCrée une nouvelle chaîne utilisateur avec le nom passé en paramètre.
-XSupprime la chaîne utilisateur. Si aucun nom n'est spécifié, toutes les chaînes utilisateur seront supprimées
-PModifie la politique par défaut de la chaîne. Il faut indiquer en plus comme paramètre la cible à utiliser.
-AAjoute une règle à la fin de la chaîne spécifiée.
-IInsère la règle avant celle indiquée. Cette place est précisée par un numéro qui fait suite au nom de la chaîne. La première porte le numéro 1. Si aucun numéro n'est indiqué, la règle est insérée au début.
-DSupprime une règle de la chaîne. Soit un numéro peut être précisé, soit la définition de la chaîne à supprimer (ses tests de concordance et sa cible).

Quelques exemples montreront l'utilisation de ces options.

Les tests de concordance et options de cible | (Haut de page) |

Pour pouvoir créer des règles, il faut effectuer des tests de concordance. Ceux-ci définissent les caractéristiques que doivent présenter les paquets pour être concernés par la règle, et donc être dirigés vers la cible spécifiée.

Certains paramètres de tests ne sont valables que si d'autres options sont précisées. Le tableau suivant, qui présente les plus utilisés de ces tests, indiquera donc le contexte d'utilisation qui correspondra aux autres options nécessaires (avec leurs paramètres) s'il y en a.

Plusieurs de ces paramètres existent en version longue ou abrégée, celle longue étant précédée de -- (deux tirets). Lorsqu'une version courte existe, elle sera indiquée ci-dessous. L'utilisation de l'une ou de l'autre est strictement équivalent.

Paramètres de test
Option de testVersion abrégéeContexteDescription
--source-sAdresse source. Cette option est suivie par l'adresse de la machine qui doit avoir émis ce paquet. Celle-ci peut-être sous forme numérique, ou sous forme de nom de machine. Si l'adresse est précédée d'un ! (point d'exclamation), le test est alors inversé et la condition sera donc remplie si l'adresse de l'émetteur ne correspond pas à celle spécifiée. On peut aussi à la suite de l'adresse indiquer un masque de sous-réseau en séparant par un / (barre oblique) ces deux éléments.
--destination-dAdresse de destination. On trouve ensuite l'adresse de destination du paquet, avec les mêmes possibilités que pour --source.
--in-interface-iChaînes INPUT, FORWARD ou PREROUTINGInterface d'entrée. Suivie par le nom de l'interface par laquelle doivent arriver les paquets. Par exemple, ce sera ppp0 pour un modem, ou eth0 pour la première carte ethernet. Le nom de l'interface peut être précédé d'un ! (point d'exclamation) pour inverser le test.
--out-interface-oChaînes FORWARD, OUTPUT ou POSTROUTINGInterface de sortie. Suivi du même paramètre que pour l'option --in-interface. Dans ce cas-là est testée l'interface par laquelle vont être envoyés les paquets.
--protocol-pProtocole. Permet de vérifier le protocole du paquet. Les valeurs littérales pouvant être utilisées à la suite de cette option sont tcp, udp, icmp ou all qui les regroupe toutes.
--source-port--protocol tcp ou --protocol udpPort source. Cette option permet de vérifier le port source (celui utilisé par l'émetteur). Il est spécifié sous forme numérique ou par nom de service (comme http ou smtp). On peut aussi indiquer une plage de port en séparant les deux bornes par : (deux points). Par exemple 25:110 pour tous les ports compris entre 25 et 110. Pour exclure le ou les ports spécifiés, il faut utiliser un ! (point d'exclamation) après --source-port.
--destination-port--protocol tcp ou --protocol udpPort destination. Compare le port utilisé pour se connecter sur la machine avec la valeur ou plage de valeur indiquée. Ces dernières sont indiquées comme pour --source-port.
--state-m stateEtat du paquet. Une liste de plusieurs valeurs peut être indiquée en les séparant par des virgules. L'état de ce paquet est comparé alors à ces valeurs. NEW correspond à un paquet initiant une nouvelle connexion. ESTABLISHED est un paquet participant à une conversation déjà établie. RELATED est pour un paquet qui ouvre une nouvelle connexion, mais ceci en rapport avec une précédente déjà établie. INVALID indique un paquet qui n'est rattaché à aucune connexion.

Une fois spécifiés les critères auxquels doivent répondre les paquets, il faut indiquer vers quelle cible l'envoyer s'il y répond. Cela se fait avec l'option --jump ou en abrégé -j suivie par le nom de la cible.

Certaines de ces cibles nécessitent des options pour les paramétrer. Elles sont indiquées sous la même forme que les options de tests. Ci-suivent les plus couramment utilisées.

Options des cibles
ParamètreCibleDescription
--log-prefixLOGPréfixe dans le journal. Cette option est suivie d'une chaîne de moins de 30 caractères. Celle-ci est ajoutée devant les lignes qui sont inserées dans les fichiers journaux. Cela permet de plus facilement les retrouver par la suite.
--reject-withREJECTMessage de rejet. Avec --reject-with, on peut indiquer quel type de message ICMP doit être envoyé vers la machine dont le paquet est rejeté. A la suite de l'option, on peut trouver icmp-net-unreachable (réseau inaccessible), icmp-host-unreachable (machine inaccessible), icmp-port-unreachable (port inaccessible), icmp-proto-unreachable (protocole inaccessible), icmp-net-prohibited (réseau interdit) ou icmp-host-prohibited (machine interdite). Si le type de protocol est tcp, on peut aussi trouver tcp-reset qui indique qu'il faudra envoyer un paquet RST qui permet de fermer une connexion.
--to-sourceSNATNouvelle adresse source. Permet d'indiquer quelle adresse doit être mise en tant qu'adresse source dans le paquet pour remplacer celle existante. On peut en indiquer une seule, ou une plage en séparant les bornes par un - (tiret). On peut aussi modifier le port avec cette option. Cela se fait en spécifiant un port (ou une plage de ports) en le séparant de l'adresse par : (deux points).
--to-destinationDNATNouvelle adresse destination. Utilisée pour modifier l'adresse de destination en la remplaçant par celle indiquée à la suite de cette option. Le format du paramètre suivant --to-destination est le même que pour --to-source.

Exemples courants | (Haut de page) |

Voici une suite de quelques exemples pouvant être utiles. Ils seront accompagnés d'une explication sur leurs rôles. Vous pouvez aussi vous référer au script de pare-feu que j'utilise.

> iptables -t filter -A INPUT --source 207.46.134.190 --jump DROP

Cet exemple permet de jeter tout ce qui vient de l'adresse 207.46.134.190. La spécification de la table filter est facultative car par défaut c'est celle qui est utilisée. On pourrait abréger cette commande en la suivante :

> iptables -A INPUT -s 207.46.134.190 -j DROP

Qui produit exactement le même résultat. L'utilisation de -A ajoute cette règle à la fin de la chaîne. Donc si une précédente règle laissait passer des paquets, ils n'arriveront pas jusqu'à celle-ci.

Pour construire un ensemble de règles de filtrage, on commencera généralement par supprimer toutes celles créées précédemment.

> iptables -t filter -F

Cette commande vide intégralement la table filter de toutes les règles qui auraient précédemment été définies.

Ensuite on indiquera plutôt en premier tout ce que l'on accepte, pour ensuite refuser tout le reste. On commencera donc par des règles ressemblant à la suivante :

> iptables -t filter -A INPUT --protocol tcp --destination-port 80 --jump ACCEPT

Cette règle permet de laisser passer tout le trafic TCP entrant sur le port 80. Ce sera utile par exemple si un serveur web (par défaut sur le port 80) est présent sur la machine. Mais il faut ensuite que les réponses envoyées puissent l'être. Dans ce cas, la règle précédente autorise des personnes extérieures à demander une page. Pour que le serveur puisse la transmettre, il faudra une règle comme suit :

> iptables -t filter -A OUTPUT --protocol tcp --source-port 80 --jump ACCEPT

Pour laisser les utilisateurs internes accéder aux sites Internet, on peut définir cette règle :

> iptables -t filter -A OUTPUT --protocol tcp --destination-port 80 --jump ACCEPT

Celle-ci permet donc de laisser passer les paquets à destination du port 80 d'une machine extérieure. Et pour que les utilisateurs puissent recevoir les pages envoyées par ces machines, il faudra ajouter :

> iptables -t filter -A INPUT --protocol tcp --source-port 80 --jump ACCEPT

Pour ne pas confondre ce que font les quatre règles précédentes, voici un résumé de ce qu'elles font :

  • La première laisse entrer ce qui est pour le port 80 de la machine.
  • La deuxième laisse sortir les paquets par le port 80 de la machine.
  • La troisième laisse sortir ce qui est pour le port 80 d'une autre machine.
  • La quatrième laisse entre ce qui vient du port 80 d'une autre machine.

Dans le cas où ce sont les seules choses permises sur cette machine, on terminera par indiquer que tout le reste doit être ignoré. Arriveront à ce point seulement les paquets qui n'auront pas répondu aux critères précédents. On peut donc alors les jeter (avec la cible DROP). Une règle toute simple pourrait être la suivante :

> iptables -t filter -A INPUT --jump DROP
> iptables -t filter -A OUTPUT --jump DROP

Ces deux règles indiquent de détruire tous les paquets entrants ou sortants. On peut souhaiter en plus garder une trace de tous ces paquets refusés. Nous allons créer une chaîne utilisateur pour cela :

> iptables -N bloquer

Cette chaîne porte le nom "bloquer". On y ajoute ensuite une première règle pour l'enregistrement :

> iptables -t filter -A bloquer --jump LOG --log-prefix "Jeté: "

Tous les paquets passant par cette chaîne seront enregistré avec le message "Jeté: " devant.

On ajoute ensuite une règle semblable à celle vue plus haut :

> iptables -t filter -A bloquer --jump DROP

Tout ce qui arrive à la fin est donc bloqué. On dirige alors tous les paquets entrants et sortants vers cette chaîne plutôt que vers la cible DROP :

> iptables -t filter -A INPUT --jump bloquer
> iptables -t filter -A OUTPUT --jump bloquer

Si aucune connexion de l'extérieur ne doit être possible, si par exemple aucun serveur n'est disponible sur la machine, on peut utiliser une règle se basant sur les états :

> iptables -t filter -A INPUT -m state --state NEW,INVALID --jump DROP

Cet exemple est indépendant des précédents. Mais on peut l'utiliser à la place de la règle finale qui rejetait tout pour autoriser les connexions de l'intérieur.

On indique ici que le module state doit être chargé. Puis sur les paquets entrants, on refuse tous ceux qui sont à l'état NEW (nouvelle connexion créée) ou INVALID (non liés à une connexion existante). Cela permet d'empêcher toute connexion depuis l'extérieur. Si des services sont proposés depuis la machine, on fera précéder cette règle par celles permettant d'accepter les paquets sur les ports souhaités.

Un dernier exemple avec les états, qui est un peu le pendant de la règle ci-dessus.

> iptables -t filter -A INPUT -m state --state ESTABLISHED,RELATED --jump ACCEPT

Tous les paquets entrants correspondant à une connexion déjà établie seront acceptés. En plaçant cette règle au début de la chaîne INPUT, cela évite de devoir explicitement laisser passer les paquets entrants vers les ports laissés ouverts pour un service interne (la deuxième règle de la liste de quatre vue précédemment).

Voici donc un petit exemple de script de pare-feu. Dans ce cas, on suppose que le seul serveur tournant sur la machine est un serveur web sur le port 80 et que l'on souhaite refuser toute autre connexion depuis l'extérieur. En revanche, toutes celles qui sont créées depuis l'intérieur doivent pouvoir passer.

iptables -t filter -F
iptables -t filter -A INPUT -m state --state ESTABLISHED,RELATED --jump ACCEPT
iptables -t filter -A INPUT --protocol tcp --destination-port 80 --jump ACCEPT
iptables -t filter -A INPUT -m state --state NEW,INVALID --jump DROP

En fait, on ne crée de règle explicites que pour la chaîne INPUT dans cet exemple. La chaîne OUTPUT ayant pour politique par défaut ACCEPT, tous les paquets sortants seront donc acceptés.

Ces exemples concernaient la table filter. La page décrivant comment partager une connexion en donne pour la table nat.