Iptables firewall puissant
Vous connaissez le mot “firewall” ou “par-feu”, cet outil qui permet d’empêcher l’intrusion sur votre machine. Beaucoup confonde les par-feu, les anti-virus et les anti-spyware… Le par-feu n’ait en fait qu’un bloqueur de port. Pour résumer, les virus et les spywares (appelé aussi logiciels espions) sont des programmes qui vont soit détruire le système (pour le virus), soit envoyer des informations à un tiers vous polluant de pubs en tout genre, ou ralentissant la machine (spyware). Le par-feu n’a rien à voir avec cela. Il sert à empêcher un intrus sur le réseau d’entrer dans votre machine en se connectant à un service.
Sous Linux, le par-feu se nomme “iptables”, digne successeur de “ipchains”, il permet aussi de faire du “NAT” (Network Adress Translation) et d’autres options particulières comme le “mangling”. Mais nous allons nous pencher seulement sur la partie “firewall”, c’est à dire “par-feu”.
A quoi cela peut vous servir ? Tant il est vrai que la plupart des distributions propose une interface graphique pour configurer son par-feu, si vous commencez à vouloir gérer un serveur pour votre site il est fort important de comprendre le principe. Et ne serait-ce que pour des raisons de curiosité, pourquoi ne pas comprendre ce qu’il se passe sur votre machine ? De plus, ce n’est pas si compliqué et cela apporte un bon lot de connaissances qui vous permettra de frimer devant les filles :)
Principe de filtrage et interfaces
Alors allons-y, commençons notre introspection sur iptables. Il faut d’abord comprendre ce qu’il se passe quand on tente de se connecter à une machine. Sur votre ordinateur, ou votre serveur (dans le fond c’est exactement pareil) existent des services. Cela peut-être un service Web (Apache), SSH, telnet, FTP, emule… bref, tout ce qui permet de communiquer de votre machine à un tiers et inversement.
Chacun de ces services ont un numéro. Ce numéro est ce que l’on appelle un “port”. Les services connus et standard ont quasiment toujours le même numéro de port, par exemple un service WEB utilise le port 80. Votre navigateur qui tente de se connecter à un site utilise pratiquement toujours ce numéro de port. En ce qui concerne MSN, c’est une série de ports, allant de 1863 pour communiquer et 6891 à 6900 pour l’envoi de fichiers et 6901 pour la vidéo… Bref, ces numéros sont généralisés. Gardez en tête que le numéro de port est “virtuel”, c’est un concept du protocole TCP/IP.
Le but du firewall est d’autoriser ou non les connexions entre un tiers et vous, ou depuis votre PC vers un tiers, au travers de ports ou de type de paquets.
La seconde notion à comprendre est “l’interface”. Un interface est en fait l’outil qui va propager les “paquets” (ce sont les bloc d’informations à envoyer vers les machines cibles). Dans 99% des cas, une interface est une carte réseau. Sous Linux elles portent le nom “eth” (pour ethernet) suivit d’un numéro de carte ou ppp (pour les modems). Si vous n’avez qu’une carte réseau, l’interface se nomme “eth0”.
Il existe une interface “virtuelle” interne à votre machine, elle se nomme “lo” pour “local” et elle sert pour les communications interne à la machine. Il est fortement recommandé de ne **jamais** bloquer quoi que ce soit pour cette interface.
Changes de chaîne
Maintenant, passons à “iptables”. L’idée est que chaque paquet soit filtré via une chaîne. Cette chaîne définira des règles. Il existe des chaînes prédéfinies: -ACCEPT qui a pour règle d’accepter la connexion -DROP qui a pour régle de laisser tomber la connexion, le tiers va attendre et tomber en timeout (délais dépassé) -REJECT qui refuse la connexion (le tiers est déconnecté tout de suite) -NEW pour une connexion entrante -RELATED, ESTABLISHED pour une connexion déportée ou en cours -..
Si vous tentez une connexion, l’état du paquet est “NEW”. Si il est acceptée, il passe à ESTABLISHED. Si on l’a simplement fait transiter sur un autre port, il est RELATED, puis il passera à ESTABLISHED si la connexion est acceptée.
Ce sont ces chaînes que nous allons utiliser. Nous verrons par la suite qu’il est possible de créer nos propres chaînes afin de classer les filtres.
La technique est d’ajouter des tests et des règles à des chaînes. En général nous ajoutons nos règles et tests dans la chaîne “INPUT”. Il est très rare de refuser des connexion depuis notre PC vers un tiers… par contre, on préfère contrôler ceux qui veulent se connecter chez nous.
Pour traiter un paquet, il faut simplement travailler dans l’ordre, on commence par accepter ce que nous voulons, puis en dernier lieu on interdit **tout**. Cela permet d’ajouter des règles au fur et à mesure que nos besoins évoluent. L’ordre est important, c’est pourquoi il vaut mieux créer un script plutôt que d’ajouter les règles au fur et à mesure.
La commande iptables est, certes, de prime abord, complexe, mais logique et finalement on s’en sort facilement. Dans la plupart des cas, nous faisons ce genre de chose:
iptables -A NOMDELAchaîne [options...] -i interface -o interface -j chaîne_DE_SORTIE
NOMDELAchaîne sera INPUT, OUTPUT… en gros “où va le paquet”. -i et -o correspondent aux interfaces (eth0, lo, ppp0…) et enfin chaîne_DE_SORTIE correspond à la chaîne qui va traiter le paquet (ACCEPT, DROP…).
Bref, on va souvent procéder comme ceci:
iptables -A INPUT (on filtre) -j (on accepte, on rejette, on redirige... au choix)
Il suffira en fait de définir le filtrage. Notez les quelque part, nous allons nous baser sur une machine serveur WEB qui fera tourner Apache (port 80), tomcat (port 8080), SSH (port 22) et Red5 (port 1935). On note en français sur une feuille de papier:
Sécurité:
J'accèpte les connexions locale entrée et sortie (sur lo)
Par défaut:
J'accèpte les connexion sortantes
J'accèpte les forward (on ne sait jamais)
Je bloque les entrées
Mes règles:
J'accèpte les connexion sur le port 80
J'accèpte les connexion sur le port 8080
J'accèpte les connexion sur le port 22
J'accèpte les connexion sur le port 1935
Sinon je refuse tout !!!
Et on passe à la suite
Flexion… extensions !
Iptables est extensible et va permettre de charger des options supplémentaire selon les tests et ciblages que nous voulons traiter. Par exemple, pour le protocole “tcp” (le plus répandu) nous pouvons vérifier le port de sortie, d’entrée, les flags… bref, des choses parfois complexes… mais souvent utiles. Et oui, nous voulons vérifier les connexions à des ports, “port” étant une notion TCP, nous devons charger le proto tcp avec l’option “-p”.
Je vais donc activer de nouvelles options, par exemple: —dport pour définir le port de destination du paquet —sport pour définir le port de provenance (source) du paquet -.. et d’autres encore
Voici par exemple comment accepter les connexions externes sur le port 80 (Apache) de notre serveur (ou PC…), attention, ne tentez pas tout de suite cette commande, je préfère que vous testiez plus tard avec les autres chaînes:
iptables -A INPUT -p tcp -m tcp --dport 80 -J ACCEPT
A quoi sert l’option “-m” ? elle sert à définir ce que nous allons tester juste derrière. Nous pouvons en effet charger plusieurs extensions en même temps, iptables doit savoir à quoi vont correspondre les options qui vont suivre. C’est en quelques sortes un interrupteur.
Je vais être plus clair. Pour les tests d’état (rappelez vous: NEW, RELATED…) j’ai besoin de l’option “–state” contenu dans le groupe d’option “state”. Pour tester les ports, j’ai besoin du groupe d’option “tcp” qui me permet d’utiliser l’option “–dport” et/ou “–sport”. Voilà comment va se passer la commande:
iptables -A INPUT -p tcp -m tcp --dport 80 -m state --state NEW -j ACCEPT
C’est pas si complexe: je demande à vérifier les ports, donc je charge le proto tcp (-p tcp), j’ai besoin d’options pour ces test, donc je passe en mode “tcp” (-m tcp), je teste mon port de destination avec “–dport”. Ensuite je cherche à connaitre l’état, je passe en mode “state” (-m state) et l’état que je vise et “NEW” (–state NEW) et enfin j’accèpte le paquet. Il suffit en fait de lire dans l’ordre…
Cela veut dire que nous acceptons tout paquets venant de n’importe où en direction “entrante” sur le port 80. Nous pouvons spécifier l’interface d’entrée, par exemple nous pouvons autoriser les paquets venant sur la carte eth0 et non eth1:
iptables -A INPUT -p tcp -m tcp -i eth0 --dport 80 -J ACCEPT
iptables -A INPUT -p tcp -m tcp -i eth1 --dport 80 -J DROP
Ici, je refuse les connexions sur le port 80 avec la carte eth1, mais j’accèpte ceux de eth0.
On peut auss travailler avec les états de paquet, voici par exemple comment autoriser les nouveau paquets qui arrivent sur le port 80:
iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -i eth0 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 80 -i eth1 -j DROP
Alors, malgré ce que l’ont pense, cela risque de ne pas marcher… en fait, nous avons accepter les connexions nouvelles, mais si la connexion persiste, nous n’avons pas de règles définie. Le principe standard est d’accepter les connexion qui sont déjà présentes:
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
Bon, une chose **très importante**, je vous l’ai dit tout à l’heure, c’est de ne jamais bloquer “lo” (interface loopback, local…) l’idée sera de mettre en premier lieu une régle qui accepte tout sur “lo”:
iptables -A INPUT -i lo -j ACCEPT
Mais que fais la police ?
Tout paquet qui traverse les filtres sans être modifié passe par la police! La police étant le dernier recours :) En fait, il s’agit de définir une règle de base pour les paquets qui n’ont put être intercepté par aucun filtre.
Ceci ne peut être appliqué que sur les chaînes de base: FORWARD, INPUT, OUTPUT. Dans la majeur partie des cas on accèpte les paquets sortants, les forward, mais on laisse tomber les paquets entrants. Ce la ne veut pas dire que vous allez tout couper, mais que les paquets qui n’ont pas été filtrés par le firewall vont être laissés tombés.
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P INPUT DROP
Couper par défaut OUTPUT pourrait être stressant à configurer. Mais avec de l’expérience, cela sera plus propre de définir ce que vous acceptez en sortie.
Alors on le monte ce mur ?
Nous avons vu les principes de base, je vais vous monter un script de firewall utilisé pour un simple serveur WEB. Etant donné nous utilisons SSH pour le contrôler à distance, je dois ouvrir le port “22” (ssh) sinon je ne pourrais plus me connecter. J’accèpterai aussi les paquets ICMP, par exemple pour accépter les “ping”.
#je supprime le contenu des règles connues
iptables -F
#police par défaut
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P INPUT DROP
#la rèlge primordiale pour ne pas tuer un serveur:)
iptables -A INPUT -i lo -j ACCEPT
#cette règle pourra être utile dans le cas où vous mettez une politique de sortie
#bloquée
iptables -A OUTPUT -o lo -j ACCEPT
#on accèpte les connexions sur le port 80 pour que le serveur Apache puisse répondre:
iptables -A INPUT -i eth0 -p tcp -m tcp --dport 80 -j ACCEPT
#on accèpte aussi les connexions SSH pour pouvoir contrôler le serveur
iptables -A INPUT -i eth0 -p tcp -m tcp --dport 22 -j ACCEPT
#je veux accépter les ping, toujours sympa pour vérifier que le serveur n'est pas mort
iptables -A INPUT -p icmp -m icmp --icmp-type any -j ACCEPT
#toutes connexion déjà entreprise ne doit pas être coupée:
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
#et enfin, je refuse tout le reste
iptables -A INPUT -j REJECT
Ce script est simple et permet déjà de ne pas avoir des connexions pirates dans tous les sens. Attention, cela ne vous protège pas forcément de très bons pirates qui vont exploiter des failles de fonctionnement du service Apache, SSH etc… pour passer outre le firewall, mais je vous assure que cela va vous réduire les problèmes.
Classons
Je vous ai aussi annoncer que l’on pouvait créer nos propres chaînes. C’est une méthode sympa pour classer les types de connexions, supprimer certaines règles rapidement, les réactiver rapidement… etc…
Je vais prendre un exemple simple, toujours le même serveur web. Sachez que deux services web existent, 80 et 8080. Le premier étant le standard, le second étant plutôt utilisé par “tomcat”. Clairement, ces deux ports sont des services WEB.
En ce qui concerne SSH, c’est un service spécifique à l’administration. Je vais donc définir des chaînes spéciales, une pour les services WEB, une autre pour les connexions d’administration. Et je vais dirriger mes paquets dans ces chaînes:
#je crée des règles
iptables -N WEB_SERVER
iptables -N ADMIN
#je supprime le contenu des règles connues
iptables -F
#police par défaut
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P INPUT DROP
#la rèlge primordiale pour ne pas tuer un serveur:)
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
#ping icmp
iptables -A INPUT -p icmp -m icmp --icmp-type any -j ACCEPT
### règles spécifiques
#en ce qui concerne la règle WEB_SERVER
iptables -A WEB_SERVER -j ACCEPT
#en ce qui concerne la règle ADMIN
iptables -A ADMIN -j ACCEPT
#### maintenant on classe
#on envoit les connexion entrante sur 80, et 8080 vers une nouvelle règle "WEB_SERVER"
iptables -A INPUT -i eth0 -p tcp -m tcp --dport 80 -j WEB_SERVER
iptables -A INPUT -i eth0 -p tcp -m tcp --dport 8080 -j WEB_SERVER
#et en ce qui concerne la connexion à SSH, c'est la chaîne ADMIN qui sera utilisée
iptables -A INPUT -i eth0 -p tcp -m tcp --dport 22 -j ADMIN
#et on refuse le reste
iptables -A INPUT -j REJECT
L’intérêt est de pouvoir couper rapidement les règles spécifiques au web, en faisant simplement
iptable -F WEB_SERVER
Ou alors de lister les filtres d’une chaine:
iptable -L WEB_SERVER
Dans ce cas précis, je coupe les autorisations WEB, mais pas SSH, pas ICMP, etc… Je pourrais aussi ajouter à la volée de nouveaux test, par exemple je pourrais ajouter à “ADMIN” un test sur le port 10000 (service webmin) directement:
iptables -A INPUT -p tcp -m tcp --dport 10000 -j ADMIN
Et si j’ai un service web de streaming flash (red5, FMS… au choix) qui se trouve sur le port 1935, je n’ai qu’à ajouter:
iptables -A INPUT -p tcp -m tcp --dport 1935 -j WEB_SERVER
Et vous pouvez ajouter autant de chaîne que vous le souhaitez, même des chaînes qui redirrigent vers d’autres chaînes… etc…
Nous n’avons pas vu comment redirriger un port, ou comment faire du NAT. Mais ce sera pour la prochaîne fois. Pour le moment, essayez vous à interdire des connexions sur des ports. Vous pouvez par exemple bloquer les connexions sortantes vers SSH, tentez de vous connecter… puis flushez la règles et retentez… Bref tentez votre coup!
le mot de la fin ?
J’ai rédigé cet article il y a plusieurs mois, je l’ai repris pour en parler un peu. Il se peut que je dise de grosses bêtises ou que mes exemples ne fonctionne pas. Quoiqu’il en soit, essayez vos règles de par-feu **EN PREMIER LIEU SUR UNE MACHINE EN TEST, DANS VOTRE LAN**. Couper les connexions par inadvertance sur un serveur en prod serait catastrophique…
Sachez que les règles que vous appliquez via un script sera oublié lors d’un redémarrage de serveur. De ce fait si vous pouvez redémarrer le serveur après une erreur, cela ne sera pas trop grave. Afin de rendre le par-feu automatiquement configuré au démarrage, il suffira de créer un démon simplement.
Après que vous soyez sûr et certain que votre firewall fonctionne:
iptables-save > /etc/myfirewall.conf
vi (ou nano, au choix) /etc/init.d/myfirewall
placez ce code:
#!/bin/bash
# chkconfig: - 90 10
# description: My firewall via iptables
case $1 in
start)
echo -n "Starting firewall..."
[[ ! -f /var/lock/subsys/firewall ]] && iptables-restore /etc/myfirewall.conf
touch /var/lock/subsys/firewall
echo " [OK]"
;;
stop)
echo -n "Stoping firewall..."
[[ -f /var/lock/subsys/firewall ]] && iptables -F
rm -f /var/lock/subsys/firewall
echo " [OK]"
;;
status)
[[ -f /var/lock/subsys/firewall ]] && echo "Runing" || echo "Not Running"
iptables -L
;;
restart)
stop
sleep 1
start
;;
*)
echo "Usage: firewall {start|stop|restart|status}"
;;
esac
exit
puis faite chmod +x /etc/init.d/myfirewall puis de placer ce service au démarrage:
chkconfig --add myfirewall
chkconfig --level 35 myfirewall
Je n’active le firewall qu’au level 3 et 5… si un soucis intervient, je préfère pouvoir redémarrer au runlevel 1 (mode single) et supprimer mes bêtises :)
Bref, vous pourrez relancer le firewall ou le couper… simplement via service myfirewall stop ou service myfirewall start… et voir l’état du firewall via service myfirewall status
Note: les Ubunteros devront remplacer service myfirewall par /etc/init.d/myfirewall… les joies des différences d’Ubuntu…
Les ressources
Je me suis inspiré de pas mal de documents, voici la liste:
-http://www.netfilter.org/documentation/HOWTO/fr/packet-filtering-HOWTO-7.html -http://www.lea-linux.org/cached/index/Reseau-secu-iptables.html -http://www.netfilter.org/
Voilà qui est fait :)