La Lanterne Rouge

Warning: Geek Inside

OpenVPN, NETMAP et masquerading

- Posted in Sans catégorie by

J'ai récemment découvert le principe concret du VPN et qu'il n'était pas si difficile d'en mettre un en place entre plusieurs machines. Bien que le concept ne m'était pas étranger, je n'avais jamais pris le temps de me documenter sur le sujet, car n'ayant alors aucune motivation à le faire. Puis, l'intérêt s'est finalement révélé lorsque j'ai voulu planifier des backups de mon PC principal vers mon serveur distant.

Je voulais un logiciel simple d'utilisation, graphique comme toute application Windows se doit d'être (oui mon PC principal est encore sous XP) et efficace, offrant notamment des sauvegardes complètes, incrémentales et différentielles. Après moultes recherches, je n'ai pu trouver aucun soft libre potable possédant les qualités que je viens d'énumérer tout en supportant un protocole de transfert chiffré (comme SFTP).

Cependant, tous offraient de base le support FTP, ce qui est tout de même le minimum. Mais FTP dit transferts en clair, et mon serveur étant relié à la box par wifi (pas par choix, mais par nécessité), l'idée ne me plaisait pas du tout. Je me suis donc attelé à la mise en place d'un VPN, et me suis vite retrouvé confronté au problème des plages IP similaires côté serveur et côté client.

