13. Déboguer vos scripts▲
Un des aspects les plus importants et sous-estimés dans l'écriture de vos propres tables de règles est de savoir comment les déboguer, et retrouver les erreurs. Ce chapitre vous montrera quelques étapes de base dans le débogage de vos scripts, ainsi que certaines choses un peu plus élaborées pour éviter les problèmes de connexion à votre pare-feu lorsque vous avez accidentellement exécuté une règle incorrecte.
Tout ce que nous écrivons ici est fondé sur la supposition que les tables de règles de vos scripts sont écrites en shell Bash, mais elles peuvent facilement s'appliquer à d'autres environnements. Les tables de règles qui ont été sauvegardées avec iptables-save sont une autre partie du code. Les fichiers iptables-save sont simples et ne contiennent pas de code écrit à la main qui créent des règles spécifiques, ils sont aussi plus simples à déboguer.
13-1. Déboguer, une nécessité▲
Déboguer est plus ou moins une nécessité avec iptables et Netfilter et les pare-feux en général. Le problème avec 99 % des pare-feux est qu'à la fin c'est un comportement humain qui décide des stratégies et de la façon dont les tables de règles sont créées, et je peux vous promettre qu'il est facile de faire une erreur dans l'écriture de vos tables. Parfois, ces erreurs sont très difficiles à voir à l'œil nu, ou découvrir les trous laissés par le pare-feu. Les trous que nous ne connaissons pas ou qui ne sont pas intentionnels peuvent causer des dégâts dans nos réseaux, et créer des entrées faciles pour les attaquants. La plupart de ces trous peuvent être découverts aisément avec quelques bons outils.
En dehors de ça, nous pouvons laisser des bogues dans nos scripts, qui peuvent nous interdire d'ouvrir une session sur le pare-feu. Ceci peut également être résolu avec un peu de dextérité avant d'exécuter les scripts. Utiliser la pleine puissance du langage de script et de l'environnement système peut se révéler incroyablement utile, avec toutes les expériences que les administrateurs Unix ont déjà consignées, et c'est tout ce que nous faisons lors du débogage de nos scripts.
13-2. Débogage en Bash▲
Il y a certaines choses qui peuvent être faites avec Bash pour nous aider à déboguer nos scripts contenant les tables de règles. Un des premiers problèmes quand on trouve un bogue est de savoir dans quelle ligne il se trouve. Ceci peut être résolu de deux façons différentes, soit en utilisant le fanion -x du Bash, soit en tapant simplement echo pour trouver l'endroit où le problème apparaît. Idéalement, vous pouvez avec echo, ajouter quelque chose comme les états suivants à intervalles réguliers dans le code :
...
echo "Debugging message 1."
...
echo "Debugging message 2."
...
Dans mon cas, j'utilise des messages insignifiants, tant qu'ils ont quelque chose d'unique je peux retrouver le message d'erreur par un simple grep ou une recherche dans le script. Maintenant, si le message d'erreur apparaît après le « Debugging message 1 », mais avant le « Debugging message 2 », nous savons alors que la ligne de code erronée est quelque part entre les deux messages. Comme vous pouvez le comprendre, Bash n'est pas réellement mauvais, et a au moins l'idée de continuer à exécuter les commandes même s'il y a une erreur dans une commande précédente. Dans Netfilter, ceci peut provoquer certains problèmes très intéressants. L'idée d'utiliser les états echo pour trouver les erreurs est très simple, mais vous pouvez en même temps cerner l'ensemble du problème à une seule ligne de code.
La seconde possibilité pour trouver le problème est d'utiliser la variable Bash -x, comme dit précédemment. Ceci peut bien sûr être gênant, particulièrement si votre script est volumineux, et que le tampon de votre console n'est pas assez important. Ce que la variable -x indique est très simple, elle précise au script d'envoyer un écho pour chaque ligne de code de votre script vers la sortie standard du shell (généralement la console). Pour cela, changez la ligne de début du script qui doit se présenter comme :
#!/bin/bash
en la ligne suivante :
#!/bin/bash -x
Comme vous pouvez le voir, ceci ne modifie peut-être que deux lignes, dans le total important de données en sortie. Le code vous indique chaque ligne de commande qui est exécutée, et avec quelles valeurs de variables, etc. Chaque ligne exécutée est affichée sur votre écran. Une chose intéressante est que les lignes de sortie Bash sont préfixées par un signe +. Ceci rend plus facile de discerner les messages d'erreur et d'avertissement provenant du script.
L'option -x est aussi très intéressante pour déboguer les problèmes communs qu'on rencontre dans les tables de règles plus complexes. Le premier est de trouver ce qui se passe avec ce que vous pensiez être une simple boucle. Voyons l'exemple ci-dessous.
#!/bin/bash
iptables="/sbin/iptables"
$iptables -N output_int_iface
cat /etc/configs/machines | while read host; do
$iptables -N output-$host
$iptables -A output_int_iface -p tcp -d $host -j output-$host
cat /etc/configs/${host}/ports | while read row2; do
$iptables -A output-$host -p tcp --dport $row2 -d $host -j ACCEPT
done
done
Ces règles peuvent sembler simples, mais le problème existe toujours. Nous obtenons les messages d'erreur suivants que nous savons provenir du code ci-dessus en utilisant un simple écho comme méthode de débogage.
work3:~# ./test.sh
Bad argument `output-'
Try `iptables -h' or 'iptables --help' for more information.
cat: /etc/configs//ports: No such file or directory
Activons l'option -x du Bash et regardons la sortie. Celle-ci est indiquée ci-dessous, comme vous pouvez le voir il y a quelque chose de très mystérieux. Il existe un couple de commandes où les variables $host et $row2 ne sont remplacées par rien. En regardant de plus près, nous voyons que c'est seulement la dernière itération du code qui cause problème. Soit nous avons fait une erreur de programmation, soit il y a quelque chose d'étrange avec la donnée. Dans ce cas, c'est une simple erreur avec la donnée, qui contient un saut de ligne en trop à la fin du fichier. Ceci crée une boucle d'itération. En supprimant simplement ce saut de ligne du fichier, le problème est résolu. Ceci peut ne pas être une solution très élégante, mais pour un usage privé c'est suffisant. D'un autre côté nous avons ajouté du code qui indique qu'il y a certaines données dans les variables $host et $row2.
work3:~# ./test.sh
+ iptables=/sbin/iptables
+ /sbin/iptables -N output_int_iface
+ cat /etc/configs/machines
+ read host
+ /sbin/iptables -N output-sto-as-101
+ /sbin/iptables -A output_int_iface -p tcp -d sto-as-101 -j output-sto-as-101
+ cat /etc/configs/sto-as-101/ports
+ read row2
+ /sbin/iptables -A output-sto-as-101 -p tcp --dport 21 -d sto-as-101 -j ACCEPT
+ read row2
+ /sbin/iptables -A output-sto-as-101 -p tcp --dport 22 -d sto-as-101 -j ACCEPT
+ read row2
+ /sbin/iptables -A output-sto-as-101 -p tcp --dport 23 -d sto-as-101 -j ACCEPT
+ read row2
+ read host
+ /sbin/iptables -N output-sto-as-102
+ /sbin/iptables -A output_int_iface -p tcp -d sto-as-102 -j output-sto-as-102
+ cat /etc/configs/sto-as-102/ports
+ read row2
+ /sbin/iptables -A output-sto-as-102 -p tcp --dport 21 -d sto-as-102 -j ACCEPT
+ read row2
+ /sbin/iptables -A output-sto-as-102 -p tcp --dport 22 -d sto-as-102 -j ACCEPT
+ read row2
+ /sbin/iptables -A output-sto-as-102 -p tcp --dport 23 -d sto-as-102 -j ACCEPT
+ read row2
+ read host
+ /sbin/iptables -N output-sto-as-103
+ /sbin/iptables -A output_int_iface -p tcp -d sto-as-103 -j output-sto-as-103
+ cat /etc/configs/sto-as-103/ports
+ read row2
+ /sbin/iptables -A output-sto-as-103 -p tcp --dport 21 -d sto-as-103 -j ACCEPT
+ read row2
+ /sbin/iptables -A output-sto-as-103 -p tcp --dport 22 -d sto-as-103 -j ACCEPT
+ read row2
+ /sbin/iptables -A output-sto-as-103 -p tcp --dport 23 -d sto-as-103 -j ACCEPT
+ read row2
+ read host
+ /sbin/iptables -N output-
+ /sbin/iptables -A output_int_iface -p tcp -d -j output-
Bad argument `output-'
Try `iptables -h' or 'iptables --help' for more information.
+ cat /etc/configs//ports
cat: /etc/configs//ports: No such file or directory
+ read row2
+ read host
Le troisième et dernier problème peut être partiellement résolu avec l'aide de l'option -x si vous exécutez le script du pare-feu par SSH, la console se suspend au milieu de l'exécution du script, elle ne vous rend pas la main, ni vous ne pouvez vous connecter par SSH à nouveau. Dans 99 % des cas, ceci indique qu'il y a certains problèmes dans le script avec un couple de règles. En activant l'option -x, vous verrez exactement à quelle ligne ça bloque. Il y a une ou deux circonstances où ce n'est pas vrai, malheureusement. Par exemple, si le script initialise une règle qui bloque le trafic entrant, mais que le serveur ssh/telnet envoie un écho en premier comme trafic sortant, netfilter enregistrera la connexion, et donc permettra le trafic entrant de toutes destinations si vous avez une règle qui maintient les états de connexion.
Comme vous pouvez le voir, il peut être tout à fait complexe de déboguer vos tables de règles. Cependant, ce n'est pas impossible. Vous pouvez aussi avoir noté, si vous travaillez à distance sur votre pare-feu via SSH par exemple, que le pare-feu peut se figer quand vous chargez des règles incorrectes. Une des choses supplémentaires que vous pouvez faire dans ces circonstances est de faire une sauvegarde par jour. Cron est un excellent moyen pour faire ça. Exemple, vous travaillez sur un pare-feu en étant à 50 km de distance, vous ajoutez et supprimez des règles. Le pare-feu se bloque, et vous ne pouvez plus rien faire. Le seul moyen est de vous déplacer à l'endroit où se trouve physiquement le pare-feu et régler le problème, à moins que vous n'ayez pris des précautions de ce genre !
13-3. Outils système pour le débogage▲
Une des meilleures précautions que vous pouvez prendre contre un pare-feu qui se bloque, est tout simplement d'utiliser le programme cron pour ajouter un script qui se lance toutes les 5 minutes ou qui relance le pare-feu, et ensuite supprimer la ligne du cron une fois que vous êtes sûr que l'installation fonctionne bien. La ligne du cron peut ressembler à l'exemple ci-dessous, elle sera installée par la commande crontab -e.
*/5 * * * * /etc/init.d/rc.flush-iptables.sh stop
Soyez absolument sûr, que la ligne fonctionne et fait exactement ce que vous en attendez sinon ça peut bloquer le serveur.
Un autre outil qui est constamment utilisé pour déboguer les scripts est la fonction syslog. C'est ce qui journalise tous les messages générés par beaucoup de programmes différents. En fait, la plupart des gros programmes supportent la journalisation par syslog, y compris le noyau. Tous les messages envoyés à syslog ont deux variables de base dont il est très important de se souvenir, la fonction et le niveau/priorité de la journalisation.
La fonction indique au serveur syslog de quel endroit provient l'entrée du journal, et quoi journaliser. Il existe plusieurs fonctions spécifiques, mais celle qui nous intéresse est la fonction Kern, ou fonction kernel. Tous les messages générés par netfilter sont envoyés par cette fonction.
Le niveau de journalisation indique à syslog quelle priorité ont les messages. Il existe plusieurs priorités, voir ci-dessous :
- debug
- info
- notice
- warning
- err
- crit
- alert
- emerg
En fonction de ces priorités, nous pouvons les envoyer vers différents fichiers journaux en utilisant le syslog.conf. Par exemple, pour envoyer tous les messages provenant de la fonction kern avec une priorité warning vers un fichier appelé /var/log/kernwarnings, nous ferons comme ci-dessous. La ligne sera placée dans /etc/syslog.conf.
kern.warning /var/log/kernwarnings
Comme vous pouvez le voir, c'est tout à fait simple. Maintenant, vous trouverez vos journaux de netfilter dans le fichier /var/log/kernwarnings (après redémarrage, ou en faisant un HUP sur le serveur syslog). Bien sûr, ceci dépend du niveau de journalisation que vous avez mis dans vos règles de netfilter. Le niveau de journalisation peut être placé avec l'option --log-level.
Ces journaux vous fourniront l'information que vous désirez via les règles de journalisation spécifiques dans la table de règles. Avec elles, vous pouvez savoir s'il existe un problème quelque part. Par exemple, vous pouvez placer vos règles de journalisation à la fin des chaînes pour voir s'il y a des paquets qui ont transité jusqu'à la frontière de vos chaînes. Une entrée de journal peut ressembler à l'exemple ci-dessous, et inclure les informations suivantes.
Oct 23 17:09:34 localhost kernel: IPT INPUT packet died: IN=eth1 OUT=
MAC=08:00:09:cd:f2:27:00:20:1a:11:3d:73:08:00 SRC=200.81.8.14 DST=217.215.68.146
LEN=78 TOS=0x00 PREC=0x00 TTL=110 ID=12818 PROTO=UDP SPT=1027 DPT=137 LEN=58
Comme vous pouvez le comprendre, syslog peut réellement vous aider à déboguer vos tables de règles. Regarder ces journaux peut vous aider à comprendre pourquoi les ports que vous voulez ouvrir ne fonctionnent pas.
13-4. Débogage d'iptables▲
iptables peut être parfois difficile à déboguer, car les messages d'erreur provenant d'iptables lui-même ne sont pas toujours conviviaux. Pour cette raison, ce peut être une bonne idée de regarder les messages d'erreur les plus fréquents venant d'iptables, et pourquoi vous les obtenez.
Un des premiers messages d'erreur à regarder est le « Unknown arg ». Il peut apparaître pour différentes raisons. Exemple ci-dessous :
work3:~# iptables -A INPUT --dport 67 -j ACCEPT
iptables v1.2.9: Unknown arg `--dport'
Try `iptables -h' or 'iptables --help' for more information.
Cette erreur est simple à corriger, car nous n'avons utilisé qu'un seul argument. Normalement, nous pouvons avoir utilisé une très longue commande et obtenir ce message. Le problème dans le scénario ci-dessus est que nous avons oublié d'utiliser la correspondance --protocol, et à cause de ça, le module --dport n'est pas disponible. En ajoutant la correspondance --protocol nous résoudrons le problème. Soyez absolument certains que vous n'oubliez aucune précondition spéciale nécessaire pour utiliser une correspondance spécifique.
Une autre erreur très commune est que vous oubliez un tiret (-) quelque part dans la ligne de commande, comme en dessous. La solution est de rajouter simplement ce tiret, et la commande fonctionnera.
work3:~# iptables -A INPUT --protocol tcp -dport 67 -j ACCEPT
Bad argument `67'
Try `iptables -h' or 'iptables --help' for more information.
Et enfin, le simple oubli, ce qui est le plus courant. Voir ci-dessous. Le message d'erreur est exactement le même que lorsque vous oubliez d'ajouter une correspondance prérequise à votre règle, aussi il est nécessaire de les regarder de près.
work3:~# iptables -A INPUT --protocol tcp --destination-ports 67 -j ACCEPT
iptables v1.2.9: Unknown arg `--destination-ports'
Try `iptables -h' or 'iptables --help' for more information.
Il existe aussi une cause probable quand vous obtenez l'erreur « Unknown arg ». Si l'argument est écrit correctement, et qu'il n'y a pas d'erreur dans les prérequis, il est possible que la cible/correspondance/table ne soit pas compilée dans le noyau. Par exemple, nous oublions de compiler la table filter dans le noyau, ce qui ressemblera à ça.
work3:~# iptables -A INPUT -j ACCEPT
iptables v1.2.9: can't initialize iptables table `filter': Table does not exist
(do you need to insmod?)
Perhaps iptables or your kernel needs to be upgraded.
Normalement, iptables est capable de charger automatiquement un module spécifique qui n'est pas déjà dans le noyau, c'est généralement le signe que soit vous n'avez pas fait un depmod correct après avoir redémarré avec le nouveau noyau, soit vous avez simplement oublié le(s) module(s). Si le module problématique est une correspondance, le message d'erreur sera davantage crypté et difficile à interpréter. Par exemple, regardons ce message d'erreur :
work3:~# iptables -A INPUT -m state
--state ESTABLISHED -j ACCEPT
iptables: No chain/target/match by that name
Dans ce cas, nous avons oublié de compiler le module, et comme vous avez vu, le message d'erreur n'est pas très facile à interpréter. Mais il fait allusion à ce qui est faux. Finalement, nous avons la même erreur de nouveau, mais cette fois, la cible est omise. Comme vous l'avez compris en regardant le message, il est plus compliqué, car c'est exactement le même pour les deux erreurs (correspondance et/ou cible oubliée).
work3:~# iptables -A INPUT -m state
--state ESTABLISHED -j REJECT
iptables: No chain/target/match by that name
Le moyen le plus simple de savoir si vous avez oublié de faire un depmod, ou si le module est manquant, est de regarder dans le répertoire où se trouvent les modules. C'est le répertoire /lib/modules/2.6.4/kernel/net/ipv4/netfilter. Tous les fichiers ipt_* écrits en majuscules sont des cibles, et tous les autres en minuscules sont des correspondances. Par exemple, ipt_REJECT.ko est une cible, tandis que ipt_state.ko est une correspondance.
Dans les noyaux 2.4 et plus anciens, l'extension de fichier pour tous les modules du noyau était en .o, qui a été changée pour les fichiers des noyaux 2.6 en .ko.
Une autre façon d'obtenir de l'aide d'iptables lui-même est de commenter une chaîne entière dans votre script pour savoir si ça résout le problème. En supprimant la chaîne et mettant à la place une stratégie par défaut ACCEPT, et ensuite en testant, si ça marche mieux, c'est que c'était cette chaîne qui causait les problèmes. Si rien ne s'améliore, alors c'est une autre chaîne, et vous devez chercher le problème ailleurs.
13-5. Autres outils de débogage▲
Il existe bien sûr d'autres outils qui peuvent être extrêmement utiles pour déboguer vos scripts. Cette section présente brièvement les plus communs qui sont utilisés pour savoir comment apparaît votre pare-feu dans les deux sens (interne et externe). Les outils que j'ai choisis sont ici Nmap et Nessus.
13-5-1. Nmap▲
Nmap est un excellent outil vu dans une perspective de pare-feu, pour trouver quels ports sont ouverts et un niveau d'information de plus bas niveau. Il possède un support d'empreinte système, plusieurs méthodes différentes de balayage de port, le support de IPv6 et IPv4 et le scan de réseau.
La forme de base du balayage est exécutée avec une syntaxe de ligne de commande très simple. N'oubliez pas de spécifier les ports à scanner avec l'option -p, par exemple -p 1-1024. Voir ci-dessous.
blueflux@work3:~$ nmap -p 1-1024 192.168.0.1
Starting nmap 3.50 ( http://www.insecure.org/nmap/ ) at 2004-03-18 17:19 CET
Interesting ports on firewall (192.168.0.1):
(The 1021 ports scanned but not shown below are in state: closed)
PORT STATE SERVICE
22/tcp open ssh
25/tcp open smtp
587/tcp open submission
Nmap run completed -- 1 IP address (1 host up) scanned in 3.877 seconds
Il est aussi capable de deviner le système d'exploitation de l'hôte scanné par l'empreinte système (fingerprinting). Le fingerprinting nécessite les privilèges administrateur (root), mais il peut aussi être très intéressant à utiliser pour savoir ce que la plupart des gens voient sur l'hôte. Fingerprinting peut ressembler à ça :
work3:/home/blueflux# nmap -O -p 1-1024 192.168.0.1
Starting nmap 3.50 ( http://www.insecure.org/nmap/ ) at 2004-03-18 17:38 CET
Interesting ports on firewall (192.168.0.1):
(The 1021 ports scanned but not shown below are in state: closed)
PORT STATE SERVICE
22/tcp open ssh
25/tcp open smtp
587/tcp open submission
Device type: general purpose
Running: Linux 2.4.X|2.5.X
OS details: Linux Kernel 2.4.0 - 2.5.20
Uptime 6.201 days (since Fri Mar 12 12:49:18 2004)
Nmap run completed -- 1 IP address (1 host up) scanned in 14.303 seconds
Le fingerprinting n'est pas parfait, comme vous pouvez le voir, mais il est très utile, pour vous et pour un attaquant. Il faut le savoir. La meilleure chose à faire est de montrer le moins de choses possible, donc avec cet outil vous saurez ce qu'un attaquant peut voir de votre machine.
Nmap possède également une interface graphique, appelée nmapfe (Nmap Front End). C'est une excellente interface, et si vous avez besoin de faire des recherches un peu plus complexes, vous pourrez l'utiliser avec bonheur. Un exemple de capture d'écran :
Bien sûr, Nmap a d'autres fonctions que vous pouvez découvrir sur le site. Pour plus d'informations, regardez Nmap.
Comme vous l'avez compris, c'est un excellent outil pour tester votre hôte, et trouver les ports ouverts et ceux qui sont fermés. Par exemple, après avoir terminé vos réglages, utilisez nmap pour savoir si ce que vous avez fait correspond à votre attente. Obtenez-vous les réponses correctes des bons ports, et ainsi de suite.
13-5-2. Nessus▲
Alors que Nmap est plus un scanner de bas niveau, indiquant les ports ouverts, etc., le programme Nessus est un scanner de sécurité. Il tente de se connecter à différents ports, et de trouver en plus, les versions des serveurs en activité. Nessus effectue cela une étape plus loin, en trouvant les ports ouverts, ce qui est actif et sur quels ports, quel programme et quelle version, et ensuite teste les diverses menaces pour la sécurité dans les programmes, et finalement crée un rapport complet de toutes les menaces concernant la sécurité.
Comme vous pouvez le comprendre, c'est un outil extrêmement utile. Le programme fonctionne sur le modèle client/serveur, ainsi il est plus facile d'en connaître davantage sur votre pare-feu depuis l'extérieur en utilisant le démon Nessus, ou en interne de cette manière. Le client est une interface graphique dans laquelle vous vous connectez au démon, réglez vos paramètres, et spécifiez quel hôte scanner. Le rapport généré peut ressembler à l'exemple ci-dessous.
Nessus devrait être utilisé avec certaines précautions, car il peut faire « planter » une machine ou un service spécifié par une attaque. Ces attaques sur les machines sont désactivées par défaut, heureusement.
13-6. Prochain chapitre▲
Dans ce chapitre nous avons vu diverses techniques que vous pouvez utiliser pour déboguer vos scripts de pare-feu. Le débogage de scripts peut devenir fatigant à la longue, mais c'est une nécessité. Si vous utilisez les exemples, ça peut être très facile. Nous avons vu les techniques suivantes en particulier :
- Débogage par le Bash
- Outils système pour le débogage
- Débogage d'iptables
- Autres outils pour le débogage