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

Les tables

Netfilter est composé de trois tables

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éfaut, 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:

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ée dans une communication FTP). Toute communication TCP doit commencer par un paquet comportant le flag SYN et uniquement celui-ci, 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   -m state --state NEW -p TCP --tcp-flags ! ALL 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 aussi bien 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 --state NEW -p TCP --tcp-flags ! ALL SYN -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 INVALID -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 --tcp-flags ! ALL SYN -m state --state NEW -m limit --limit 3/s -j LOG --log-prefix "INPUT TCP sans SYN: "
    iptables -A INPUT   -p TCP --tcp-flags ! ALL SYN -m state --state NEW -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 INVALID -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 --tcp-flags ! ALL SYN -m state --state NEW -m limit --limit 3/s -j LOG --log-prefix "FORWARD TCP sans SYN: "
    iptables -A FORWARD -p TCP --tcp-flags ! ALL SYN -m state --state NEW -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 -m limit --limit 3/s -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 accessible: le serveur web et la passerelle Internet. La translation d'adresse va venir à vous 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 tout 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
NB: Pensez à configurer Squid en proxy transparent avec en particulier httpd_accel_uses_host_header on

Les modules

Selon vos besoins, vous allez avoir besoin de charger les modules suivants (ou d'autres), placez ces lignes au début de votre script de firewall.

  modprobe ip_conntrack_ftp
  modprobe iptable_nat
  modprobe 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

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émentaire pour optimiser votre Linux dans Documentation/networking/ip-sysctl.cfg.

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
grenier@cgsecurity.org http://www.cgsecurity.org

La première version de cet article est parue dans Linux Mag France de mai 2002.
Dernière version: 23 décembre 2002