Au gré de mes bidouillages (même si c'est un grand mot), j'ai pu découvrir une méthode simple permettant à un serveur VPN configuré en mode route de servir de passerelle transparente en présentant les machines sur son propre réseau local via des adresses IP virtuelles, en utilisant une petite astuce NetFilter/iptables, assez peu répandue d'après mes recherches.

Je reviens donc sur les aspects classiques du VPN, et de cette petite manip sur iptables qui vaut son pesant de cacahuètes, j'ai nommé : NETMAP.

Le VPN : Quézac'eau ?

Oui je suis assez fier de mon jeu de m'eau.

Petit rappel : le VPN ou "Réseau Privé Virtuel", en vieux françois du Sud, représente un assemblage de techniques permettant de créer – comme son nom l'indique – un réseau privé virtuel.

  • Réseau, parce qu'il s'agit d'un ensemble de machines interconnectées et directement atteignables entre elles (ou au moins depuis la machine serveur)
  • Privé, parce que toutes les machines chiffrent les données au moyen d'une clé délivrée par le serveur (point central du VPN) avant de les transmettre, et empêchent ainsi l'analyse des échanges par toute machine intermédiaire (même un autre client ne le pourrait pas),
  • Virtuel, parce que le réseau reste une vue logique d'un ensemble de machines, celles-ci pouvant être physiquement sur des réseaux locaux très différents, distants les uns des autres; la seule nécessité étant de pouvoir contacter le noeud du VPN (le serveur) sur le port d'écoute correspondant.

http://upload.wikimedia.org/wikipedia/commons/0/0f/VPN_site-to-site.jpg

Un logiciel très connu pour gérer un serveur VPN et ses clients est OpenVPN. C'est celui-ci que j'ai utilisé car libre, disponible sous Windows et GNU/Linux, et très bien documenté.

OpenVPN

En résumé, comment ça marche ? Ou plutôt, quel est le résultat observable concrètement ?

Les machines clientes sur le VPN communiquement tout simplement de manière transparente avec le serveur, comme si celui-ci était situé à 2 mètres, seulement relié à elles par un câble Ethernet (bon, le débit en moins évidemment). Cela signifie par exemple qu'il est possible de contacter le serveur sur n'importe lequel de ses ports, même si la machine locale est sur un réseau local (un vrai) dont le firewall n'autorise pas les connexions sortantes sur le port choisi. Moi ça déjà, ça me bluffe. C'est très bête, très simple quand on y réfléchit, mais c'est puissant.

Je ne vais pas détailler ici l'installation d'un serveur ni d'un client OpenVPN. Sachez simplement que le tutorial disponible sur ubuntu-fr est très bien fait et vous facilitera grandement la tâche si vous ne savez pas par quoi commencer : http://doc.ubuntu-fr.org/openvpn

Des routes et des ponts

Concernant ma configuration, j'utilise le mode route. Dans ce mode, le VPN géré par le serveur est un réseau logique autonome dont les clients ne peuvent "sortir".

À opposer au mode bridge qui correspond en fait à se servir du serveur comme d'un vulgaire switch (commutateur) réseau sur une interface physique existante (ex : eth0), les clients du VPN étant alors sur le même réseau.

En gros, en route les clients sont sur un réseau purement virtuel, sans "pont" avec un réseau réel, alors qu'en bridge les clients sont sur le réseau local du serveur, simplement.

Je rassure tout de suite ceux qui ont peur de faire le mauvais choix : aucune décision n'est inaliénable. Vous pouvez très facilement convertir un VPN route en bridge et inversement.

Pour l'explication du "pourquoi le mode route" sur ma configuration, et donc "pourquoi ce post", lire plus loin.

Serveur, l'addition svp

Mais revenons à notre client. Une fois connecté au serveur, cela se traduit par une nouvelle interface réseau physique (en fait virtuelle) disponible depuis votre OS. Sous GNU/Linux, un petit

# ifconfig -a

vous affichera quelque chose comme

tun0      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
          inet adr:10.8.0.6  P-t-P:10.8.0.5  Masque:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 lg file transmission:100
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

Cette interface au prefixe "tun" bizarre est l'entrée de notre tunnel vers le serveur VPN. Tout ce qui est envoyé par cette interface (= à une adresse IP sur ce réseau) est chiffré puis transféré au serveur. Lui seul pourra ensuite déchiffrer les paquets avant de les traiter. Le serveur a donc lui-aussi une interface similaire lui permettant de communiquer avec les clients.

À ce stade on peut donc faire un ping sur l'adresse du serveur (par défaut 10.8.0.1) et le TTL résultant nous indiquera bien que nous y accédons directement, même si notre serveur est à Perpète-les-Oies et qu'il a fallu traverser la moitié de la planète pour l'atteindre. On peut surtout s'y connecter avec un protocole non-chiffré (FTP, Samba/CIFS) sans risque, ce qui est bien l'intérêt premier du VPN.

Par défaut, seul le serveur est accessible. Si un autre client se connecte à notre VPN (parce qu'on lui a créé une clé), il pourra nous donner son adresse IP VPNisée et on pourra tenter un ping mais cela ne répondra jamais. La directive suivante dans le fichier de configuration du serveur est en effet commentée par défaut :

client-to-client

Si on la décommente on pourra alors voir les autres machines connectées et communiquer avec elles, comme si elles étaient sur le même réseau local (Petite parenthèse : c'est notamment hyper-intéressant pour les vieux jeux qui ne connaissaient pas encore Internet à l'époque de leur conception et ne fonctionnaient qu'au niveau LAN. (Petite sur-parenthèse : seul bémol, le broadcast reste impossible)).

Plus on est de foo...

On voit donc maintenant tout le monde sur votre VPN. Super cool. Si si j'insiste.

Mais imaginons la configuration suivante, configuration qui n'est autre que la mienne, et qui m'a poussée à parcourir la Toile à la recherche d'un temps que je n'ai finalement pas perdu :

  • Un serveur derrière une box, sur un LAN en 192.168.1.0/24 (ça, c'est chez mon hébergeur(1))
  • Un client derrière une box, sur un LAN aussi en 192.168.1.0/24 (ça, c'est chez moi)

Les box en question elle sont bien gentilles, mais dans leur immense générosité, leur FAI n'a pas pensé à intégrer la possibilité de changer de plage réseau(2) (passer en 192.168.2.0/24 par exemple).

Résultat : un bon gros conflit d'adresses si jamais on bridge un VPN sur le serveur et que le client s'y connecte. Intérêt zéro. Seule solution, rester en mode route pour avoir une plage réseau totalement arbitraire et tant qu'à y être différente de celles imposées par les box.

Oui mais.

Oui mais alors ça veut dire qu'on peut faire une croix sur l'accès aux machines sur le LAN du serveur. Oui mais... non !

Contournage en règle

En règle iptables bien sûr (ha ha). Bon, trève de galéjades, j'ai décomposé le processus en 4 étapes présentées ci-après.

Masque réseau VPN

On va tout d'abord éditer le fichier de configuration de notre serveur VPN et changer le masque attribué au réseau.

server 10.8.0.0 255.255.255.0

devient

server 10.8.0.0 255.255.0.0

Ce qui nous permet d'élargir la plage d'adresses IP correspondante, mais surtout – car on n'aura peu probablement plusieurs dizaines de milliers de machines connectées (corrigez-moi si je me trompe) – on améliore surtout la lisibilité pour les prochaines étapes.

Hormis une petite déconnexion/reconnexion pour mettre à jour le masque, aucune modification n'est nécessaire côté client, c'est bien l'avantage.

IP Forward

Revenons à notre serveur :

echo 1 > /proc/sys/net/ipv4/ip_forward

Ça c'est pour transformer notre serveur GNU/Linux en routeur réseau surpuissant avec des réacteurs dans le dos (si vous ne voyez pas les réacteurs, prière de contacter le SAV de votre ordinateur).

Comme on veut que cette modification soit permanente, on va aussi éditer /etc/sysctl.conf et décommenter la ligne (ou l'ajouter si elle n'existe pas)

net.ipv4.ip_forward=1

pour ne pas avoir à retaper la première ligne à chaque reboot.

À partir de ce moment, notre serveur sait relayer les paquets IP. Il saura donc, si on le lui dit, que les paquets ne lui étant pas expréssément destinés arrivant par une interface doivent être renvoyés sur une autre (par exemple). Notre machine n'est plus un simple cul-de-sac pour les paquets mais un noeud intelligent au sein du réseau (enfin, c'est vite dit...).

NETMAP

Là c'est le gros morceaux. C'est THE killing feature dans iptables.

Grâce à une petite règle, on va dire à notre serveur GNU/Linux que :

  • AVANT de router les paquets (PREROUTING, avant de décider sur quelle interface les renvoyer)
  • SI leur adresse de destination est en 10.8.1.0/24 (par exemple 10.8.1.1)
  • ALORS on va altérer leur en-tête et remplacer les 3 premiers octets de leur adresse de destination par 192.168.1.

Cette règle, c'est celle-ci :

iptables -t nat -A PREROUTING -d 10.8.1.0/24 -j NETMAP --to 192.168.1.0/24

Maintenant, l'explication.

Lorsqu'on reçoit un paquet IP marqué comme étant destiné à une adresse sur la plage 10.8.1.0/24 (-d 10.8.1.0/24) on va remplacer cette adresse par son équivalent sur la plage 192.168.1.0/24 (--to 192.168.1.0/24).

Si on utilise 10.8.1.0/24 c'est pour que les paquets partent inévitablement vers le serveur depuis le client. En effet, pour le client, cette adresse est sur le réseau du VPN (qui est en 10.8.0.0/16), elle va donc être envoyée par le tunnel. Mais on aurait pu ajouter une route supplémentaire "pushée" lors de la connexion au serveur. Il suffit ensuite d'être cohérent tout le long du processus. Là c'est purement par facilité (et flemme).

Exemple :

L'adresse 10.8.1.1 envoyée depuis un client sur le VPN devient 192.168.1.1, qui est une adresse existante sur notre réseau local côté serveur (en l'occurrence, la box). Et ceci avant de router le paquet. Ce qui fait que le paquet lors du routage, avec sa nouvelle adresse de destination, va être renvoyé sur notre interface connectée au réseau local (wifi ou Ethernet) et va atteindre notre machine en 192.168.1.1 ! Ha ha ! Notre Destin va enfin s'accomplir ! L'Histoire retiendra notre nom et le glorifiera pour les millénaires à venir !... heu, je m'emporte.

Je m'emporte d'autant plus que l'on va vite s'apercevoir qu'il nous manque encore un morceau, dans l'histoire.

Masquerade

Notre paquet, il a bien son adresse de destination qui a été "traduite" par notre serveur, et va donc pouvoir atteindre la machine sur le réseau local. Fort bien. Mais son adresse source ? Hein ? Eh oui, elle porte encore son adresse originale du client sur le VPN, par exemple 10.8.0.6.

Et la machine destinataire, elle le connait ce réseau en 10 point 8 point quelque chose ? Point du tout. Fi donc, par la malepeste, messire voilà nostre paquet perdu (poil au c... rohhh).

En effet, la machine destinataire ne connait pas la machine 10.8.0.6 et ne saura pas comment lui répondre. Elle va donc ignorer le paquet après réception. Comment alors faire en sorte qu'elle puisse répondre correctement afin que la réponse revienne bien à notre client sur le VPN ?

On revient à notre iptables, et on va se servir du masquerading.

Le masquerading permet au serveur de substituer l'adresse source du paquet par la sienne avant de le renvoyer. Mais également, grâce à une table des connexions en cours, de bien refaire l'opération inverse lorsqu'un paquet est reçu.

Voici la dernière commande :

iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -d 192.168.1.0/24 -j MASQUERADE

Ici on précise qu'après le routage (POSTROUTING), on veut "masquer" (remplacer) l'adresse source sous la nôtre (par celle du serveur) si celle-ci est en 10.8.0.0/24 (typiquement, une machine sur le VPN) et si la destination est en 192.168.1.0/24 (une machine sur le réseau local), ce qui correspond bien à notre situation. Et voilà, les paquets vont et viennent librement comme si Schengen s'appliquait subitement à notre réseau !

Enfin, un Schengen bizarre qui fait que la machine sur le LAN sait répondre à une machine sur le VPN, mais pas la contacter directement (la nuance est importante). Mais dans notre cas, ça remplit l'objectif.

On peut désormais, en tant que client connecté sur le VPN, accéder à toute machine sur le réseau local du serveur, en utilisant la plage d'adresses "virtuelle" 10.8.1.0/24, tout en gardant notre configuration en mode route.

Cela signifie, et c'était un de mes objectifs à l'origine, qu'il est possible d'accéder aux interfaces de chaque box indépendamment, en utilisant le classique 192.168.1.1 pour celle sur notre réseau local côté client, et 10.8.1.1 pour accéder à celle du côté du serveur, sans rien changer ni sur l'une ni sur l'autre.

Et 4 qui nous font 5

En fait c'est un point complémentaire en bonus vu que j'ai dit que ça ne ferait que 4 étapes.

Il convient de faire quelques vérifications dans le cas où cela ne fonctionnerait pas du premier coup. Je suis tombé dans ce cas-là personnellement.

Bon, moi je suis un gros flemmard et je reste trop dépendant des interfaces graphiques pour me faire passer pour un puriste en gérant NetFilter/iptables en ligne de commandes. À la place j'utilise Webmin et je ne pense pas me tromper en disant que c'est une des meilleures interfaces qui existe aujourd'hui pour cet outil.

Webmin_firewall.png

Le truc c'est que j'utilise un paquet de règles prédéfinies permettant d'éviter, ou tout du moins de limiter les attaques DoS et Ping-of-Death. Les connexions sont aussi autorisées au cas par cas, et ignorées si la règle n'est pas présente.

L'ajout d'un service, comme le VPN, m'oblige à ajouter une règle autorisant explicitement les connexions sur le port 1194. Sinon, ben je peux chercher longtemps mais ça ne marchera jamais.

Il est aussi nécessaire d'autoriser les connexions selon les interfaces et le sens. Par exemple moi j'avais oublié de préciser qu'il fallait autoriser les connexions wlan0->tun0 et inversement. C'est très con mais ça arrive. Heureusement les logs du kernel (/var/log/kern.log) notent bien les paquets rejetés pour peu qu'on ait ajouté cette règle dans iptables. Il suffit alors de monitorer ce fichier pendant les tests et on résout souvent rapidement le problème.

Épilogue

Pas de conclusion particulière au final, si ce n'est qu'en cherchant un peu et en essayant de se plonger un peu dans le fonctionnement basique du réseau on peut arriver à ses fins.

Je ne crois pas avoir oublié de détail capital ni avoir dit trop de conneries dans ce post, mais si c'est le cas je suis prêt à le corriger. Je l'ai simplement écrit parce que je n'avais trouvé aucun équivalent sur le Web, et que j'ai pensé que ça pourrait être utile. Puis moi ça me fait un bon mémo si jamais je veux le refaire sur une autre bécane plus tard :)

(1) Qui n'est autre que ma mère ! Garanti pas cher, et c'est le seul qui vous appelle toutes les semaines pour savoir si vous allez bien et êtes satisfait du service ! ^^

(2) Pour la petite histoire, j'ai découvert bien après qu'il est possible de changer cette fameuse plage IP définie par la box en accédant à une page cachée de l'interface (sur la NeufBox en tout cas, car oui ce sont des NeufBox dont je parle). Mais avouez que c'est quand même vachement moins drôle que de geeker avec les bidouilles expliquées ici.