11.6. Cible DNAT

La cible DNAT est utilisée pour la Traduction d'Adresse Réseau de Destination, ce qui veut dire qu'elle sert à réécrire l'adresse IP de Destination du paquet. Si un paquet est sélectionné, et qu'il est la cible de la règle, ce paquet et tous les paquets suivants du même flux seront traduits, et ensuite routés vers le matériel, l'hôte ou le réseau appropriés. Cette cible peut être extrêmement utile, par exemple, quand vous avez un hôte avec un serveur web dans un LAN, mais pas d'IP réelle routable sur l'Internet. Vous pouvez alors indiquer au pare-feu de transférer tous les paquets allant vers son propre port HTTP, vers le serveur web réel dans le LAN. Vous pouvez aussi spécifier une plage d'adresses IP de destination, et le mécanisme DNAT choisira l'adresse IP de destination au hasard pour chaque flux. Nous pourrons donc réaliser une sorte d'équilibrage de charge en faisant ça.

Notez que la cible DNAT est disponible uniquement dans les chaînes PREROUTING et OUTPUT de la table nat. Les chaînes contenant des cibles DNAT ne peuvent pas être utilisées depuis d'autres chaînes, comme la chaîne POSTROUTING.

Tableau 11.5. Options de la cible DNAT

Option --to-destination
Exemple iptables -t nat -A PREROUTING -p tcp -d 15.45.23.67 --dport 80 -j DNAT --to-destination 192.168.1.1-192.168.1.10
Explication L'option --to-destination indique au mécanisme DNAT quelle Destination IP placer dans l'en-tête IP, et où sont envoyés les paquets qui sont sélectionnés. L'exemple ci-dessus enverra tous les paquets destinés à l'adresse IP 15.45.23.67 dans une plage IP de réseau local comprise entre 192.168.1.1 jusqu'à 192.168.1.10. Notez que, comme décrit précédemment, un simple flux utilisera toujours le même hôte, et chaque flux aura une adresse IP attribuée au hasard, et qui sera toujours en direction de quelque part, dans ce flux. Nous pouvons aussi avoir à spécifier une seule adresse IP, dans ce cas nous serons toujours connectés au même hôte. Notez aussi que nous pouvons ajouter un port ou une plage de ports vers lequel le trafic sera redirigé. Ceci se fait en ajoutant, par exemple, un :80 à l'adresse IP pour laquelle nous voulons traduire les paquets. Une règle peut alors ressembler à --to-destination 192.168.1.1:80 par exemple, ou --to-destination 192.168.1.1:80-100 si nous voulons spécifier une plage de ports. Comme vous pouvez le voir, la syntaxe est à peu près la même que la cible SNAT, même si elles font deux choses totalement différentes. Les spécifications de port sont valides uniquement pour les règles qui précisent les protocoles TCP ou UDP avec l'option --protocol.

Comme DNAT nécessite pas mal de travail pour fonctionner correctement, j'ai décidé d'ajouter une explication plus complète sur ce sujet. Prenons un bref exemple pour comprendre comment les choses se passent normalement. Nous voulons publier notre site web via notre connexion Internet. Nous ne possédons qu'une seule adresse IP, et le serveur HTTP est situé dans notre réseau interne. Notre pare-feu possède l'adresse IP externe $INET_IP, et notre serveur HTTP a l'adresse IP interne $HTTP_IP et enfin le pare-feu a l'adresse IP interne $LAN_IP. La première chose à faire est d'ajouter la simple règle suivante à la chaîne PREROUTING dans la table nat :

iptables -t nat -A PREROUTING --dst $INET_IP -p tcp --dport 80 -j DNAT \
--to-destination $HTTP_IP

Maintenant, tous les paquets provenant de l'Internet et allant vers le port 80 sur notre pare-feu, sont redirigés (ou DNATés) vers notre serveur HTTP interne. Si vous testez ceci depuis L'Internet, tout devrait fonctionner parfaitement. mais, que se passe-t-il si vous essayez de vous connecter depuis un hôte sur le même réseau local que le serveur HTTP ? Il ne fonctionnera tout simplement pas. C'est un réel problème avec le routage. Commençons par voir ce qui se passe dans un cas normal. La machine externe possède une adresse IP $EXT_BOX, pour conserver la lisibilité.

  1. Le paquet quitte l'hôte connecté allant vers $INET_IP et la source $EXT_BOX.

  2. Le paquet atteint le pare-feu.

  3. Le pare-feu DNAT le paquet et envoie celui-ci à travers les différentes chaînes, etc.

  4. Le paquet quitte la pare-feu pour aller vers le $HTTP_IP.

  5. Le paquet atteint le serveur HTTP, et la machine HTTP répond en retour à travers le pare-feu, si c'est cette machine que la base de routage a entré comme passerelle pour $EXT_BOX. Normalement, ça devrait être la passerelle par défaut du serveur HTTP.

  6. Le pare-feu Un-DNAT le paquet de nouveau, ainsi le paquet semble provenir du pare-feu lui-même.

  7. Le paquet en réponse transite vers le client $EXT_BOX.

