Réseau

[Astuce] Accès distant avec OpenVPN

0

En voulant configurer un serveur d’accès distant avec OpenVPN, je suis tombé dans quelques petits pièges. Aussi, pour que mon expérience puisse servir à d’autres, autant vous en faire part.

Contexte

C’est très simple, presque bateau : un serveur, avec OpenVPN, des clients, avec OpenVPN également et quelques petites subtilités côté serveur. Les clients se connectent au serveur et accèdent à des ressources locales au serveur mais aussi à l’infrastructure qui se trouve derrière. Imaginez que l’infrastructure est importante et que le serveur serve de bordure à un protocole de routage dynamique.

Première astuce : les types de tunnels

D’abord, j’ai fait l’erreur de laisser les clients se connecter via un VPN TAP. Bien que ça fonctionne, ce n’est pas vraiment adapté en pratique. Sommairement, les tunnels de type TAP sont principalement conçus pour des connexions de site à site, et les tunnels de type TUN servent aux accès distant pour un terminal (ordinateur, téléphone, tablette…). Bref, on repasse en TUN pour le cas qui m’intéresse, c’est-à-dire de l’accès en itinérance de terminaux.

Deuxième astuce : la topologie des tunnels de type TUN

Les interfaces de type TUN fonctionnent au niveau 3 du modèle OSI, mais en fait c’est un montage un peu complexe quand on y regarde de très près mais qui fonctionne très bien. Sauf bien évidemment quand on utilise déjà des astuces de routage ou de pare-feu, ce qui était le cas sur mon serveur.

Regardons justement plus en détail. Sur une extrémité de tunnel TAP, on trouve une interface réseau virtuelle, possédant son adresse IP et son masque, et un pair en face. On est donc pas bien loin du tunnel GRE si on enlève le chiffrement qui va autour, dans le principe bien sûr.

Pour une extrémité TUN, il faut tenir compte de la notion de topologie. Par défaut, un serveur TUN démarre avec une topologie dite « net30 », soit assignation d’un sous-réseau /30 (2 IP possibles, le serveur et le client). Et pour que le client communique avec le serveur (au sens applicatif, par exemple un serveur Web abrité derrière la connexion VPN), il faut que le noyau puisse recevoir les paquets, et il n’y a qu’une seule interface virtuelle qui est montée. Dans ce cas, on va tomber sur une interface TUN déclarée dans le noyau avec une adresse IP /32, une route injectée « manuellement » par OpenVPN pour tout le sous-réseau, et un routage strictement interne à OpenVPN entre les clients. Obscur hein ! Décryptons avec un dessin.

Ce schéma représente le principe de fonctionnement d’une topologie Net30. Le serveur réel (le noyau du système d’exploitation) voit une unique adresse IP en 192.168.90.1/32, tout le reste étant masqué par le processus OpenVPN. Chaque client voit sa propre adresse IP dans un sous-réseau dédié par OpenVPN. Par exemple le client ayant l’adresse 192.168.90.6/30 discutera avec le serveur via l’adresse 192.168.90.5/30 et ne pourra pas communiquer avec le reste du monde, sauf à lui « pousser » des routes.

Il faut bien comprendre qui va tenir le rôle de routeur : est-ce le serveur OpenVPN ou est-ce le noyau de l’OS ? La réponse dépend de la configuration : avec l’option « client-to-client », c’est OpenVPN qui s’occupera du routage, sinon c’est le noyau.

Avec un Net30 et une plage réduite dans la configuration d’OpenVPN, disons 192.168.90.0/29, on peut avoir un serveur et un client (et un seul), et instancier autant de processus OpenVPN que nécessaire, et donc autant d’interfaces dans le noyau. Ca peut éventuellement servir pour appliquer des règles de pare-feu très fines entre clients ou différentes d’un client à l’autre.

Mais c’est peut-être un peu beaucoup… Et c’est pas tout à fait ça que je cherchais. Tout simplement parce que l’interface déclarée en tant que 192.168.90.1/32 crée du souci à mon protocole de routage, même avec la route « manuelle » évoquée plus haut qui serait ici 192.168.90.0/24. Dans mon cas, le protocole choisit le masque le plus restreint plutôt que la route statique, même en lui forçant la main.

Et c’est là qu’entre en jeu le paramètre « topology ». Moyennant de lui indiquer le mode « subnet », on retombe sur le même principe de fonctionnement que les interfaces TAP, et le noyau verra de nouveau l’interface virtuelle en tant que 192.168.90.1/24. Et voilà que mon routage s’est remis d’aplomb presque instantanément !

