J'ai un serveur Wireguard fonctionnant chez moi pour pouvoir accéder à mon réseau domestique depuis l'extérieur. Wireguard utilise UDP. Malheureusement, je ne peux pas simplement ouvrir un port UDP IPv4 dans ma boîte fritz comme cela fonctionnait autrefois car mon fournisseur m'a mis derrière CG-NAT, je dois utiliser IPv6 mais IPv6 n'est pas encore utilisable sur mon lieu de travail.
Donc, à la maison, j'ai IPv6 et au travail, j'ai IPv4 et aucun moyen d'accéder à mon réseau domestique.
J'ai également un VPS avec un IPv4 public sur lequel je pourrais établir des tunnels SSH inversés pour le trafic TCP. Malheureusement, les tunnels ssh ne prennent en charge que TCP et de toute façon, il ne faut pas établir de connexions VPN via TCP, raison pour laquelle Wireguard utilise uniquement UDP.
VPN-Server VPN-Client
(UDP) | | (IPv4 UDP)
| | | not possible |
-----------|--------| <-------------------
| |
NAT CG-NAT
VPN-Server VPN-Client
(UDP) (IPv4 UDP)
| |
| |
inside agent outside agent
tunnel tunnel
|| | | ||
|| punch| punch| ||
| --------->|-------->|-------------------- |
-----------|<--------|<--------------------
| |
| |
NAT CG-NAT
L'agent externe s'exécute sur un VPS avec IPv4 public
L'agent interne enverra des datagrammes UDP à l'adresse IP publique et au port de l'agent externe, cela percera des trous dans les deux NAT, l'agent externe recevra ces datagrammes et apprendra de son adresse source et de son port comment renvoyer les datagrammes à l'agent interne. .
Le client VPN peut envoyer UDP à l'agent externe et ces datagrammes seront transmis à l'agent interne et de là au serveur VPN, le serveur VPN peut répondre à l'agent interne, ceux-ci seront transmis à l'agent externe et de là à le client VPN.
L'agent externe apparaîtra comme serveur VPN pour le client et l'agent interne apparaîtra comme client VPN pour le serveur.
Plusieurs connexions client sont possibles car elles utiliseront un tunnel et un nouveau socket sur l'agent interne pour chaque nouveau client se connectant à l'extérieur, de sorte qu'au serveur, il apparaîtra comme si plusieurs clients s'exécutaient sur l'hôte de l'agent interne.
Clonez ou décompressez le code sur les deux machines et créez-le. Vous avez besoin au moins de make et gcc. Entrez le répertoire source et utilisez la commande
$ make
Après une construction réussie, vous vous retrouvez avec le udp-tunnel
binaire dans le même dossier. Vous pouvez désormais soit le démarrer directement depuis un terminal (avec les bonnes options bien sûr) pour faire quelques tests rapides, soit l'installer à l'aide du makefile.
Le udp-tunnel
binaire compilé peut agir soit en tant qu'agent interne, soit en tant qu'agent externe, en fonction des options que vous transmettez sur la ligne de commande.
Supposons, à titre d'exemple, que le serveur VPN écoute sur UDP 1234, s'exécute sur localhost (identique à l'agent interne) et que la machine externe est jump.example.com et nous voulons qu'elle écoute sur le port UDP 9999.
Sur l'hôte intérieur, nous le démarrons avec
$ ./udp-tunnel -s localhost:1234 -o jump.example.com:9999
Sur l'hôte extérieur, nous le démarrons avec
$ ./udp-tunnel -l 9999
Le makefile contient 3 cibles d'installation : install
pour installer uniquement le binaire, install-outside
et install-inside
pour installer également les fichiers du service systemd. Les deux derniers ont besoin de variables transmises à make pour fonctionner correctement.
Pour installer l'agent externe sur l'hôte de saut (en supposant que vous souhaitiez le port 9999), exécutez cette commande :
$ sudo make install-outside listen=9999
Cela installera le binaire dans /usr/local/bin/
et installera un fichier de service systemd dans /etc/systemd/system/
contenant la commande nécessaire pour le démarrer en mode agent externe avec le port 9999.
Pour installer l'agent interne sur la machine interne, utilisez la commande suivante (en supposant, à titre d'exemple, que votre serveur VPN est localhost:1234 et que votre hôte de saut est jump.example.com) :
$ sudo make install-inside service=localhost:1234 outside=jump.example.com:9999
Cela installera à nouveau le binaire dans /usr/local/bin/
et un fichier d'unité systemd dans /etc/systemd/system/
À ce stade, vous souhaiterez peut-être jeter un coup d'œil rapide aux fichiers de l'unité systemd pour voir comment le binaire est utilisé et vérifier si les options sont correctes. Les options devraient ressembler à celles décrites ci-dessus dans le test rapide.
Une fois les fichiers systemd installés et confirmés comme étant corrects, ils ne sont pas encore activés pour le démarrage automatique, vous devez les activer et les démarrer. Sur la machine intérieure :
$ sudo systemctl enable udp-tunnel-inside.service
$ sudo systemctl start udp-tunnel-inside.service
et sur la machine extérieure
$ sudo systemctl enable udp-tunnel-outside.service
$ sudo systemctl start udp-tunnel-outside.service
Il n'y a pas de cryptage. Les paquets sont transmis tels quels, il est supposé que quel que soit le service que vous tunnelisez, il sait comment protéger ou chiffrer ses données par lui-même. C'est généralement le cas pour les connexions VPN.
De plus, un attaquant pourrait vouloir usurper les paquets keepalive provenant de l'agent interne pour confondre l'agent externe et détourner le tunnel vers sa propre machine, ce qui entraînerait une interruption du service. Pour empêcher cette attaque très simple, les datagrammes keepalive peuvent être authentifiés avec un code d'authentification de message basé sur le hachage. Vous pouvez utiliser un mot de passe pré-partagé en utilisant l'option -k aux deux extrémités du tunnel pour activer cette fonctionnalité.
Sur l'hôte intérieur, vous l'utiliseriez comme ceci
$ ./udp-tunnel -s localhost:1234 -o jump.example.com:9999 -k mysecretpassword
Sur l'hôte extérieur, vous le démarreriez avec
$ ./udp-tunnel -l 9999 -k mysecretpassword
Une fois que les étapes d'installation ci-dessus ont fonctionné avec succès, vous souhaiterez peut-être modifier manuellement vos fichiers systemd aux deux extrémités et ajouter une option -k, puis recharger et redémarrer aux deux extrémités.
Le message keepalive contiendra alors un SHA-256 sur ce mot de passe et sur une valeur occasionnelle strictement croissante qui ne peut être utilisée qu'une seule fois pour empêcher de simples attaques par relecture.
Les deux agents maintiennent une liste de connexions, chaque connexion stocke les adresses de socket et les descripteurs de socket associés à cette connexion client particulière.
Au démarrage, l'agent interne initiera un tunnel sortant vers l'agent externe, le marquera comme inutilisé et enverra des paquets keepalive dessus. L'agent extérieur verra ces paquets et ajoutera ce tunnel avec son adresse source à sa propre liste de connexions. Les paquets keepalive sont signés avec un nom occasionnel et un code d'authentification, ils ne peuvent donc pas être usurpés ou relus.
Lorsqu'un client se connecte, l'agent externe enverra les données du client dans ce tunnel de rechange inutilisé, l'agent interne le verra, marquera le tunnel comme actif, créera un socket pour communiquer avec le service et transmettra les données dans les deux sens. Il créera alors immédiatement un autre nouveau tunnel de réserve sortant pour être prêt pour le prochain client entrant.
Lorsque le nouveau tunnel de réserve arrive chez l'agent extérieur, celui-ci l'ajoute à sa propre liste de connexions pour pouvoir servir immédiatement le prochain client qui se connecte.
À ce stade, les deux agents ont 2 tunnels dans leur liste, un est actif et un autre est disponible, en attente de la prochaine connexion client.
les délais d'inactivité garantiront que les tunnels morts (ceux qui avaient été marqués comme actifs dans le passé mais sans données client depuis un certain temps) seront supprimés et leurs sockets seront fermés. L'agent interne détectera le manque de données transférées pendant une période prolongée, les supprimera de sa propre liste et fermera ses sockets, puis quelque temps plus tard, l'agent externe détectera qu'il n'y a plus de keepalives arrivant pour cette connexion et les supprimera de la sienne. liste aussi.
Ce code est encore très expérimental, alors ne basez pas dessus une entreprise multimillionnaire, du moins pas encore. Cela sert parfaitement mon objectif, mais cela pourrait planter, brûler et exploser votre serveur pour vous. Vous êtes prévenu.