Maintenant, voyons ce qui se passe si le paquet est généré par un client sur le même réseau que le serveur HTTP lui-même. Le client possède l'adresse IP $LAN_BOX, tandis que les autres machines ont les mêmes réglages.

  1. Le paquet quitte la $LAN_BOX vers $INET_IP.

  2. Le paquet atteint le pare-feu.

  3. Le paquet est DNATé, et toutes les autres actions requises sont prises, cependant, le paquet n'est pas SNATé, ainsi la même adresse source IP est utilisée pour le paquet.

  4. Le paquet quitte le pare-feu et atteint le serveur HTTP.

  5. Le serveur HTTP essaie de répondre au paquet, et voit dans les tables de routage que le paquet provient d'une machine locale sur le même réseau, et donc tente d'envoyer le paquet directement à l'adresse source IP d'origine (qui devient alors l'adresse IP de destination).

  6. Le paquet atteint le client, et le client est dans la confusion car le paquet en retour ne provient pas de l'hôte qui a envoyé la requête d'origine. Donc, le client supprime le paquet, et attend une réponse "réelle".

La solution la plus simple à ce problème est de SNATer tous les paquets entrants dans le pare-feu sortant vers un hôte ou une IP sur lequel nous faisons du DNAT. Exemple, regardons la règle ci-dessus. Nous SNATons les paquets entrants dans notre pare-feu qui sont destinés à $HTTP_IP port 80 et ainsi il est vu que des paquets proviennent d'une $LAN_IP. Ceci force le serveur HTTP à envoyer ces paquets vers notre pare-feu, lequel Un-DNAT ceux-ci et les envoie au client. La règle ressemble à ceci :

iptables -t nat -A POSTROUTING -p tcp --dst $HTTP_IP --dport 80 -j SNAT \
--to-source $LAN_IP

Souvenez vous que la chaîne POSTROUTING est exécutée en dernier, et donc le paquet sera déjà DNATé une fois qu'il joint cette chaîne spécifique. C'est la raison pour laquelle nous sélectionnons les paquets basés sur une adresse interne.

[Avertissement] Avertissement

Cette dernière règle nuira sérieusement à votre journalisation, ainsi il n'est pas recommandé d'utiliser cette méthode, mais l'ensemble de l'exemple est valide. Que se passe-t-il alors, le paquet provient de l'Internet, est SNATé et DNATé et finalement atteint le serveur HTTP (par exemple). Le serveur HTTP voit maintenant les requêtes comme si elles provenaient du pare-feu, et donc journalisera toutes ces requêtes provenant de l'Internet comme si elles provenaient du pare-feu.

Ceci peut avoir également d'autres implications plus graves. Prenons un serveur SMTP sur un LAN, qui autorise les requêtes depuis le réseau interne, et vous avez un pare-feu paramétré pour transférer le trafic SMTP vers ce serveur. Vous avez donc créé un serveur SMTP en relais ouvert, avec une journalisation horrible !

Une solution à ce problème est de rendre la règle SNAT plus spécifique dans sa partie sélection, et de travailler seulement sur les paquets qui proviennent du LAN. En d'autres termes, ajoutez un -i $LAN_IFACE à l'ensemble de la commande. Ceci fera que la règle ne fonctionnera que sur les flux provenant du LAN, et donc n'affectera pas la source IP, ainsi les journaux seront corrects, sauf pour les flux venant du LAN.

Vous auriez mieux fait, en d'autres termes, de résoudre ces problèmes soit en paramétrant un serveur DNS (serveur de nom) séparé pour votre LAN, soit en paramétrant une DMZ séparée, la dernière étant préférable si vous en avez les moyens.

Vous pouvez penser que c'est suffisant, et c'est vrai, sauf à considérer un dernier aspect du scénario. Que se passe-t-il si le pare-feu lui-même essaie d'accéder au serveur HTTP, où va-t-il ? Il tentera malheureusement d'accéder à son propre serveur HTTP, et pas au serveur situé sur $HTTP_IP. Pour parer à ça, nous devons rajouter une règle DNAT à la chaîne OUTPUT. Suivant l'exemple ci-dessus, ça ressemblerait à quelque chose comme :

iptables -t nat -A OUTPUT --dst $INET_IP -p tcp --dport 80 -j DNAT \
--to-destination $HTTP_IP

Tous les réseaux séparés qui ne sont pas situés sur le même réseau que le serveur HTTP fonctionneront sans soucis, tous les hôtes sur le même réseau que le serveur HTTP pourront s'y connecter et enfin, le pare-feu pourra exécuter ses connexions correctement. Maintenant, tout fonctionne et aucun problème ne devrait survenir.

[Note] Note

Tout le monde devrait réaliser que ces règles affectent seulement la façon dont le paquet est DNATé et SNATé. En plus de ces règles, nous avons aussi besoin de règles supplémentaires dans la table filter (chaîne FORWARD) pour permettre aux paquets de traverser ces chaînes. N'oubliez pas que tous les paquets sont déjà passés par la chaîne PREROUTING, et donc ont vu leur adresse de destination réécrite par DNAT.

[Note] Note

Fonctionne avec les noyaux Linux 2.3, 2.4, 2.5 et 2.6.