Cette page est en deux parties, la première concernera l’initiation à Netfilter et la commande iptables. Des exercices sont en seconde partie.

Présentation de Netfilter et iptables

Initiation à Netfilter

iptables est un outil de gestion de pare-feu, intégré au noyau Linux (depuis la version 2.4). Le pare-feu en lui-même s’appelle Netfilter. Le principe de fonctionnement est simple, lorsqu’un paquet est reçu, il est transmis à la partie Netfilter du noyau. Netfilter va ensuite étudier ce paquet en se basant sur des règles définies par l’administrateur. Il faut bien évidemment être root sur les machines pour manipuler le pare-feu.

Les tables

Voici un petit récapitulatif des tables et de leurs fonctions :

  • filter : permet le filtrage des paquets et des connexions.
  • nat : permet le NAT et ses dérivés (NAPT par ex.).
  • mangle : permet d’altérer les paquets
  • raw : pour configurer des dérogations à conntrack, cette table est prioritaire, elle travaille sur les paquets bruts.

Intéressons-nous à la table filter qui s’appuie sur trois chaînes : INPUT, OUTPUT et FORWARD. Quand un paquet arrive de la carte réseau, le noyau regarde la destination de ce paquet. Deux possibilités :

  1. Ce paquet est destiné à cette machine : il passe dans la chaîne INPUT, en direction des processus qui l’attendent
  2. Ce paquet est destiné à une autre machine: là encore deux possibilités :

    • Le forwarding est autorisé : le paquet passe dans la chaîne FORWARD
    • Le forwarding n’est pas autorisé : le paquet est effacé

La chaîne OUTPUT concerne les paquets créés par la machine locale. Ces paquets passent par la chaîne OUTPUT immédiatement. Chacune de ces chaînes peut ensuite donner plusieurs réponses pour chaque paquet :

  • ACCEPT : le paquet est accepté
  • DROP : le paquet est refusé, on l’efface et on ne répond rien
  • REJECT : le paquet est refusé et on signale le rejet à l’expéditeur

Chaque chaîne possède une politique (policy) de filtrage. Nous verrons par la suite que l’administrateur peut créer des chaînes personnalisées supplémentaires.

iptables flowchart

iptables flowchart

Filtrage statique

# afficher le contenu de la table filter
iptables -t filter -L -v
# afficher le contenu des autres tables
iptables -t nat -L -v
iptables -t mangle -L -v
iptables -t raw -L -v

Si on se connecte en ssh à une machine distante, la seule chose qui change c’est le nombre de paquets qui sont passés. Que ce soit en INPUT ou en OUTPUT.

Quelques options de filtrage

  • -s : ip source
  • -d : ip destination
  • -p : protocole des couches 3 et 4
  • –dport : port destination
  • –sport : port source
  • -i et -o : interfaces entrée et sortie
  • -j : cible (DROP etc.)

Quelques exercices

On va vouloir empêcher les connexions en ssh à sa machine, on part du principe que ssh c’est sur le port 22 en TCP.

# empêcher les connexions ssh silencieusement
iptables -I INPUT -p tcp --dport 22 ­-j DROP

Si on tente une connexion, aucune réponse du serveur, ça attend le timeout. Si on veut être gentil, on indiquera à la personne qui tente la connexion qu’on ne veut pas qu’elle se connecte. Pour cela, on ne va pas « drop » la connexion, on va la « reject », il y aura une signalisation qui se fera.

# empêcher les connexions ssh avec REJECT
iptables -I INPUT -p tcp --dport 22 -j REJECT

Cette fois-ci, on accepte la connexion ssh à partir d’un seul poste, qui a l’IP 10.0.0.1. Cette règle doit être insérée avant la règle précédente qui rejette la connexion.

# accepter la connexion ssh à partir d'un poste
iptables -I INPUT -s 10.0.0.1 -p tcp --dport 22 ­-j ACCEPT

Si on regarde à nouveau la table filter, on peut voir les règles en question. Les compteurs des règles ont augmentés également.

Pour remettre à zéro les compteurs de paquets (INPUT, OUTPUT), il faut utiliser l’option -Z.

# remettre à zéro les compteurs
iptables -Z

Indiquer l’inverse d’une règle

Un exemple simple d’utilisation de l’inverse d’une règle serait qu’on veut laisser passer tous les paquets allant vers une machine sauf les paquets TCP. Pour cela, il faut ajouter un point d’exclamation avant une propriété.

# accepter tout sauf les paquets tcp
iptables -I INPUT ! -p tcp -j ACCEPT

Il faut qu’on puisse voir les numéros de règles, pour pouvoir les sélectionner plus tard, pour les supprimer ou en ajouter avant par exemple.

# afficher le numéro des règles
iptables -L -v --line-numbers

Supprimer une règle

