![]() |
SIG 11 | ARTICLES | DOCUMENTS | MEMBRES | LIENS |
Avec la mutiplication concomittante des apprentis pirates
et du haut débit, des louveteaux et des agneaux en somme, le coupe-feu est
devenu un outil indispensable à toute connexion Internet. Linux se montre
particulièrement bien adapté à cette tâche, et se retrouve souvent à la
base de solutions commerciales qui peuvent coûter, suivant la taille des
sites et le nombre de clients à protéger, plusieurs milliers d'Euros. On
lit parfois qu'un 486 avec 16 Mo de mémoire suffit pour fabriquer un pare-feu
efficace. Encore faut-il trouver un 486, ce qui devient chaque jour plus
difficile, et réussir l'exploit d'y installer une distribution récente avec
le noyau 2.4 ; plus sérieusement, le pare-feu est un excellent moyen à peu
près gratuit de fournir à une machine plus tout jeune mais pas trop vieille
une occasion supplémentaire de servir la collectivité. Un coupe-feu Linux comprend deux éléments associés et indispensables : des fonctions de filtrage intégrées, directement ou par l'intermédiaire de modules, au noyau, dites netfilter ou filtrage de paquets, et un programme chargé de l'accès à ces fonctions, au moyen d'un ensemble de règles que l'on doit écrire. Chaque nouvelle version majeure du noyau Linux a vu une évolution du code du pare-feu, et du programme qui leur est associé : le ipfwadm du noyau 2.0 est devenu ipchains dans le 2.2, et iptables avec le noyau 2.4, aujourd'hui stable et bien établi. Construire un pare-feu implique donc trois étapes distinctes :
|
les modules du noyauUn paquet IP, comme son équivalent postal, comprend deux éléments essentiels : un contenu, alias charge utile, et un ensemble d'informations de service destinées à permettre son bon acheminement, lesquelles sont regroupées dans une en-tête. Le coupe-feu va examiner, avec plus ou moins de profondeur selon les fonctions compilées dans le noyau et les règles d'analyse mises à sa disposition, ces en-têtes, et décider en conséquence du destin du paquet concerné : celui-ci pourra, pour l'essentiel, être accepté, abandonné, auquel cas l'expéditeur ne sera pas informé de son sort, ou rejeté, avec envoi d'un message d'erreur à l'expéditeur.Mais iptables ne se contente pas de son rôle de coupe-feu : le programme assure deux autres fonctions tout à fait disjointes :
|
Une fonction ne sera bien sûr disponible que si
elle a été compilée dans le noyau ; certaines sont rigoureusement indispensables,
et d'autres moins. Et n'oublions pas que, pour le bon fonctionnement de
l'interface RNIS, l'utilisation des sources du noyau SuSE, le 2.4.18 dans
la version 8.0, est obligatoire. On va donc choisir les valeurs suivantes
dans la section des options réseau :
|
![]() |
![]() |
On passe ensuite à la configuration du filtrage
lui-même, ou l'on trouve :
|
Il nous reste une dernière série d'éléments à
compiler : ceux qui nous permettront justement de créer ces tables qui conserveront
les chaines dont le noyau aura besoin pour sa fonction de filtrage. Packet Filtering nous permettra de créer la première d'entres elles, la table filter contenant les règles du pare-feu, laquelle nous est donc indispensable. Deux autres sont optionelles : Full NAT avec en complément les module MASQUERADE et REDIRECT, pour la traduction des adresses IP à l'aide de la table nat et la création d'un proxy transparent, et Packet mangling, qui sert donc à une manipulation assez complexe et un peu expérimentale de certains des éléments de l'en-tête IP, avec la table mangle. Notons pour finir :
|
![]() |
le principe d'iptablesNous avonc donc maintenant notre noyau nouveau, avec des fonctions de filtrage qui ne demandent qu'à être appelées par la commande iptables. Elle permet de manipuler les tables de filtrage en indiquant au noyau quelles sont les règles à suivre. Prenons un exemple simple :/usr/sbin/iptables -t filter -A FORWARD -p tcp -s 192.168.10.0/24 -d 0/0 -jEt décryptons :
La commande une fois lancée, on peut voir comment elle a été prise en charge grâce à iptables -L -n ; -L pour list, et -n pour éviter une longue, infructueuse et coûteuse, puisqu'elle entraîne à chaque fois une connexion, tentative de résolution des noms.. Chain INPUT (policy ACCEPT)Il va sans dire :
|
écrire son scriptOn n'en a pas vraiment conscience, sans doute parce qu'une règle
tient bien souvent sur une seule ligne, mais iptables procède au fond
d'une logique hiérarchique. À un premier niveau, il nous faut choisir la
catégorie de la fonction à mettre en oeuvre, et donc le type de table : les
trois choix possibles sont le filtrage des paquets, leur acheminement avec
NAT, ou la réécriture de leur en-tête avec MANGLE. Commençont
donc par l'essentiel : le fitrage. #Les variables pour les interfacesRappellons que nous nous connectons à Internet avec une carte RNIS, donc avec l'interface ippp0, et qu'elle est installée sur bravek6, alias 192.168.10.21, lequel fait office de coupe-feu. On charge ensuite, par précaution, les différents modules du noyau : #Si on veut utiliser la cible REJECTlesquels dépendent, bien sûr, des choix faits lors de la compilation. Enfin, il est utile, avant d'écrire les chaînes, d'enregistrer les commandes suivantes : #Tout remettre a zeroA chaque lancement du script, toutes les règles mémorisées par le noyau sont effacées : on peut ainsi y apporter des modifications sans craindre d'effets secondaires. Contrairement à l'usage courant, iptables, par défaut, accepte tout ce qui n'est pas spécifiquement interdit. C'est pourquoi l'on commence par inverser ce comportement de la façon suivante : iptables -P INPUT DROPLe -P majuscule désigne la politique à adopter, et ne doit pas être confondu avec le -p minuscule qui spécifie un protocole. Il nous faudra ensuite réglementer le trafic entre les diverses interfaces, en ajoutant avec iptables -A des règles aux trois chaînes définies par défaut. D'abord, ne surtout pas oublier l'hôte local ; par définition, seules les chaînes INPUT et OUTPUT ont ici un sens : #Une regle pour localhostPuis, régler le problème de la machine coupe-feu : #Une regle pour bravek6On accepte donc d'une part les paquets destinés à l'interface Internet, et de l'autre les paquets des machines du réseau, soit en entrée avec -s pour source et INPUT, soit en sortie avec -d pour destination et OUTPUT. Enfin, il nous reste à autoriser le transit des paquets du réseau local vers Internet : iptables -A FORWARD -s $lan -d 0/0 -j ACCEPTOn offre donc aux données venant du réseau local le libre passage vers une adresse IP par définition inconnue, puisque dynamique. Reste à régler la question de fond : comment permettre le trafic aller-retour normal de notre réseau vers Internet, tout en repoussant les inconnus qui s'intéressent avec une insistance suspecte à nos humbles machines ? C'est là qu'entre en scène la grande nouveauté du noyau 2.4, l'analyse de l'état des connexions. Celui-ci peut connaître quatre valeurs distinctes :
#The state machine iptables -N ok iptables -A ok -i $isdn -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A ok -i ! $isdn -m state --state NEW -j ACCEPT iptables -A ok -i $isdn -m state --state NEW,INVALID -j DROPOn commence donc par créer une nouvelle chaîne appelée ok. On accepte tout le trafic venant de l'extérieur, donc par l'interface ippp0, à condition qu'il soit ESTABLISHED ou bien RELATED. On accepte également que des connexions soit initialisées par toutes les interfaces, sauf ippp0. Et enfin, on refuse tout le reste. state, contrairement aux paramètres tels le protocole ou l'interface, doit être précisé de manière explicite. La syntaxe est donc d'abord -m state, puis --state et le ou les états associés. Notons également le -i ! $isdn : usage classique du point d'exclamation comme élément de négation, séparé par un espace de ce à quoi il s'applique, et rôle dont on aura saisi l'importance : en son absence, on autorise ce que l'on cherche précisément à interdire. Enfin, pour rendre active cette nouvelle règle, il nous reste à la relier aux règles par défaut de la façon suivante : iptables -A INPUT -j ok iptables -A OUTPUT -j ok iptables -A FORWARD -j okVoilà, nous sommes désormais dotés d'un filtre de paquets. Avant de tester son efficacité, il faudra y ajouter quelques fonctions annexes. LOG, MASQUERADE et REDIRECTACCEPT, DROP ou REJECT sont des cibles terminales : l'analyse prend
fin quand elle sont atteintes. Tel n'est pas le cas de LOG qui, on l'aura
compris, va inscrire, par défaut dans /var/log/messages, des informations
que l'on peut paramètrer précisément. Pour notre script, on utilisera dans
un premier temps les règles suivantes : #Creer un log raisonnableLe trafic généré dans chacune des trois règles principales est donc enregistré dans /var/log/messages. Le niveau de sévérité est précisé avec --log-level info : celui-ci est très bas, et servira essentiellement à vérifier que tout va bien. Il faudra ensuite le remplacer par notice ou bien warning. Pour ne pas être malgré tout accablé d'informations, on va en limiter la quantité avec -m limit --limit 4/minute --limit-burst 1 : on permet ainsi l'enregistrement d'une information toutes les quinze secondes, et d'une seule. Voilà qui termine la partie filtrage du script. Passons maintenant à la seconde table, NAT. Elle va nous
servir à réaliser deux opérations du plus haut intérêt. Il est d'abord
indispensable, même avec notre proxy, d'utiliser la cible MASQUERADE, qui
permet de remplacer l'adresse IP des machines de notre réseau par celle qui
a été attribuée lors de la connexion à Internet ; cette cible permet de
réaliser cette opération avec une adresse IP inconnue à l'avance, et s'applique
donc exclusivement à l'utilisation d'interfaces commutées, comme notre connexion
RNIS. Elle permettra aux clients qui ne passent pas par le proxy, FTP et
IRC en l'occurrence, d'accèder à Internet, et se présente très simplement
comme ceci : iptables -t nat -A POSTROUTING -o $isdn -j MASQUERADEIl est désormais obligatoire avec -t nat de spécifier la table utilisée ; on va donc ajouter à la chaîne POSTROUTING, chaîne, avec sa collègue PREROUTING, propre à la table nat, une règle MASQUERADE appliquée aux paquets qui quittent le coupe-feu par l'interface ippp0. Il y a plus fort : la cible REDIRECT redirige, comme son nom l'indique, des paquets entrant sur le coupe-feu et à destination d'un certain port vers un ou plusieurs autres ports qu'il nous faudra préciser. En d'autre termes, la règle : iptables -t nat -A PREROUTING -i $eth_if -p tcp --dport 80 -j REDIRECT --to-ports 8080va intercepter les requêtes que les navigateurs de nos clients envoient aux serveurs Web, et les diriger vers le port 8080 du coupe-feu : on sait que c'est là que le proxy wwwoffle attend les connexions. On réalise ainsi l'opération dite transparent proxying, grâce à laquelle il n'est plus nécessaire de paramétrer individuellement chaque client, comme on l'a fait plus haut : l'adresse de la passerelle, inscrite dans leur configuration réseau, suffit désormais au navigateur pour atteindre automatiquement le proxy. Et en plus, ça marche. Attention toutefois à la syntaxe, --to-ports est bien au pluriel. Il faut enfin dire quelques mots de la table mangle, qui
permet de trafiquer les valeurs d'en-tête des paquets ip, comme par exemple
le TTL ou time to live, c'est à dire le nombre de routeurs qu'un
paquet est autorisé à traverser avant d'être abandonné. Pour parapharser
l'excellent Oskar Andreasson, une règle de ce type : iptables -t mangle -A PREROUTING -i $eth_if -j TTL --ttl-set 64permet de donner une même valeur de TTL à tous les paquets quittant le coupe-feu. Or des valeurs différentes pour des données sensées provenir d'une seule et même machine trahissent l'existence en amont d'un réseau dont les paquets on déjà un peu vécu. Les fournisseurs d'accès fouineurs peuvent ainsi repérer la présence de réseaux là où leur contrat autorise la connexion d'une machine unique. Malin. Et quand même trop particulier pour qu'il soit nécessaire d'en dire plus. Voilà, c'est terminé. Enfin, presque. Généralement, on sait que le noyau Linux fournit nombre d'informations sur son fonctionnement par l'intermédiaire du répertoire /proc, un pseudo système de fichiers grâce auquel on peut prendre connaissance de celles-ci avec une simple commande, comme cat. On sait moins que, à l'inverse, on peut modifier les paramètres du noyau de manière similaire ; on trouve quelques textes à ce sujet dans la documentation des sources du noyau, proc.txt, une introduction globale, dans le répertoire filesystems, et, dans le répertoire networking, ip-sysctl.txt, qui explique quelles valeurs donner à quels paramètres. Or il est indispensable d'activer une fonction que le noyau n'exécute pas par défaut, la transmission des paquets entre les deux interfaces réseau qui caractérisent notre coupe-feu. On rajoute donc la ligne suivante à la fin du script : /bin/echo "2" > /proc/sys/net/ipv4/ip_forwardecho 2 et pas 1 permet d'inscrire un message dans les logs. On peut activer d'autres paramètres de la même manière, l'allocation d'adresse dynamique avec /proc/sys/net/ipv4/ip_dynaddr, ou les syncookies. l'épreuve du feuVoilà, maintenant on peut y aller. On lance le script depuis une console, et on peut ensuite afficher le résultat avec iptables -L -nv, le -v rendant comme toujours la commande plus prolixe :Chain INPUT (policy DROP 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 2 108 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0 0 0 ACCEPT all -- * * 192.168.10.0/24 0.0.0.0/0 0 0 LOG all -- * * 192.168.10.0/24 0.0.0.0/0 limit: avg 4/min burst 1 LOG flags 0 level 6 0 0 ok all -- * * 0.0.0.0/0 0.0.0.0/0 Chain FORWARD (policy DROP 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 0 0 ACCEPT all -- * * 192.168.10.0/24 0.0.0.0/0 0 0 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 limit: avg 4/min burst 1 LOG flags 0 level 6 0 0 ok all -- * * 0.0.0.0/0 0.0.0.0/0 Chain OUTPUT (policy DROP 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 2 108 ACCEPT all -- * lo 0.0.0.0/0 0.0.0.0/0 0 0 ACCEPT all -- * ippp0 0.0.0.0/0 0.0.0.0/0 0 0 ACCEPT all -- * * 0.0.0.0/0 192.168.10.0/24 0 0 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 limit: avg 4/min burst 1 LOG flags 0 level 6 0 0 ok all -- * * 0.0.0.0/0 0.0.0.0/0 Chain ok (3 references) pkts bytes target prot opt in out source destination 0 0 ACCEPT all -- ippp0 * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED 0 0 ACCEPT all -- !ippp0 * 0.0.0.0/0 0.0.0.0/0 state NEW 0 0 DROP all -- ippp0 * 0.0.0.0/0 0.0.0.0/0 state INVALID,NEWLa présentation en tableau est fort claire : d'abord les chaînes par défaut INPUT, FORWARD et OUTPUT, puis notre chaîne ok. Nombre et taille des paquets, cibles et protocoles, interface et adresses d'origine et de destination : tout y est répertorié. Mais comment être sûr que ça marche ? On peut déjà faire quelque chose de très simple et de gratuit : interdire l'accès au coupe-feu depuis le réseau local. Il suffit de changer une cible dans le script : iptables -A INPUT -s $lan -j REJECTpuis de le relancer. Un simple ping vers un des postes du réseau fournit une indication, les réponses étant bloquées par le filtre, et la cible REJECT entraînant l'envoi d'une réponse : "host unreachable". |
Mais on obtiendra un diagnostic plus complet avec un outil servant
à ça, comme le célèbre et redouté nmap. On trouvera sur le site de son auteur toute la documentation nécessaire à la compréhension de son outil, ainsi qu'un tas d'articles d'une lecture fascinante. Cet utilitaire dispose d'une interface graphique,
xnmap alias nmpafe, qui nous permet d'entrer l'adresse ip de la machine
cible, et dans laquelle on cochera les options don't
resolve, fast scan et don't ping, qui permettront à cette vaine tentative
d'ouvrir une porte hermétiquement close de prendre un peu moins de temps. Ensuite, il ne restera qu'à essayer les
diverses méthodes proposées, connect(), SYN et FIN stealth en particulier. Et dans la mesure où le proxy participe
à la sécurité de l'hôte, on n'oubliera pas, pour un résultat plus probant,
de désactiver au préalable wwwoffle. Il faudra quand même un temps parfois considérable avant que nmap ne s'avoue vaincu. Evidemment, la règle rejetant sans nuance tous les paquets envoyés depuis notre réseau local, le test est trop facile, et permet juste de s'assurer que le fitrage marche : mais pour éprouver son efficacité, il faudrait travailler en conditions réelles, avec des données qui arrivent par l'interface RNIS. Pour cela, la meilleure solution est de disposer d'un complice équipé de RNIS, câble ou ADSL : une fois connecté à Internet, on lui communique par téléphone l'adresse ip dont on dispose, et on peut ensuite suivre en temps réel ses tentatives d'effraction. |
![]() |
Á défaut, on peut toujours simuler le fonctionnement du filtre à
partir de notre réseau local. Pour se retrouver dans une situation crédible, il faudra que le coupe-feu permette la réception des pages web demandées par notre navigateur, tout en bloquant les autres connexions : on aura besoin desrègles suivantes :#iptables -A INPUT -s $lan -j ACCEPT iptables -A OUTPUT -d $lan -j ACCEPT iptables -N ok iptables -A ok -i $eth_if -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A ok -i $eth_if -m state --state NEW,INVALID -m limit --limit 4/minute --limit-burst 1\ -j LOG --log-prefix "Nmap attaque ! " --log-level debug iptables -A ok -i $eth_if -m state --state NEW,INVALID -j DROPOn remarque :
Chain INPUT (policy DROP 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 2 100 ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0 72218 68M ok all -- * * 0.0.0.0/0 0.0.0.0/0 Chain FORWARD (policy DROP 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 136 6456 ok all -- * * 0.0.0.0/0 0.0.0.0/0 Chain OUTPUT (policy DROP 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 2 100 ACCEPT all -- * lo 0.0.0.0/0 0.0.0.0/0 16 1040 ACCEPT all -- * ippp0 0.0.0.0/0 0.0.0.0/0 17221 958K ACCEPT all -- * * 0.0.0.0/0 192.168.200.0/24 0 0 ok all -- * * 0.0.0.0/0 0.0.0.0/0 Chain ok (3 references) pkts bytes target prot opt in out source destination 54968 67M ACCEPT all -- eth0 * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED 219 12553 LOG all -- eth0 * 0.0.0.0/0 0.0.0.0/0 state INVALID,NEW limit: avg 4/min burst 1 LOG flags 0 level 7 prefix `Nmap attaque ! ' 17386 941K DROP all -- eth0 * 0.0.0.0/0 0.0.0.0/0 state INVALID,NEWOn aura remarqué que les paquets qui transitent par la chaîne ok se décomposent en 54968 paquets en l'état RELATED ou ESTABLISHED, réponses à nos demandes comptabilisées dans la chaîne OUTPUT, et 17386 paquets NEW ou INVALID, que le pare-feu a rejetés. En bref, ça marche. Notons pour finir que certains sites Internet comme AuditMyPC proposent ce type d'analyses, lesquelles semblent
cependant essentiellement consacrées à la recherche de ports NetBios sur
un poste Windows, ce qui ne nous aide guère. iptables -A ok -i $isdn -m state --state NEW,INVALID -j LOG\ --log-prefix "INTRUS ! " --log-level debugavant la règle finale de la chaîne ok, pour collecter dans /var/log/messages les tentatives d'intrusions qui ne manqueront pas de se produire, avec toutes les saletés comme OpaServe qui circulent de nos jours sur Internet. ; un grep INTRUS /var/log/messages permettra d'en dresser la liste. En voici un petit extrait : INTRUS ! IN=ippp0 OUT= MAC= SRC=33.30.1.153 DST=212.43.198.213 LEN=78 TOS=0x00 PREC=0x00 TTL=108 ID=39430 PROTO=UDP SPT=1650 DPT=137 LEN=58 INTRUS ! IN=ippp0 OUT= MAC= SRC=208.35.253.134 DST=212.43.198.213 LEN=48 TOS=0x00 PREC=0x00 TTL=113 ID=1633 DF PROTO=TCP SPT=4774 DPT=1433 WINDOW=16384 RES=0x00 SYN URGP=0Avec le proxy, le coupe-feu, et surtout la certitude que ce qui importe vraiment, c'est d'avoir une serrure un peu plus grosse que celle du voisin, on peut dormir tranquille. Denis Berger, 15 novembre 2002 |
SIG 11 | DOCUMENTS | LA CONFIGURATION | LES PARAMÈTRES | LE PROXY | L'INDEX |