iodine : l'IP-over-DNS ou l'internet là où seul le DNS passe
La scène est connue: l’hôtel d’une conférence, le hotspot du fast-food du coin, l’un de ces portails captifs qui éclosent partout depuis que le Wi-Fi est devenu un service. Vous vous connectez, une page s’ouvre, vous êtes censé payer ou saisir un code de chambre et tout ce qui n’est pas HTTP sur le port 80 tombe dans le vide. Pas de SSH, pas de SMTP direct, pas de VPN visible. Un filtre qui ne laisse passer que le web, donc presque rien.
Sauf que le portail a un impératif technique: il doit laisser fonctionner la résolution de noms. Un portail captif qui ne résout pas le DNS est un portail auquel on ne peut même pas se connecter, puisque sa propre page d’accueil a un nom à résoudre. Donc le resolver du réseau résout, pour tout le monde, authentifié ou pas et il relaie vers l’extérieur, sinon il ne servirait à rien.
C’est là que tout se joue. Le protocole DNS est un protocole de transport qui s’ignore. Une requête, c’est un nom de domaine et un nom de domaine peut embarquer des données dans ses labels. Une réponse, c’est un enregistrement et un enregistrement peut en embarquer autant. Si le réseau ne vous laisse que le DNS, vous n’avez pas presque rien: vous avez un canal bidirectionnel. Et un canal, ça se route.
iodine fait exactement ça: du tunneling IPv4 over DNS. Première version publique le 24 juin (0.3.0, Linux, FreeBSD, OpenBSD), 0.3.1 ce mois-ci pour ajouter Mac OS X. Deux auteurs, Erik Ekman et Björn Andersson, licence ISC, deux binaires (client et serveur), un prérequis non négociable: un device TUN/TAP. Et un nom qui est un blason: IOD pour IP Over DNS et l’iode, élément de numéro atomique 53, pour le port 53 du DNS. On ne fait plus clair.
Le test préliminaire: une question à dig
Le portail captif vous donne le DNS, donc il vous donne l’internet. Encore faut-il le vérifier, parce que tous les résolveurs ne se valent pas. Certains relais refusent de résoudre ce qui n’est pas public, d’autres tronquent, d’autres basculent tout en minuscules. Le test tient en une commande et il se pose AVANT de toucher à quoi que ce soit (dig a eu son article le mois précédent ):
$ dig -t NS foo123.tunnel.mondomaine.fr +short
Si une réponse arrive (même cabalistique), c’est que le resolver du portail a relayé votre requête vers le serveur de noms de VOTRE domaine. Et votre serveur de noms, c’est vous. Le chemin est ouvert. Si la requête meurt en timeout, le portail filtre aussi le DNS relayé et tout cet article est théorique: retournez à la page d’accueil du portail, il n’y a pas de honte à payer le code de chambre.
Pourquoi ça marche: la délégation. Le resolver du portail ne connaît pas tunnel.mondomaine.fr, alors il fait ce que font tous les résolveurs depuis RFC 1035: il remonte l’arbre DNS jusqu’à trouver le serveur autoritaire et c’est le vôtre. Le portail ne fait que du DNS normal, il ne peut pas faire autrement.
La délégation: deux lignes dans la zone
Il faut trois choses qu’on possède déjà: un domaine à soi, un serveur avec une IP publique (la dedibox du week-end fait très bien l’affaire) et dix minutes. Le serveur DNS de la zone ajoute deux lignes:
tunnel IN NS ns.tunnel.mondomaine.fr.
ns.tunnel IN A <ip.de.la.dedibox>
Notez le point final sur la ligne NS, il est obligatoire et son absence est la première cause de zone cassée de l’histoire du DNS. Ces deux lignes disent au monde: tout ce qui finit par tunnel.mondomaine.fr doit être demandé à ns.tunnel.mondomaine.fr, dont voici l’adresse. C’est tout. Chaque requête DNS pour un nom de ce sous-domaine atterrira désormais sur la dedibox, où que soit l’interrogé, y compris depuis un resolver de portail captif.
Deux détails qui comptent pour le débit: le sous-domaine doit être court (chaque caractère économisé sur le nom est un caractère gagné pour les données) et la cible du NS doit être un nom avec un enregistrement A, pas un CNAME.
iodined, le serveur
$ ./iodined -f 10.0.0.1 tunnel.mondomaine.fr
Le premier argument est l’IP de l’extrémité serveur du tunnel (une plage que vous n’utilisez pas déjà, le couple classique 10.0.0.1/10.0.0.2 fera l’affaire), le second est le sous-domaine délégué. iodined ouvre une interface tun et écoute sur le port 53/udp. S’il y a déjà un serveur DNS sur la machine, le manpage explique comment changer son port d’écoute et faire forwarder par iodined, mais sur une dedibox vierge, le port est libre.
Le client, derrière le portail
$ ./iodine resolver.du.portail.fr tunnel.mondomaine.fr
Le premier argument est le resolver relais, c’est-à-dire celui du portail (celui que cat /etc/resolv.conf vous montre), le second votre sous-domaine. iodine ouvre son interface tun et le client prend l’IP suivante dans la plage: 10.0.0.2. Le rituel de vérification, tel quel:
$ ping 10.0.0.1
Si le ping répond, vous avez une interface IP qui fonctionne dans un réseau qui ne vous laissait qu’un filtre web. La version 0.3.1 ne consulte pas encore /etc/resolv.conf toute seule: passez le resolver du portail en premier argument, c’est fait pour.
Ce qui passe dans le tuyau
Le sens asymétrique est la signature du DNS tunneling. À la montée, les données sont compressées (gzip), encodées en base32 et découpées dans les labels du nom de requête: 63 caractères par label, 255 par nom, une seule requête par paquet. C’est le sens lent et il le sera toujours: le DNS n’a pas été pensé pour ça.
À la descente, iodine répond aux requêtes avec des enregistrements de type NULL, qui n’ont pas de format imposé et donc pas d’encodage: chaque réponse DNS peut bourrer plusieurs centaines d’octets de charge utile. C’est le sens rapide, celui par lequel votre web passe.
Le tunnel n’est PAS chiffré et le README le dit lui-même: le tunneling au-dessus est de mise. Le couple gagnant:
$ ssh -D 1080 user@dedibox
Un SOCKS local au bout du tunnel et le web du hotspot passe par votre propre sortie. La variante puriste: route par défaut à travers le tunnel (une route host vers le resolver du portail d’abord, sinon le tunnel lui-même devient injoignable, puis la default via 10.0.0.1) et NAT sur la dedibox. La variante web seulement: un Privoxy sur le serveur et le navigateur pointe dessus.
Les limites, honnêtement
Pas d’authentification. La version 0.3.x ne connaît aucun mot de passe: celui qui découvre votre sous-domaine peut se brancher sur votre tunnel. La seule barrière actuelle est l’obscurité du nom plus une règle iptables sur l’IP source si votre client ne bouge pas. L’authentification est la première demande ouverte du projet; en attendant, ne publiez pas le sous-domaine et traitez le tunnel comme un tuyau ouvert.
Les relais détruisent des tunnels. Certains résolveurs basculent les noms longs en minuscules (mort du codec sensible à la casse), d’autres refusent les requêtes de type NULL, d’autres répondent si lentement que le débit s’effondre. C’est le risque numéro un du domaine et il ne se teste qu’en conditions réelles, d’où le dig préliminaire.
C’est visible. Un tunnel DNS, c’est un flot continu de requêtes vers un seul domaine, avec des hostnames de 255 caractères à base quasi aléatoire. Un admin qui regarde les logs de son resolver le voit en une minute. Ici l’exercice est de ressortir d’un portail captif d’hôtel, pas de faire passer un réseau d’entreprise sous silence; dans le second cas, il faudrait parler de port 53 bloqué par la politique et l’article serait un autre article.
Les CGU du hotspot. Une ligne de plus que personne ne lit, celle qui dit que le tunneling n’est pas couvert. L’exercice reste scolaire: comprendre qu’un filtre qui laisse passer un protocole le laisse passer tout ce que ce protocole peut porter.
Le kit
Screen pour garder le tunnel ouvert dans un terminal qui survit au logout, dig pour tous les tests, netcat pour vérifier que ce qui passe au bout répond, ssh -D pour la sortie chiffrée, tcpdump ou ethereal pour regarder les paquets si ça ne marche pas. Rien d’exotique: le kit du week-end.
Le portail captif vous donne le DNS. Le DNS est un protocole de transport. Donc le portail captif vous donne l’internet. Le reste n’est que de l’encodage.
[edit: 2026] Vingt ans plus tard, iodine tourne toujours. La version 0.4.0 “Run Home” (mars 2007) a apporté l’authentification par mot de passe que cet article réclamait, la 0.5.2 “WifiFree” (juin 2009) a avoué le cas d’usage dans son propre nom de code, la 0.6.0 a ajouté les replis TXT/SRV/MX/CNAME et le mode UDP brut pour les réseaux où le port 53 sort en direct, la 0.7.0 “Kryptonite” (2014) un type d’enregistrement PRIVATE dans la plage d’usage privé pour les serveurs qui acceptent RFC 3597 et corrigé une faille d’authentification signalée par Oscar Reparaz. La 0.8.0 est sortie en 2023, le projet est dans toutes les distributions et dans le livecd Kali.