Pour supprimer une règle, on va regarder son numéro pour pouvoir la sélectionner.

# suppression de la règle 1 de INPUT dans filter
iptables -t filter -D INPUT 1
# suppression de toutes les règles
iptables -t filter -F

Les modules

iptables peut également accepter des fonctions supplémentaires via un système de modules (par exemple, tracer une connexion). On peut les charger de façon implicite (par exemple -p tcp) ou de façon explicite (-m <module>). Ils sont listés dans la partie “MATCH EXTENSIONS” du manuel. Par exemple, addrtype, ah, cluster

  • multiport: sélectionner des ensembles (ou des suites) de ports

On va utiliser le module multiport pour spécifier plusieurs ports sources et/ou destinations dans une seule règle, afin de désactiver l’accès aux ports 1 à 10 et 80 à 1023.

Plage de ports: debut:fin. Suite de ports: p1,p2,p3.

# utilisation d'un module
iptables -t filter -I INPUT -p tcp -m multiport --ports 1:10,80:1023 -j DROP
# supprimer les règles, table filter, chaîne INPUT
iptables -t filter -F INPUT

L’accumulation des règles se faisant assez rapidement, nous allons chercher un moyen d’organiser nos règles. Pour cela, nous pouvons créer nos propres chaînes utilisateurs. On va créer une nouvelle chaîne utilisateur dans la table filter, et la nommer sshchain par exemple.

# création d'une chaîne dans la table filter
iptables -t filter -N sshchain

On transfère l’étude de tous les paquets destinés à ssh (au port 22) depuis la chaîne INPUT vers sshchain. Ensuite, ajoutez les règles dans cette nouvelle chaîne autorisant uniquement votre voisin à se connecter en ssh sur votre machine.

# utilisation de chaînes utilisateur
iptables -I INPUT -p tcp --dport 22 -j sshchain
iptables -I sshchain -s 130.79.92.67 -p tcp --dport 22 -j ACCEPT
iptables -A sshchain -j DROP

Il existe deux fonctionnalités intéressantes, à savoir l’historique et le retour à une chaîne supérieure. Dans le manuel, voir la section TARGET EXTENSIONS. Le retour à la chaîne supérieure se fait automatiquement à la fin de la table utilisateur. Pour forcer le retour à la chaîne supérieure il faut utiliser la cible RETURN. Pour l’historique, la cible LOG. Le contenu du fichier /var/log/kern.log est intéressant.

# refuser tous les paquets de la chaîne INPUT
iptables -F # on supprime toutes nos règles
iptables -P INPUT DROP

On va écrire une liste de règles pour autoriser une machine à se connecter sur les ports 22 (ssh) et 23 (telnet) de votre machine. On va également journaliser les connexions ssh que notre machine refusera.

# journalisation des connexions qu'on refuse
iptables -t filter -I INPUT -s 130.79.92.67 -p tcp -m multiport --ports 22,23 -j ACCEPT 
iptables -t filter -I INPUT -j LOG

On va filtrer maintenant les paquets sortants de notre machine. On interdit tout trafic web sortant (port 80 et port 443).

# on interdit le trafic web sortant
iptables -F # on supprime toutes nos règles
iptables -I INPUT -j ACCEPT # on accepte les règles en entré
iptables -t filter -I OUTPUT -p tcp -m multiport --ports 80,443 -j DROP

On va autoriser uniquement l’utilisateur root à faire des connexions sur le web. Pour cela on va utiliser le module « owner ».

# autorisation suivant l'utilisateur
iptables -I OUTPUT -m owner --uid-owner 0 -p tcp -m multiport --ports 80,443 -j ACCEPT

Imposer des limites de connexion

La Smurf Attack utilise l’envoi de requêtes icmp (comme ping) en broadcast en prenant l’identité de l’émetteur. Pour contrer cette attaque, on pourrait ne pas répondre tout le temps aux les requêtes ping en broadcast. On peut utiliser le module « limit » pour cela.

# limiter les attaques smurf en imposant un rate limit aux réponses ping en broadcast
iptables -A INPUT -p icmp -m icmp --icmp-type 8 -m limit --limit 1/second -j ACCEPT

Filtrage dynamique

Nous avons vu Netfilter travaillant paquet par paquet, sans se soucier des paquets précédents ni des suivants. Il n’a donc pas de notion de session ou de suivi d’un flux dans le temps. Cependant, parfois il est important de procéder à un filtrage dynamique lié à ce qui s’est passé précédemment. Le principe d’un filtrage dynamique est de contrôler le trafic sortant pour en déduire ce qu’il faut laisser entrer. Le module « state » permet de réaliser un filtrage dynamique en s’appuyant justement sur le type de flux (nouveaux flux, flux établis, flux liés à un autre flux, etc.). On peut utiliser l’option --state ETAT pour faire des règles plus précises.

