====== Netfilter avec un grand N ======
{{tag> netfilter networking iptables}}
Le premier volet de ce dossier sur le networking avancé sous Linux est consacré à Netfilter. Petit bémol toutefois, je ne souhaite pas ici faire une présentation générique de Netfilter. Je vais donc me concentrer sur une utilisation plus technique du firewall de Linux, sans prétendre à l’exhaustivité, mais plutôt pour coucher sur le papier mes découvertes et expériences.
Il y a plusieurs points intéressants à aborder. Mais tout d’abord, un retour sur la structure interne de l’outil me semble nécessaire. Après, on parlera écriture de règles, window tracking, MARK, Ipset, Failover et un poil de Queuing pour le fun.
===== Le moteur de Netfilter =====
Netfilter fait partie intégrante du kernel. Cela lui permet d’agir au niveau du driver de la carte réseau (NIC, pour Network Interface Card). Etant positionné si bas dans le système, il lui faut une interface pour recevoir les ordres de l’utilisateur. L’interface la plus couramment utilisée est « iptables », mais il y en a d’autres, comme on va le voir.
Jan Engelhardt a réalisé deux schémas qui présentent la structure interne de Netfilter. Le premier présente son positionnement par rapport au kernel et aux composants userland.
{{ressources:dossiers:advanced_networking:nf-components.png?400x200|}}
Le core de Netfilter se situe juste au dessus de la stack réseau. Puis, on trouve les Xtables et le système de suivi de connexions (pour le stateful), et enfin les chaines de filtering et modification des paquets.
Bon, ça c’est pour l’information générale, maintenant le graphique réellement intéressant c’est celui qui représente les flux.
{{ressources:dossiers:advanced_networking:nf-packet-flow.png?400x200|}}
Dans celui-ci (que je vous conseille d’agrandir), on voit un paquet entrant en bas à gauche et sortant en bas à droite. Entre les deux, nous avons le détail exact des chaines traversées.
On retrouve, en fait, une version plus détaillée du graphique habituel qui reprend les chaines de base :
{{ressources:dossiers:advanced_networking:dport-routing-02.png?400x200|}}
Sauf que si l’on se limite à ce schéma, il nous manque des informations importantes, comme par exemple le fait que **Mangle** est toujours effectué avant **Filter** et **Nat**. Ou encore que les chaines PREROUTING et POSTROUTING peuvent faire du Mangle et du Nat mais pas de Filter.
On découvre également une table **RAW** de la chaine PREROUTING qui est traversée avant que le moteur de conntrack ne lie le paquet à une éventuelle session.
Au sujet du moteur de suivi de connexion, le conntrack, ce dernier permet de réaliser des opérations en mode Stateful en TCP et, apparemment, en UDP. Cela signifie que toute nouvelle connexion sera notée comme NEW par le kernel, puis passera en ESTABLISHED si elle est continuée puis ASSURED si elle est validée.
Les informations de conntrack sont accessibles via /proc/net/ip_conntrack :
tcp 6 431999 ESTABLISHED src=192.32.189.184 dst=192.32.189.158 sport=2186 dport=22 packets=5996 bytes=526811 src=192.32.189.158 dst=192.32.189.184 sport=22 dport=2186 packets=7593 bytes=628012 [ASSURED] mark=0 secmark=0 use=1
udp 17 5 src=192.32.189.32 dst=192.32.189.255 sport=138 dport=138 packets=2 bytes=484 [UNREPLIED] src=192.32.189.255 dst=192.32.189.32 sport=138 dport=138 packets=0 bytes=0 mark=0 secmark=0 use=1
J’en ai pris deux au hasard. La première connexion est du SSH. On voit qu’elle est établie et même assurée, ce qui signifie qu’elle ne sera pas supprimée si l’on atteint le nombre max de connexion simultanées.
A l’inverse, la connexion UDP est marquée UNREPLIED car aucun paquet retour n’a été vu. Etant donné que le port est celui de Netbios, on peut supposer qu’il s’agit la du bruit généré par un Windows sur le réseau auquel le système n’aura pas répondu.
La durée de vie d’une entrée dans le conntrack est spécifiée dans la 3éme valeur de chaque ligne. Pour la ligne TCP, cette valeur est de 5 jours, ce qui nous garantit que des connexions TCP longue durée ne sortiront pas de conntrack toutes les 5 minutes (une liaison BGP peut avoir un uptime de plusieurs semaines, voir plus…). Quand le TTL atteint zéro, l’entrée est supprimée du conntrack.
Les trois champs à la fin de chaque ligne, mark, secmark et use, ont également leurs utilité.
* secmark est utilisé par SELinux. Iptables peut marker un paquet ou une connexion dans secmark via la cible « –j SECMARK »
* mark correspond à la marque appliquée sur la connexion par la cible CONNMARK d’iptables, on utilisera cela pour faire de la QoS un peu plus tard
* use est un champ totalement obscur dont je n’ai pu trouver la signification nulle part… il est définit dans la structure « nf_conntrack_expect » du fichier « linux/include/net/netfilter/ nf_conntrack_expect.h » de la façon suivante :
/* Usage count. */
atomic_t use;
Ce qui laisse à penser que cette valeur est utilisée pour compter le nombre de connexion RELATED reliées a une connexion maitre.
D’ailleurs, je n’ai pas parlé de RELATED. C’est une fonctionnalité intéressante de Netfilter qui consiste à prévoir, moyennant une connaissance du protocole, quelle va être la connexion qui va suivre la connexion courante. L’exemple courant de l’utilisation de RELATED est le protocole FTP, qui établit deux connexions distinctes pour les contrôles et les données. La connexion de contrôle est effectuée en premier, Netfilter s’attend donc à voir arriver une connexion de données. Lorsque cette dernière arrive, elle est marquée comme RELATED à la connexion de contrôle.
Voilà, avec cela, on a déjà quelques billes pour comprendre le moteur interne de Netfilter. On va maintenant écrire quelques règles pour notre système.
===== Ecriture de règles =====
Dans l’optique d’un firewall capable de se manger plusieurs dizaines de milliers de paquets par secondes, il est nécessaire de détailler notre jeu de règle intelligemment.
==== Premier point : le LOG ====
Un firewall, à la base, ça sert à bloquer des paquets dont on ne veut pas. Et pour vérifier ce qui est bloqué, il faut des LOG.
Netfilter fournit un politique par défaut qui est positionné à ACCEPT. On peut modifier cette politique pour la passer en DROP et n’ouvrir que les flux qui nous intéressent. Problème : la politique par défaut ne LOG pas.
On va donc faire l’inverse, on laisse en ACCEPT et on rajoute une règle à la fin qui renvoi tous les paquets n’ayant pas matchés précédemment vers la table LOGDROP. Il faut, bien sur, créer cette table :
$IPT -N LOGDROP
$IPT -A LOGDROP -j LOG --log-prefix "DROP => " --log-level debug
$IPT -A LOGDROP -j DROP
$IPT –A INPUT –j LOGDROP
$IPT –A OUTPUT –j LOGDROP
Si l’on active cela, on voit de joli ligne dans syslog :
Dec 17 14:43:00 miniarael kernel: DROP => IN=eth0 OUT= MAC=00:0c:29:5e:06:eb:00:18:71:7e:aa:70:08:00 SRC=192.32.189.184 DST=192.32.189.158 LEN=92 TOS=0x00 PREC=0x00 TTL=128 ID=21963 DF PROTO=TCP SPT=2186 DPT=22 WINDOW=63284 RES=0x00 ACK PSH URGP=0
que vous pourrez lire le matin devant un café, ou parcourir avec l’outil de votre choix. Par exemple, avec « fwlogwatch » on a une restitution basique dans une page html.
{{ressources:dossiers:advanced_networking:fwlogwatch.png|}}
généré avec le simple fichier de configuration suivant :
input = /var/log/syslog
parser = n
html = yes
output = /var/www/logs.html
pidfile = /var/run/fwlogwatch.pid
Evidemment, il est possible d’affiner.
==== Deuxième point : les NEW ====
Si vous hébergez un serveur LAMP, un FTP, un serveur MAIL ou quoique ce soit qui se base sur TCP, il est inutile de demander à Netfilter de matcher tous les paquets d’une connexion. Ou plutôt : il est plus intéressant de ne matcher que les établissements de connexions et de considérer une connexion établie comme valide. On va également loguer ces établissements.
$IPT -N LOGACCEPT
$IPT -A LOGACCEPT -j LOG --log-prefix "ACCEPT NEW => " --log-level debug
$IPT -A LOGACCEPT -j ACCEPT
$IPT –A INPUT –i $NETCARD –p tcp –m state --state ESTABLISHED –j ACCEPT
$IPT –A OUTPUT –o $NETCARD –p tcp –m state --state ESTABLISHED –j ACCEPT
Les règles ci-dessus créé la table LOGACCEPT, qui matchera les connexions en NEW, et accepte systématiquement tout ce qui rentre et sort de notre carte réseau, en tcp, et qui fait parti d’une connexion établie.
Ensuite, pour chaque service, il nous suffit d’une seule règle. Par exemple, pour ouvrir l’accès entrant au serveur web :
$IPT –A INPUT –i $NETCARD –p tcp --dport 80 –m state --state NEW –j LOGACCEPT
Pas besoin d’ouvrir la route de sortie, elle est prise en compte par la route générale ESTABLISHED que l’on a écris juste avant.
==== Et sur les protocoles sans états ? ====
A priori, c’est faisable également pour des protocoles basés sur UDP. Ce qui peut être utile pour filtrer des communications RTP par exemple. Par contre, c’est un aspect que je n’ai pas encore creusé…
//[... en cours d'écriture...]//~~DISCUSSION~~