Netfilter : firewalling sous Linux 2.4

Le noyau 2.4 de Linux vient avec un nouveau code de filtrage très puissant. Celui-ci apporte plusieurs nouveautés dont :

  • la notion d'état permettant de savoir si un paquet appartient à une communication en cours ou non;
  • le NAT : la translation d'adresse;
  • la possibilité de modifier des paquets à la volée.

    Les tables

    Netfilter est composé de trois tables :

  • filter : la table de filtrage composée des règles :

    INPUT
    OUTPUT
    FORWARD

  • nat : la table de translation d'adresse avec les règles :

    PREROUTING et OUTPUT permettant de modifier la destination des paquets avant que le paquet soit filtré.
    POSTROUTING pour modifier la source apparente des paquets mangle pour altérer les paquets sortants.

    Pour commencer, nous effaçons les règles de firewall existantes des trois tables.

    	iptables -F
    	iptables -t nat -F
    	iptables -t mangle -F
    
    Par défault, la table filter est utilisée.
    Netfilter permet de définir ses propres règles, effaçons celles présentes sur le système.
    	iptables -X
    
    Le ménage est fait.

    Politique par défaut

    Le plus sûr lorsque l'on conçoit un firewall est de tout interdire par défaut et d'autoriser explicitement.

    	iptables -P INPUT DROP
    	iptables -P OUTPUT DROP
    	iptables -P FORWARD DROP
    
    L'instruction DROP va ignorer le paquet, tandis que REJECT va signaler à la machine à l'origine du paquet que la communication est impossible.

    Déclaration des chaînes utilisateurs

    Afin de présenter un exemple complet, notre firewall a trois interfaces :

    eth0, l'interface externe connectée à Internet
    eth1, l'interface de la DMZ, la zone où se trouve les serveurs accessibles depuis Internet
    eth2, reliant le firewall au réseau interne.

    good va désigner le réseau interne, dmz la DMZ, bad Internet et me le firewall. Définissons les chaînes utilisateurs correspondant aux différentes communications possibles. Par exemple, good-bad désigne les paquets provenant du réseau interne à destination d'Internet.

    	iptables -N good-bad
    	iptables -N good-dmz
    	iptables -N good-me
    	iptables -N dmz-good
    	iptables -N dmz-bad
    	iptables -N dmz-me
    	iptables -N bad-dmz
    	iptables -N bad-me
    

    Redirection vers les chaînes utilisateurs

    Les états

    On accepte les connexions venant de l'interface lo (loopback). Si l'état de la connexion est invalide, INVALID, on ignore le paquet. Si la connexion est établie, ESTABLISHED, ou assimilable à une connexion établie, RELATED (cas, par exemple, du canal de données dans une communication FTP). Toute communication TCP doit commencer par un paquet comportant le flag SYN; si ce n'est pas le cas, on rejette la connexion. Enfin, on trie les connexions entrantes selon les interfaces.

    	iptables -A INPUT -i lo -j ACCEPT
    	iptables -A INPUT -m state --state INVALID -j DROP
    	iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
    	iptables -A INPUT -p TCP --state NEW ! --syn -j DROP
    	iptables -A INPUT -i eth0 -j bad-me
    	iptables -A INPUT -i eth1 -j dmz-me
    	iptables -A INPUT -i eth2 -j good-me
    	iptables -A INPUT -j DROP
    
    Les scanners de ports comme nmap ne fonctionneront pas en mode furtif, seuls les TCP scans et les SYN scans sont possibles.

    Enregistrement : les logs

    Il peut être intéressant d'enregistrer les tentatives de connexion à votre machine, les scans de ports. Mais attention, si tous les ports scannés sont enregistrés, le serveur risque de saturer aussi bien en performance CPU qu'en espace disque. Pour enregistrer les scans de ports TCP furtifs avec un maximum de trois par secondes, il suffit d'ajouter la ligne suivante avant la règle rejetant le paquet.

    	iptables -A INPUT -p TCP --state NEW ! --syn -j DROP -m limit --limit 3/s -j LOG --log-prefix "BAD INPUT"
    
    Chaque ligne sera préfixée par BAD INPUT, le préfixe pouvant faire jusqu'à 29 caractères.

    Généralisation

    En généralisant l'enregistrement des logs aux trois chaînes de filtrage, on obtient :

    	iptables -A INPUT -i lo -j ACCEPT
    	iptables -A INPUT -m state --state INPUT -m limit --limit 3/s -j LOG --log-prefix "INVALID INPUT: "
    	iptables -A INPUT -m state --state INVALID -j DROP
    	iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
    	iptables -A INPUT -p TCP --state NEW ! --syn -j DROP -m limit --limit 3/s -j LOG --log-prefix "INPUT TCP sans SYN: "
    	iptables -A INPUT -p TCP --state NEW ! --syn -j DROP
    	iptables -A INPUT -i eth0 -j bad-me
    	iptables -A INPUT -i eth1 -j dmz-me
    	iptables -A INPUT -i eth2 -j good-me
    	iptables -A INPUT -m limit --limit 3/s -j LOG --log-prefix "BAD INPUT: "
    	iptables -A INPUT -j DROP
    	iptables -A FORWARD -m state --state INPUT -m limit --limit 3/s -j LOG --log-prefix "INVALID FORWARD: "
    	iptables -A FORWARD -m state --state INVALID -j DROP
    	iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
    	iptables -A FORWARD -p TCP --state NEW ! --syn -j DROP -m limit --limit 3/s -j LOG --log-prefix "FORWARD TCP sans SYN: "
    	iptables -A FORWARD -p TCP --state NEW ! --syn -j DROP
    	iptables -A FORWARD -i eth2 -o eth0 -j good-bad
    	iptables -A FORWARD -i eth2 -o eth1 -j good-dmz 
    	iptables -A FORWARD -i eth1 -o eth0 -j dmz-bad
    	iptables -A FORWARD -i eth1 -o eth2 -j dmz-good
    	iptables -A FORWARD -i eth0 -o eth1 -j bad-dmz
    	iptables -A FORWARD -j LOG -m limit --limit 3/s --log-prefix "BAD FORWARD: "
    	iptables -A FORWARD -j DROP
    
    Faisant confiance au firewall, on l'autorise à émettre sans restriction.
    	iptables -A OUTPUT -j ACCEPT
    

    Règles de filtrage

    Pour l'exemple, je vais considérer que la DMZ contient un relais de messagerie en 192.168.1.1, un serveur Web 192.168.1.2 et un cache Internet 192.168.1.3 sur le port 3128. Le serveur de messagerie interne est en 10.0.0.1. Depuis Internet, seuls les serveurs de messagerie et le serveur Web sont joignables.

    	iptables -A bad-dmz -p TCP -d 192.168.1.1 --dport smtp -j ACCEPT
    	iptables -A bad-dmz -p TCP -d 192.168.1.2 --dport http -j ACCEPT
    
    Limitons aussi les connexions du réseau interne à la DMZ mais tolérons le ping.
    	iptables -A good-dmz -p TCP -d 192.168.1.1 --dport smtp -j ACCEPT
    	iptables -A good-dmz -p TCP -d 192.168.1.2 --dport http -j ACCEPT
    	iptables -A good-dmz -p TCP -d 192.168.1.3 --dport squid -j ACCEPT
    	iptables -A good-dmz -p ICMP --icmp-type echo-request -j ACCEPT
    
    La passerelle de messagerie peut communiquer avec le serveur de mail interne.
    	iptables -A dmz-good -p TCP -s 192.168.1.1 -d 10.0.0.1 --dport smtp -j ACCEPT
    
    Maintenant, autorisons la DMZ à communiquer raisonnablement avec Internet. La passerelle de mail peut envoyer des messages, le proxy Internet peut surfer sur des sites sur les ports http (80) et https (443). Toutes les machines de la DMZ peuvent faire des requêtes DNS (domain = 53).
    	iptables -A dmz-bad -p TCP -s 192.168.1.1 --dport smtp -j ACCEPT
    	iptables -A dmz-bad -p TCP -s 192.168.1.3 --dport http -j ACCEPT
    	iptables -A dmz-bad -p TCP -s 192.168.1.3 --dport https -j ACCEPT
    	iptables -A dmz-bad -p TCP --dport domain -j ACCEPT
    	iptables -A dmz-bad -p UDP --dport domain -j ACCEPT
    
    Je vous laisse le soin d'ajouter des règles de logs et de spécifier les règles pour good-bad.

    Règles de NAT

    Masquerading

    Votre réseau interne et la DMZ ne peuvent communiquer directement avec Internet. Les serveurs sur lesquels vous vous connectez ne pourront vous répondre que si la source des paquets est une adresse publique. C'est là que le masquerading intervient, chaque paquet émis vers Internet va utiliser l'adresse de votre interface de sortie.

    	iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
    

    NAT

    Il pourrait y avoir un hic. Vous n'avez qu'une seule adresse IP et deux serveurs doivent être accessibles : le serveur Web et la passerelle Internet. La translation d'adresse va venir à votre rescousse, on va rediriger les ports concernés vers les bonnes destinations.

    	iptables -t nat -A PREROUTING -j DNAT -i eth0 -p TCP --dport smtp --to-destination 192.168.1.1
    	iptables -t nat -A PREROUTING -j DNAT -i eth0 -p TCP --dport http --to-destination 192.168.1.2
    
    Si vous souhaitez que tous les surfeurs passent par votre cache Internet et que tous les mails circulent via la passerelle de messagerie, ce n'est pas un problème. Il n'y aura même pas à configurer les clients. Cela est particulièrement utilisé pour des entreprises ayant un antivirus sur le relais de messagerie.
    	iptables -t nat -A PREROUTING -j DNAT -i eth2 -p TCP --dport http --to-destination 192.168.1.3:3128
    	iptables -t nat -A PREROUTING -j DNAT -i eth2 -p TCP --dport smtp --to-destination 192.168.1.1
    
    N.B. : pensez à configurer Squid en proxy transparent avec en particulier httpd_accel_uses_host_header_on.

    Les modules

    Selon vos besoins, vous allez devoir charger les modules suivants (ou d'autres). Placez ces lignes au début de votre script de firewall (sous Debian, /etc/init.d/iptables ).

    	insmod ip_conntrack_ftp
    	insmod iptable_nat
    	insmod iptable_filter
    
    Par exemple, le module ip_conntrack_ftp vous permettra de faire du FTP passif aussi bien que du FTP actif.

    Configuration du kernel avec sysctl

    Le programme sysctl permet de modifier des paramétrages du noyau. Ces mêmes paramétrages sont aussi accessibles par l'arborescence /proc. Voici les trois paramètres principaux pour un firewall :

  • net.ipv4.ip_forward = 1 active le routage.
  • net.ipv4.conf.all.rp_filter = 1 vérifie la source des paquets, utile en conséquence contre le spoofing.
  • net.ipv4.ip_always_defrag = 1 défragmente automatiquement les paquets, nécessaire pour le bon fonctionnement du firewall, et évite des attaques contre les services accessibles depuis Internet.

    Pour protéger vos serveurs contre les SYN-flood (demande de connexion répétée), il suffit d'utiliser net.ipv4.tcp_syncookies = 1, les services de votre serveur resteront accessibles. Vous trouverez de nombreux paramètres supplémentaires pour optimiser votre Linux dans Documentation/networking/ip-sysctl.cfg, présent dans les sources de votre kernel.

    Conclusion

    Netfilter est un firewall très puissant, capable de rivaliser en performance et en efficacité avec de nombreux firewalls commerciaux. La seule lacune est une absence de fail-over, d'un dispositif de redondance permettant à un second firewall d'assurer la continuité du service en cas de panne du premier. C'est, je crois, l'un des derniers freins à son utilisation en entreprise.

    Christophe Grenier, consultant sécurité chez Global Secure
    Linux Magazine France n°39 - Mai 2002