# suppression des règles et chaînes
iptables -F # supprimer les règles
iptables -X # supprimer les chaînes

Pour la suite on va considérer qu’on a deux machines:

  • machine A, adresse : 10.0.0.1
  • machine B, adresse : 10.0.0.2

Sur A on exécute nc -l 3000 pour se mettre à l’écoute sur le port 3000. Sur B on exécute nc @IP-A 3000 pour ouvrir une connexion tcp avec A. Maintenant, tout ce qu’on a écrit d’un coté est reçu de l’autre.

# créer une connexion TCP entre deux machines
nc -l 3000
nc 10.0.0.2 3000

Lorsqu’on tape une chaîne, elle est transmise.

# on interdit tout le flux
iptables -I INPUT -j DROP

On va autoriser la connexion.

# on accepte la connexion avec le module state
iptables -I INPUT -p tcp --dport 3000 -m state --state NEW -j ACCEPT

Là, les messages ne peuvent pas passer, mais la connexion TCP se fait. Maintenant on autorise la connexion et l’échange de messages.

# on accepte la connexion et l'échange de messages
iptables -I INPUT -p tcp --dport 3000 -m state --state NEW,ESTABLISHED -j ACCEPT

La table « NAT »

Quelques rappels concernant le NAT

Le NAT est une technique qui consiste à modifier un paquet qui traverse une passerelle pour réécrire soit l’adresse IP source du paquet (Source NAT), soit l’adresse IP de destination (Destination NAT). Le Source NAT permet de dissimuler une ou plusieurs machines (ayant des adresses privées) derrière une seule adresse IP publique. Le Destination NAT fait exactement l’inverse, à savoir réécrire l’adresse IP de destination afin que le paquet soit redirigé sur une autre machine. On pourrait ainsi rediriger les connexions sur le port 21 vers un serveur ftp hébergé en réalité sur un autre PC écoutant sur le port 2021. Au niveau netfilter, le NAT est implémenté à l’aide de trois chaînes, regroupées dans la table nat : PREROUTING, POSTROUTING et OUTPUT.

  • PREROUTING : utilisée pour la traduction d’adresse de destination.
  • POSTROUTING : utilisée pour la traduction d’adresse source.
  • OUTPUT : rarement utilisé, elle sert uniquement pour les connexions provenant de la machine locale.

Pour cette partie, on va mettre les machines A et B dans un réseau local privé (avec l’interface eth0), et garder qu’une des deux connectée à internet. On commence par transformer la machine connecté à internet en passerelle. N.B. Ce paramètre peut être positionné avec la commande sysctl pour une modification non-permanente, ou bien en éditant le fichier /etc/sysctl.conf pour une modification permanente.

# permet le transit de paquets par notre machine
sysctl net.ipv4.ip_forward=1

On fait en sorte que A soit notre passerelle, donc on donne la commande suivante à B pour qu’il l’utilise comme passerelle par défaut.

# configuration d'une passerelle par défaut
ip route add default via 10.0.0.1 dev eth0

À partir de maintenant, on peut utiliser une adresse IP quelconque pour la machine B. Je choisi d’utiliser l’IP 192.168.0.2 comme étant ma nouvelle adresse IP: ip a a 192.168.0.2/24 dev eth0.

Notre machine connectée à Internet agit maintenant comme un routeur. Si elle reçoit un paquet qui ne lui est pas destiné (niveau IP), la machine le transmettra en suivant ses règles de routage. Tous les paquets qui sont transmis de cette manière passeront au travers de la chaîne FORWARD et pas dans les chaînes INPUT et OUTPUT. On a donc la possibilité de filtrer les flux réseaux qui traversent notre passerelle via cette chaîne. Par dessus ces techniques standards de routage se rajoute le Source NAT et le Destination NAT qui vont permettre de protéger des réseaux internes. On peut envoyer des données, mais on ne peut pas recevoir, puisque l’adresse que nous avons n’est pas routée correctement.

Pour le moment aucun ping depuis le client vers le réseau Internet ne fonctionne, parce que la traduction d’adresses sur A n’est pas faite.

SNAT et DNAT

SNAT permet d’avoir la même IP de sortie pour plusieurs machines. L’utilisation courante de cette méthode est faite dans les *box, pour connecter autant d’ordinateurs derrière une seule IP publique.

DNAT permet de rediriger une connexion externe vers le réseau interne. Par exemple une connexion sur un port particulier (donc on peut mettre un serveur derrière un NAT, en redirigeant les ports qu’il faut).

On va mettre en place une traduction d’adresses pour que B puisse accéder au net.

# traduction d'adresses
iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 10.0.0.1
iptables -t nat -A PREROUTING -j DNAT --to-destination 192.168.0.2

La solution précédente n’est pas valide si on souhaite accéder à des machines sur le net depuis notre passerelle. On redirige actuellement tout vers la machine derrière le NAT. La solution pour faire fonctionner le NAT correctement c’est la méthode de « MASQUERADING ».