Troisième astuce : tout ça pour le client OpenVPN sur Android

Le client OpenVPN pour Android ne supporte pas les tunnels TAP ! C’est de là que tout est parti. Quand, lorsque j’ai bêtement converti mon serveur de TAP vers TUN, j’ai vu surgir tous ces problèmes de topologie mais aussi de routes et de DNS !

Parce que malgré un « ip route » sur un teminal Android (7.1.2), impossible de voir les routes poussées par le serveur dans le noyau Linux de l’Android. Même en root. Alors qu’elles sont bien présentes et bien inscrites par OpenVPN. Bref, que faut-il alors faire pour que ça fonctionne ?

Sur le serveur, il faut pousser les routes et éventuellement si c’est nécessaire (comme dans mon cas) les serveurs DNS. On placera donc les éléments suivants, que vous aurez bien évidemment pris soin d’adapter à votre cas, dans la configuration côté serveur :

push "route 10.0.0.0 255.0.0.0"
push "dhcp-option DOMAIN labcellar.com"
push "dhcp-option DNS 10.0.0.10"
push "dhcp-option DNS 10.0.0.11"

Je ne veux rediriger que le trafic interne dans le tunnel, c’est pouquoi je ne fais que pousser la route adaptée. Vous trouverez sur Internet pléthore de sites vous expliquant comment rediriger l’intégralité du trafic dans le tunnel. Ici, c’est l’accès aux outils internes qui m’intéresse, et la résolution des noms. Côté client Android, il faut bien penser à laisser l’option « pull » activée afin que soient pris en compte tous ces paramètres « pushés » par le serveur. Et n’oubliez pas dans les versions récentes d’Android d’aller régler les « Applications autorisées », soit en mode « liste blanche » (seulement les applications cochées), soit en mode « liste noire » (interdire les applications cochées).

En fait, au delà de l’astuce de configuration, il faut savoir que le seul moyen d’avoir des informations de débogage consiste à augmenter la « verbosité » du logiciel OpenVPN et à faire confiance au fait qu’il affiche ou non des erreurs à chacune de ses actions. Demander ces informations directement au noyau Android aboutit à n’avoir que des informations erronées ou incomplètes si l’on tente de procéder comme sur un ordinateur classique.

Références

Il y a une petite coquille dans cet article (qui n’est pas liée à la syntaxe ou à la grammaire mais bien sur le sujet de fond), et qui n’a pas d’impact sur toutes les explications données, ni sur la solution mise en œuvre. Je l’ai laissée (presque) exprès. Saurez-vous la retrouver ? J’éditerai cet article dans un mois environ pour indiquer la solution.

Edit du 09/04/2018 : si vous avez bien suivi les explications sur la topologie Net30, vous avez peut-être remarqué qu’il y a une incohérence dans le schéma que j’ai proposé. En effet si le pseudo-serveur qui émule le switch interne qui relie les tunnels entre eux s’attribue d’abord une adresse avant d’en attribuer une à son client, ça signifie que la patte en haut à gauche qui représente la machine physique ne contient pas les bonnes adresses. Côté switch, c’est l’adresse .1 et côté serveur physique, c’est .2. Et c’est effectivement le cas : l’adresse visible avec un « ifconfig » ou un « ip address » est bien une valeur paire, et généralement .2 pour un subnet /24 divisés en morceaux de /30.

[Mémo] Démarrer des instances OpenVPN avec systemd

0

Remarque préliminaire : cet article traite du démarrage d’instances d’OpenVPN sur un système Linux, ici une Debian Stretch, avec systemd. Il ne traite pas de la manière dont on configure une instance OpenVPN (paramètres et clés).

Avec SysV, un démon maître était lancé et chaque instance à démarrer automatiquement se faisait par configuration dans le fichier « /etc/default/openvpn » (du moins sur les Debian-like).

systemd est conçu pour gérer nativement ces problématiques de multi-instances, alors autant en profiter.

Contexte

Supposons que votre ordinateur portable nécessite d’être raccordé à trois infrastructures distantes via VPN, dont une de manière permanente. Imaginez que vous êtes un prestataire qui prend le contrôle à distance chez ses clients, tout en gardant en permanence un lien vers son serveur NAS à la maison,

On a donc 3 configurations différentes que nous appellerons « maison », « client-a » et « client-b ». Ces trois configurations et tous les fichiers nécessaires (notamment les clés) se placent dans leurs dossiers respectifs. Ce qui donne :

