Iptables

De wiki jackbot
Aller à la navigation Aller à la recherche

Présentation

Iptables est un moyen de configurer netfilter (pare-feu au niveau du noyau linux) en utilisant différentes tables : INPUT, FORWARD et OUTPUT. Il est cependant aussi possible de créer ses propres tables. Voici un visuel très bien fait, permettant de comprendre les différentes étapes

Iptables-Flowchart.jpg


Configuration de base

La seule bonne manière de faire est de tout bloquer puis de configurer ce que l'on souhaite accepter en entrée/sortie. Bien lire ce paragraphe jusqu'au bout pour éviter les blocages. Pour ce faire il faut commencer par vider les tables avec la commande :

iptables -t filter -F

Puis supprimer toutes les chaînes que l'utilisateur a pu rentrer :

iptables -t filter -X

Et enfin bloquer toutes les entrées/sorties :

iptables -t filter -P INPUT   DROP
iptables -t filter -P FORWARD DROP
iptables -t filter -P OUTPUT  DROP

À partir de ce moment là votre ordinateur n'acceptera plus aucune données venant de l'extérieur et interdira également toute sortie. S'il s'agit d'un ordinateur distant auquel vous accédez via ssh, votre connexion sera coupé !! Pour éviter que cela n'arrive il faut ajouter les lignes :

iptables -A INPUT  -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

qui permettent de ne pas casser les connexions déjà établies. Un peu plus loin on verra comment préciser ces règles.

Ouvrir un port

Maintenant que la base est là, on peut autoriser certaines connexions.

Pour tout le monde

Prenons l'exemple de ssh. Si vous souhaitez autoriser l'accès à une machine, il faut ajouter les lignes :

#autorisation d'entrer
iptables -t filter -A INPUT -p tcp --dport 22 -j ACCEPT
#autorisation de sortir
iptables -t filter -A OUTPUT -p tcp --sport 22 -j ACCEPT

Pour une seule ip

Si vous voulez qu'un seul ordi de votre réseau puisse se connecter en ssh, on peut restreindre l'accès qu'a une seule addresse :

#autorisation d'entrer
iptables -t filter -A INPUT -s 192.168.1.42 -p tcp --dport 22 -j ACCEPT
#autorisation de sortir
iptables -t filter -A OUTPUT -d 192.168.1.42 -p tcp --sport 22 -j ACCEPT

Pour un sous-réseau

Ou alors autoriser à un sous réseau :

#autorisation d'entrer
iptables -t filter -A INPUT -s 192.168.1.0/24 -p tcp --dport 22 -j ACCEPT
#autorisation de sortir
iptables -t filter -A OUTPUT -d 192.168.1.0/24 -p tcp --sport 22 -j ACCEPT

Redirection de ports

En plus de gérer les autorisation d'entrées et sorties iptables permet de faire du routage en redirigeant certains ports en entrée vers une autre adresse ip et/ou un autre port. La première étape sera de changer la destination du paquet, ce qui se fait en modifiant la chaîne PREROUTING :

#redirection du port 25 vers le serveur courriel
iptables -t nat -A PREROUTING -p tcp --dport 25 -j DNAT --to-destination 192.168.1.42:25

Cependant cela ne suffit pas, car suite à cette modification, le paquet ne sera plus destiné à la machine qui vient de le recevoir. Si vous regardez le schéma vous comprendrez que le paquet va alors devoir traverser la chaîne FORWARD

iptables -A FORWARD -p tcp --dport 25 -j ACCEPT

Il reste tout à fait possible de combiner les options de source et de destination afin de limiter les adresse ip autorisées à passer cette redirection.

Passerelle

Jusqu'à présent les règles décrites ne s'occupe que des paquets voyageant dans un seul sens. Suite à la redirection la source des paquets n'a pas changé et il est possible que la machine recevant ce paquet soit dans l'incapacité de contacter directement la source. Ce qui empêchera l'envoie d'une réponse. Regardons comment créer une vraie passerelle qui aura pour rôle de faire communiquer deux réseaux distincts.

Contexte

Diagramme reseau.png

Ordi 1 et Ordi 2 sont sur des réseaux différents et ne peuvent donc pas communiquer directement. Passerelle étant sur les deux réseaux elle pourra jouer le rôle d'intermédiaire.

Ordi 1 contacte Ordi 2

Dans ce cas il faut que Ordi 1 envoie ses paquets à Passerelle et Passerelle doit effectuer une redirection en changeant les chaînes PREROUTING et FORWARD comme vu précédemment

iptables -t nat -A PREROUTING -p tcp --dport 25 -j DNAT --to-destination 192.168.1.2:25
iptables -A FORWARD -p tcp --dport 25 -j ACCEPT

Ordi 2 recevra effectivement les paquets, mais ils auront pour source : 192.168.0.2, qui est un réseau inaccessible pour Ordi 2. La réponse sera donc perdu ... Il faut donc faire croire à Ordi 2 que le paquet provient de Passerelle. Pour cela deux solutions SNAT et MASQUERADE

SNAT

SNAT est l'acronyme pour Source Network Address Translation. Qui est très simple à comprendre, on va simplement modifier l'adresse source du paquet.