# masquerading
iptables -F -t nat
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

Table « mangle »

La table mangle est utilisée pour modifier des paquets à la volée. Ceci peut être souhaité pour diverses raisons. Par exemple, la modification du champ TTL pourrait permettre de masquer l’architecture du réseau au-delà de la passerelle. On éviterait ainsi la détection par un tiers de plusieurs machines branchées sur la même connexion.

Un exemple pour imposer un TTL à une certaine valeur (1):

# imposer un TTL dans le paquet
iptables -t mangle -I POSTROUTING -o eth0 -j TTL --ttl-set 1 

Redirection de ports et DMZ

Lorsque l’on s’héberge chez nous, on a une box qui souvent ne permet de rediriger qu’un certain nombre de ports vers des adresses IP locales, ce qui est contraignant pour quelqu’un qui utilise réellement son accès à Internet et qui héberge réellement des choses. Par exemple, un port 80 et 443 pour le web et SSL, 25 pour le mail puis 665 pour tinc, puis encore deux ou trois ports pour des jeux vidéo en ligne puis… ça va vite.

Donc une solution pour être moins bridé en nombre de ports accessibles, c’est de configurer un hôte DMZ. Cet hôte reçoit toutes les connexions entrantes sur votre adresse IP publique, et on se sert de ça pour rediriger ces connexions vers les adresses IP réseaux locales. Je ne montre pas comment configurer un hôte DMZ, c’est relativement facile à faire sur les box et ce serait laborieux à mettre sur cette page.

Une fois que cela est fait, on se retrouve avec une IP locale qui reçoit toutes les connexions, et qui va devoir en rediriger certaines. On va chercher à rediriger des connexions entrantes sur un port vers une autre IP locale et aussi vers un port sur cette machine.

Premier exemple

Exemple simple, on souhaite héberger un serveur web à la maison, on va rediriger le port 80 vers l’adresse IP de notre serveur web local, sur le même port (pas obligatoire).

# port entrant 80 vers ip locale même port
iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.0.100:80

Cela ne suffit pas, il faut aussi retenir les connexions entrantes (couple IP et port de la personne qui se connecte) pour leur renvoyer les réponses de notre serveur. Pour ça on fait du MASQUERADING sur le port ouvert.

# masquerading pour renvoyer les réponses
iptables -t nat -A POSTROUTING -p tcp --dport 80 -j MASQUERADE

Second exemple

Second exemple, on souhaite faire la même chose mais sur le port 443 (SSL actif sur nos sites web) :

# activer la redirection du port SSL
iptables -t nat -A PREROUTING -p tcp --dport 443 -j DNAT --to-destination 192.168.0.100:443
iptables -t nat -A POSTROUTING -p tcp --dport 443 -j MASQUERADE

Sauvegarder sa configuration iptables

# sauvegarder et restaurer sa configuration iptables
iptables-save > /etc/iptables.rules
cat /etc/iptables.rules | iptables-restore

Exercices (TP en binôme)

Deuxième partie de cette page, des exercices à faire sur son ordinateur. À chaque étape, vérifiez votre configuration en visualisant les messages qui passent avec tcpdump ou wireshark.

Fitrage simple

  1. visualisez les tables filter, nat, mangle et raw
  2. filtrez le port TCP 3000, le mettre en drop, puis essayez avec votre binôme d’accéder à ce port
  3. supprimez le filtre, puis filtrer le port 3000 cette fois-ci avec reject et essayez à nouveau
  4. filtrez les ports TCP 5000 à 5100 venant de votre binôme
  5. tout en conservant la règle précédente, autorisez maintenant une connexion de votre binôme au port 5050
  6. visualisez le nombre de paquets échangés
  7. enregistrez l’historique des messages
  8. supprimez les filtres

NAT

  1. votre binôme doit accéder à Internet grâce à vous, vous êtes la machine A

    1. A et B se connectent en Ethernet sur leur interface enp2s0
    2. A et B montent leur interface : ip link set up dev enp2s0
    3. A et B ajoutent une adresse sur cette interface, exemple : ip address add 10.0.0.1/24 dev enp2s0 pour A
    4. B change sa route par défaut pour passer par A : ip route del default puis ip route add default via 10.0.0.1
  2. A active le MASQUERADING sur la bonne interface pour créer du NAT
  3. vérifiez que B arrive à communiquer avec l’extérieur
  4. redirigez les messages sur le port 8080 de A vers le port 8080 de B

Sauvegarde

  1. sauvegarder ce que vous avez fait dans un fichier mes-regles-iptables
  2. supprimer vos tables
  3. importer votre configuration à nouveau, vérifiez que vos tables sont correctes
  4. supprimer de nouveau vos tables pour les prochains cours