$ ls /etc/openvpn
client   client-a   client-b   maison   server

Cela dit, on remarque la présence de deux dossiers supplémentaires appelés « client » et « server ». Ils sont liés au fonctionnement prédéfini par les gabarits d’unités systemd pour OpenVPN 2.4.

Mise en œuvre

Pour chaque dossier créé, on trouve un certain nombre d’éléments, dont le fichier de configuration. Voici un exemple :

$ ls /etc/openvpn/maison
maison.conf   shared.key

Remarque : on pourrait tout bourrer comme des sagoins dans le dossier « client », mais je ne trouve pas ça très propre et très maintenable si les tunnels se multiplient.

Cependant, c’est bien le dossier « client » qui va faire foi dans un premier temps, vu la manière dont le gabarit est construit. Un simple lien symbolique suffit :

# ln -s /etc/openvpn/maison/maison.conf /etc/openvpn/client/maison.conf

Le gabarit n’est pas amorçable directement, il faut nommer explicitement une instance. Ici le fichier s’appelle « maison.conf ». Inscrire l’instance « maison » (qui ira bien chercher « maison.conf » dans le dossier « client ») est enfantin :

# systemctl enable openvpn-client@maison
Created symlink /etc/systemd/system/multi-user.target.wants/openvpn-client@maison.service → /lib/systemd/system/openvpn-server@.service.

Désormais, le VPN vers la maison se lance automatiquement au démarrage du système !

Et pour lancer les autres instances, pas besoin de les inscrire, systemd se débrouille à partir du nom indiqué pour retrouver ses petits.

# systemctl start openvpn-client@client-a

Remarques

Le paquet OpenVPN exploite donc désormais les services systemd, et non plus les scripts de type UNIX System V. Pour bien décortiquer l’affaire, il faut donc comprendre le fonctionnement des « unités » systemd. La petite bête réside dans ce paragraphe (en langue de Shakespeare) :

Optionally, units may be instantiated from a template file at runtime. This allows creation of multiple units from a single configuration file. If systemd looks for a unit configuration file, it will first search for the literal unit name in the file system. If that yields no success and the unit name contains an « @ » character, systemd will look for a unit template that shares the same name but with the instance string (i.e. the part between the « @ » character and the suffix) removed. Example: if a service getty@tty3.service is requested and no file by that name is found, systemd will look for getty@.service and instantiate a service from that configuration file if it is found.

Soit en gros : il détecte la présence d’un arobase, donc il suppose une instanciation. Il extrait le nom de l’instance entre le ‘@’ et le ‘.’, et lance le gabarit (reconnaissable au ‘@.service’) avec en paramètre le nom de l’instance.

Le paquet lui-même installe ces fichiers :

# ls -l /lib/systemd/system/openvpn*
-rw-r--r-- 1 root root 707 juil. 18 22:15 /lib/systemd/system/openvpn-client@.service
-rw-r--r-- 1 root root 780 juil. 18 22:15 /lib/systemd/system/openvpn-server@.service
-rw-r--r-- 1 root root 320 juil. 18 22:15 /lib/systemd/system/openvpn.service
-rw-r--r-- 1 root root 894 juil. 18 22:15 /lib/systemd/system/openvpn@.service

Dans le fichier « openvpn-client@.service », on trouve ces directives (parmi d’autres) :

WorkingDirectory=/etc/openvpn/client
[...]
ExecStart=/usr/sbin/openvpn --suppress-timestamps --nobind --config %i.conf

Et voilà grosso modo comment systemd s’y retrouve pour lancer des instances différentes à partir d’un même fichier. Même principe pour le gabarit « openvpn-server ».

Et les deux derniers ? Ce sont les versions pour OpenVPN 2.3, que je recommande d’abandonner si vous le pouvez. Ces instances cherchent leur configuration dans le dossier « /etc/openvpn » et sont lancées de manière significativement différentes. Si vous n’êtes pas un expert OpenVPN, ou ne recherchant pas explicitement ces détails de lancement, vous pouvez les abandonner sans regret. Sachez qu’un OpenVPN 2.4 peut parfaitement discuter avec un OpenVPN 2.3 moyennant de les laisser s’entendre sur les algorithmes et certains paramètres nouvellement introduits : c’est testé et approuvé !

systemd, faut en manger ! C’est bon ! (Mais des fois ça croque un peu sous la dent…)

Go to Top