iptables -t nat -A POSTROUTING -p tcp --dport 25 -j SNAT --to-source 192.168.1.1

Grâce à ça les paquets qui se présenteront à Passerelle et qui sont à destination du port 25 seront redirigé vers Ordi 2 sur le port 25 et la source sera changé pour 192.168.1.1, l'adresse de Passerelle que Ordi 2 peut contacter. Mais les plus malins d'entre vous auront sans doute une question : "Si pour une raison ou une autre je change l'adresse de ma passerelle, faut-il que je change la configuration ?", la réponse est oui. C'est pour cela qu'on va aborder également la deuxième façon de faire : MASQUERADE

MASQUERADE

Le masquerading fait exactement la même chose que SNAT, à la différence qu'il faut lui fournir l'interface par laquelle le paquet va sortir.

iptables -t nat -A POSTROUTING -p tcp --dport 25 -o eth0 -j MASQUERADE

Avec cette ligne de commande, les paquets sortant auront pour source la même ip que l'interface eth0. Dans ce cas, si l'adresse de Passerelle change, pas besoin de refaire la configuration.

Ordi 2 répond à Ordi 1

Arrivé ici Ordi 2 a bien reçu le paquet de Ordi 1 et croit qu'il provient de Passerelle. Il va donc répondre à Passerelle. On pourrait essayer d'écrire des règles pour débloquer le chemin inverse, mais il y a plus simple :

iptables -A FORWARD -p tcp --sport 25 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

Pour bien comprendre ce qu'il se passe il faut savoir qu'Iptables ne fait qu'agir sur NetFilter qui est un composant du noyau linux. Au moment où un paquet passera par la chaîne PREROUTING il sera tracé. La réponse se verra donc attribuer la bonne adresse de destination et si vous regardez le schéma tout en haut de cette page, vous comprendrez alors que le paquet passera par la chaîne FORWARD. Donc nul besoin d'interagir avec les chaînes INPUT et OUTPUT, FORWARD suffit.

Récapitulatif

iptables -t nat -A PREROUTING -p tcp --dport 25 -j DNAT --to-destination 192.168.1.2:25
iptables -A FORWARD -p tcp --dport 25 -j ACCEPT
iptables -t nat -A POSTROUTING -p tcp --dport 25 -o eth0 -j MASQUERADE
iptables -A FORWARD -p tcp --sport 25 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

Voici donc les quatre règles qui vous permettront de rediriger correctement un port ouvert sur Ordi 2. On peut encore affiner ces règles en limitant à certaines adresses sources si nécessaire.

Chaînes personnalisées

Il peut être très intéressant d'utiliser des chaînes personnalisées.

Création d'une chaîne

Pour créer une nouvelle chaîne il suffit de taper :

iptables -N nom_de_la_chaine

Par défaut cette chaîne sera vide et n'aura donc aucune utilité.

Remplissage

On peut ajouter des règles à cette chaîne avec :

iptables -A nom_de_la_chaine -p tcp --dport 22 -j ACCEPT

Qui aura donc pour but d'accepter les paquets entrant à destination du port 22 Cependant aucun paquet n'atteindra cette règle ...

Où se trouve une chaîne personnalisée dans le diagramme ?

Bonne question. Pour l'instant, nul part ! Pour associer notre nouvelle chaîne au diagramme il faut utiliser ce genre de commande :

iptables -A INPUT -j nom_de_la_chaine

Ce qui aura pour effet qu'un paquet rentrant passera en premier lieu aux travers des règles INPUT. Mais si aucune correspondance n'est trouvé, à la sortie de INPUT, le paquet sera dirigé vers la chaîne : nom_de_la_chaine. Une chaîne personnalisée peut donc se retrouver n'importe où dans le diagramme suivant les associations qui seront faite.

Suppression d'une chaîne

Dans un premier temps il faut vider la chaîne avec :

iptables -F nom_de_la_chaine

Puis la supprimer avec :

iptables -X nom_de_la_chaine

Exemple de redirection de port

Si on reprend notre exemple de redirection de port vu plus haut, on peut avec des chaînes personnalisées, classer tout ça de manière plus propre

iptables -t nat -N SMTP_PREROUTING
iptables -t nat -A SMTP_PREROUTING -p tcp --dport 25 -j DNAT --to-destination 192.168.1.2:25
iptables -t nat -A PREROUTING -j SMTP_PREROUTING
 
iptables -N SMTP_FORWARD
iptables -A SMTP_FORWARD -p tcp --dport 25 -j ACCEPT
iptables -A SMTP_FORWARD -p tcp --sport 25 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -j SMTP_FORWARD
 
iptables -t nat -N SMTP_POSTROUTING
iptables -t nat -A SMTP_POSTROUTING -p tcp --dport 25 -o eth0 -j MASQUERADE
iptables -t nat -A POSTROUTING -j SMTP_POSTROUTING

Ce qui rend les choses plus lisible quand on affiche les règles Iptables :

Chain SMTP_PREROUTING (1 references)
target     prot opt source               destination         
DNAT       tcp  --  anywhere             anywhere             tcp dpt:smtp to:192.168.1.2:25

