Nat Loopback, qu’est ce donc que cette diablerie ?
C’est la possibilité de pouvoir s’adresser à un service que l’on fourni sur une IP externe (par exemple un serveur Web) depuis le réseau interne mais tout cela en communiquant via l’IP externe.
Faut être tordu ? Non pas forcement 😉
Article que j’avais prévu d’écrire depuis longtemps et pour le coup, Black Exporter m’y a un peu obligé…
I – Le pourquoi
Je vous explique plus en détail :
L’idée, c’est qu’une VM (10.10.1.99) dans mon réseau interne (10.10.1.0/24) puisse interroger par exemple https://blog.debugo.fr, hébergé sur un serveur (10.10.1.10) lui aussi donc dans le réseau interne.
Mais tout ceci en passant par l’IP externe 99.99.99.99 (IP utilisée par les clients externes à mon réseau, par exemple, vous…)
A – Pourquoi faire cela ?
Après tout, je pourrais très bien dire via un enregistrement DNS interne que la ressource demandée est accessible sur l’IP interne :
blog.debugo.fr. IN A 10.1.1.10
Et vous vous dites qu’au final je chercher encore à complexifier.
Oui mais non…
Cela fonctionne, pas de soucis, mais dans mon cas, j’ai des configurations particulières en place au niveau de mes serveurs :
Si la requête vient de l’intérieur ou de l’extérieur, elle ne sera pas traitée de la même façon.
Le résultat final est bien le même, mais certaines choses sont actives dans un cas et pas dans l’autre (FW Applicatif, analyse, logging, etc…).
Et je pourrais tout à fait avoir un cas ou le site fonctionne de l’interne, mais pas de l’externe.
Il s’agit la d’un choix volontaire de ma part !
Bien sur, je pourrais changer les configurations sur mes serveurs pour réagir de façon ‘externe’ si la requête vient d’une machine particulière…
Mais encore une fois : oui mais non…
Car si cette technique marche pour le serveur Web ou pour le serveur SMTP, pour mon DNS, je me fais avoir….
B – Et pourquoi un soucis avec le DNS ?
Pourquoi ? Haha, la bonne question !
Dans mon article sur le DNS, j’explique gérer plusieurs zones et ce, en fonction de vues.
Si la requête vient du réseau interne, Bind fourni une réponse.
Si la requête vient du réseau externe, Bind fourni une autre réponse.
Donc si en interne, je fais une requête DNS vers mon Bind, je reçois naturellement une réponse de la zone interne.
Et je veux avoir la possibilité d’avoir une réponse « externe » mais demandée de « l’interne » !!
Et quitte à faire ça pour le DNS, autant le faire pour le Web (et SMTP, IMAPS et ce que vous voulez…)
Bon, la encore, c’est vraiment du à ma façon d’avoir conçu l’architecture DNS.
C – Et avec mon DNS ?, le retour !
Bon, je vous parle aussi rapidement d’une autre particularité dû la encore à mon DNS.
Toujours dans cet article sur le DNS, j’explique ne gérer qu’une zone : debugo.fr, que ce soit en autoritaire (externe) ou en interne (par ex, un serveur toto aura un fqdn toto.debugo.fr).
Et pour manager cela, j’utilise les vues : le fichier de zone n’est pas le même selon l’ip source.
Une IP interne tape dans la zone : debugo.fr mais dans un fichier db.debugo.fr.intra.
Une Ip externe tape aussi dans la zone debugo.fr mais dans un fichier db.debugo.fr.extra.
Bref, si depuis 10.10.1.99, je tente de résoudre blog.debugo.fr, le DNS interne ne connait pas si je n’ajoute pas au préalable, dans la zone interne :
blog.debugo.fr IN A 99.99.99.99
Mais la encore, c’est encore un problème que je me suis « imposé » 😉
Et pour être complet, j’ai en fait :
blog.debugo.fr. IN A IP.EXTERNE blogint.debugo.fr IN A IP.INTERNE
Ce qui me permet au choix, de contacter le serveur Web de façon interne, ou externe (en pensant a bien rajouter l’alias de serveur côté Nginx, bien évidement…)
D – Bref
On revient à la problématique de base : en interne, blog.debugo.fr se résout bien sur une IP Externe, et je veux pouvoir contacter le service Web sur cette IP Externe.
II – Problème
Tout d’abord, dans IPtables, on aurait sur le routeur (pour le serveur Web), quelque chose qui ressemble à :
iptables -t nat -A PREROUTING -d 99.99.99.99 -p tcp -m multiport --dports 80,443 -j DNAT --to-destination 10.10.1.10 iptables -t filter -A FORWARD -p tcp -d 10.10.1.10 -m multiport --dports 80,443 -j ACCEPT
Les paquets ayant pour destination 99.99.99.99 sur les ports 80 et 443 sont modifiés avec la destination 10.10.1.10 (puis le routeur fait son boulot de routeur)
Et comme ma policy forward par défaut est de droper, je débloque ensuite pour les paquets en forward et à destination de 10.10.1.10.
La configuration classique pour rendre accessible les services de l’extérieur.
Maintenant, regardons ce qu’il se passe quand le client veut causer avec 99.99.99.99 :
- Le client créé le paquet : src 10.10.1.99/dst 99.99.99.99. Il s’attend donc à recevoir une réponse avec un paquet : src 99.99.99.99/dst 10.10.1.99.
- Ce paquet ayant pour destination une adresse externe, selon la table de routage du client, il est envoyé à la gateway, à savoir le routeur.
- Le routeur reçoit le paquet et selon la règle PREROUTING, modifie l’adresse de destination : le paquet devient src 10.10.1.99/dst 10.10.1.10 et le routeur enregistre cela dans la table de Nat.
- Le routeur consulte sa table de routage et transmet le paquet sur le bon réseau (la, le stwitch virtuel lan10 d’Openvswitch enverra vers le bon lien)
- Le serveur reçoit le paquet (src 10.10.1.99/dst 10.10.1.10) et lui répond en inversant destination et source. Ce paquet réponse est : src 10.10.1.10/dst 10.10.1.99
- Le serveur consulte sa table de routage, constate qu’il peut s’adresser directement au réseau 10.10.1.0/24 et il envoit la réponse (la encore pour le switch virtuel lan10)
- Le client reçoit une réponse src 10.10.1.10/dst 10.10.1.99, et du coup, il jette. Et oui, la source attendue n’est pas la bonne.
Si on fait la même chose avec du DNS avec Dig, ce dernier nous avertit qu’il y a un hic :
# dig blog.debugo.fr +short @IPDNSEXTERNE ;; reply from unexpected source: IPDNSINTERNE#53, expected IPDNSEXTERNE#53
La réponse ne vient pas d’où elle devrait.
III – Le comment
La solution est ce qu’on appelle le Nat Loopback et c’est au final simple et logique.
Si on ajoute une règle de la sorte :
iptables -t nat -A POSTROUTING -s 10.10.1.0/24 -d 10.10.1.10 -p tcp -m multiport --dports 80,443 -j SNAT --to 99.99.99.99
Cela stipule que les paquets ayant pour source le réseau 10.10.1.0/24 et pour destination le serveur 10.10.1.10 doivent voir leurs adresses sources modifiées en 99.99.99.99.
Et cela donne ceci :
- Le client créé le paquet : src 10.10.1.99/dst 99.99.99.99. Il s’attend donc à recevoir une réponse avec un paquet : src 99.99.99.99/dst 10.10.1.99.
- Ce paquet ayant pour destination une adresse externe, selon la table de routage du client, il est envoyé à la gateway, à savoir le routeur.
- Le routeur reçoit le paquet et selon la règle PREROUTING, modifie l’adresse de destination : le paquet devient src 10.10.1.99/dst 10.10.1.10.Il enregistre cette manipulation dans la table de Nat
- Le routeur consulte sa table de routage et prend note du réseau concerné ( le switch lan10). Mais avant :
- Selon la règle de POSTROUTING, qui est vérifiée (source réseau 10.10.1.0/24 et destination 10.10.1.10), l’adresse de source est modifiée elle aussi. Le paquet devient src 99.99.99.99/dst 10.10.1.10 , le routeur enregistre ça dans la table de Nat et il transmet.
- Le serveur reçoit le paquet (src 99.99.99.99/dst 10.10.1.10) et lui répond en inversant destination et source. Ce paquet réponse est : src 10.10.1.10/dst 99.99.99.99
- Le serveur consulte sa table de routage et transmet ce paquet à destination de 99.99.99.99 à la gateway, le routeur.
- Le routeur reçoit un paquet src 10.10.1.10/dst 99.99.99.99.
- Comme il s’agit d’une réponse, il va refaire ses ptits changement.
- Il modifie la destination par la source original , le paquet devient src 10.10.1.10/dst 10.10.1.99
- Le routage indique l’interface.
- Puis il modifie la source par la destination originale : le paquet devient src 99.99.99.99/dst 10.10.1.99 et il transmet.
- Le client reçoit une réponse src 99.99.99.99/dst 10.1.1.99, ce qu’il attendait bien, tout roule ! La communication peut se faire.
Rien de bien compliqué au final, ce n’est que logique !
Pour faire ça sur mes différents serveurs, j’ai quelque chose qui ressemble au final à :
Règles classique de prerouting et de forward :
iptables -t nat -A PREROUTING -d IP.EXTERNE -p tcp -m multiport --dports 80,443 -j DNAT --to-destination IP.INTERNE.WEB iptables -t nat -A PREROUTING -d IP.EXTERNE.MAIL -p tcp -m multiport --dports 25,993 -j DNAT --to-destination IP.INTERNE.MAIL iptables -t nat -A PREROUTING -d IP.EXTERNE -p udp --dport 53 -j DNAT --to-destination IP.INTERNE.DNS iptables -t filter -A FORWARD -p tcp -d IP.INTERNE.WEB -m multiport --dports 80,443 -j ACCEPT iptables -t filter -A FORWARD -p tcp -d IP.INTERNE.MAIL -m multiport --dports 25,993 -j ACCEPT iptables -t filter -A FORWARD -p udp -d IP.INTERNE.DNS --dport 53 -j ACCEPT
Éventuellement pour que les serveurs puissent sortir d’eux mêmes (à cause de la policy forward à Drop par défaut) et le postrouting classique :
iptables -t filter -A FORWARD -p tcp -s IP.INTERNE.WEB -m multiport --dports 80,443 -j ACCEPT iptables -t filter -A FORWARD -p tcp -s IP.INTERNE.MAIL-m multiport --dports 25,80,443 -j ACCEPT iptables -t filter -A FORWARD -p tcp -s IP.INTERNE.DNS -m multiport --dports 80,443 -j ACCEPT iptables -t filter -A FORWARD -p udp -s IP.INTERNE.DNS --dport 53 -j ACCEPT iptables -t nat -A POSTROUTING -s 10.10.1.0/24 ! -d 10.10.1.0/24 -p tcp -m multiport --dports 80,443 -j SNAT --to IP.EXTERNE iptables -t nat -A POSTROUTING -s IP.INTERNE.MAIL ! -d 10.10.1.0/24 -p tcp --dport 25 -j SNAT --to IP.EXTERNE.MAIL
Ces règles ne concernent que des paquets initiés par les serveur eux mêmes (quand ils souhaitent communiquer avec l’extérieur).
Les réponses qu’ils font à des requêtes passées en amont par le routeur ne sont pas concernées (vu que c’est déjà dans la table de NAT).
Ces règles leurs servent donc pour aller faire leurs mise à jour (80,443), pour que le mail puisse faire sortir les messages (25) et pour que le DNS lui aussi soit capable de contacter les autres (53 en udp).
Edit du lendemain : Et je constate que je suis un gros boulet… (cf plus bas…)
Puis les fameuses règles de NatLoopback :
iptables -t nat -A POSTROUTING -s 10.10.1.0/24 -d IP.INTERNE.WEB -p tcp -m multiport --dports 80,443 -j SNAT --to IP.EXTERNE iptables -t nat -A POSTROUTING -s 10.10.1.0/24 -d IP.INTERNE.MAIL -p tcp -m multiport --dports 25,993 -j SNAT --to IP.EXTERNE.MAIL iptables -t nat -A POSTROUTING -s 10.10.1.0/24 -d IP.INTERNE.DNS -p udp --dport 53 -j SNAT --to IP.EXTERNE
Et voila !
Je peux maintenant depuis l’interne, simuler une connexion depuis l’externe.
Simple au final non ?
Le lecteur attentif remarquera que j’ai deux IP externes. Une pour « tout » et une pour le mail.
Ça, c’est pour les histoires de rdns valide pour le smtp.
Et si ça fonctionne sans soucis, j’ai eu, la aussi un truc « curieux », dont je vous parle dans l’article dédié au source routing.
Mais le lecteur attentif remarquera également que je suis aussi un peu un boulet, car tel que je vous le présente mes règles d’Iptable se contredise…
Plus d’explications dans la suite de cette article : Je suis un boulet.
Envie de me soutenir et de me payer un café ? C’est sur la page Don !