Pour supprimer ces règles, il suffira simplement de connaître leurs noms :

#vider les chaines
iptables -t nat -F SMTP_PREROUTING
iptables -F SMTP_FORWARD
iptables -t nat -F SMTP_POSTROUTING
 
#suppression des réferences
iptables -t nat -D PREROUTING -j SMTP_PREROUTING
iptables -D FORWARD -j SMTP_FORWARD
iptables -t nat -D POSTROUTING -j SMTP_POSTROUTING
 
#suppression des chaines
iptables -t nat -X SMTP_PREROUTING
iptables -X SMTP_FORWARD
iptables -t nat -X SMTP_POSTROUTING

Blocage d'adresse ip

Cas général

Lorsqu'on a un serveur il arrive que l'on voit des adresses essayant de se connecter, souvent à des fin malveillantes. Pour les bloquer il suffit d'ajouter cette ligne :

iptables -I INPUT -s 155.133.82.146 -j DROP

L'option -I spécifie que cette règle doit passer avant toutes les autres dans la table INPUT, de cette manière on s'assure que même s'il existe une règle permettant l'accès elle ne sera pas appliqué pour cette adresse ip.

Cas de Docker

Avec Docker la règle modifiant la table INPUT n'aura aucun effet, car Docker crée sa propre table pour gérer les entrées/sorties des conteneurs. Par défaut il ouvre les ports spécifiés des conteneurs vers le monde extérieurs sans aucune restriction. Pour en limiter l'accès il faut alors utiliser :

iptables -I DOCKER -s 155.133.82.146 -j DROP

Qui va modifier la table créée par Docker et bloquer l'adresse ip qui tente de se connecter sans cesse.

Automatisation des blocages

fail2ban est un logiciel permettant d'ajouter automatiquement des adresses ip à la liste noire lorsque trop de tentative infructueuse de connexion ont eu lieu.

Limitation de la fréquence des paquets

Prenons un cas concret, lorsqu'on possède un serveur on s'y connecte en SSH. Cependant le port 22 est l'un des premier à être scanné par des attaquants et parfois de très nombreuses fois par seconde. Il est donc intéressant de limiter la fréquence à laquelle un port peut être questionné chaque seconde.

Chaînes personnalisées

Pour faire ça propre, commençons par créer des chaînes personnalisées :

iptables -N SSH
iptables -N SSH_FAIL
iptables -N SSH_ACCEPT

Il faut ensuite créer un "point d'entrée" vers la chaîne SSH

iptables -t filter -A INPUT -p tcp --dport 22 -j SSH

De cette façon, tous les paquets à destination du port 22 seront redirigés vers la chaîne SSH.

Ne pas se tirer une balle dans le pied

Un des points important est de se laisser une porte ouverte. C'est à dire qu'il faut créer en premier une exception pour que notre accès vers notre serveur ne soit pas limité. On peut écrire une règle de ce type :

iptables -A SSH -s 192.168.1.122 -i eth0 -j ACCEPT

En adaptant évidemment l'adresse IP et/ou l'interface à laquelle on se connecte.

Suivre une adresse IP

Maintenant il faut suivre le comportement d'une adresse IP. Pour cela il faut l'ajouter à la liste

iptables -A SSH -m state --state NEW -m recent --name SSH --set
#si un nouveau paquet arrive qu il est dans la liste SSH qu il a moins de 60s et qu'il a été reçu au moins 3 fois alors il est jeté
iptables -A SSH -m state --state NEW -m recent --name SSH --update --seconds 60 --hitcount 3 -j SSH_FAIL
#dans les autres cas on accepte
iptables -A SSH -m state --state NEW -m recent --name SSH --rcheck -j SSH_ACCEPT
  • La première règle fait que quand un nouveau paquet (-m state --state NEW) arrive on l'ajoute à la liste (--set).
  • La deuxième règle va permettre de mettre à jour l'horodatage (--update) et de vérifier combien de fois cette adresse a été vu au cours d'une période (--seconds 60 --hitcount 3). Dans ce cas là si l'adresse a été vu 3 fois ou plus en 60 seconde, alors le paquet est redirigé vers la chaîne SSH_FAIL.
  • La troisième règle est simplement là dans le cas contraire. Si l'adresse source n'a pas été vu plus de 3 fois en moins de 60 secondes, alors il sera redirigé vers la chaîne SSH_ACCEPT

Décision finale sur les paquets

Pour finir il faut décider de ce qu'on va faire des paquets qui sont dirigés vers les chaînes SSH_FAIL et SSH_ACCEPT. On en profite pour ajouter des log au passage :

#SSH_FAIL
#Si un paquet arrive dans cette chaine c est qu il doit être jeté
iptables -A SSH_FAIL -j LOG --log-prefix "SSH FAIL :"
iptables -A SSH_FAIL -j DROP

#SSH_ACCEPT
#Si un paquet arrive dans cette chaine c est qu il doit être accepté
iptables -A SSH_ACCEPT -j LOG --log-prefix "SSH ACCEPT :"
iptables -A SSH_ACCEPT -j ACCEPT



Sources :