ipipou : plus qu'un simple tunnel non chiffré

Que disons-nous à Dieu IPv6 ?

ipipou : plus qu'un simple tunnel non chiffré
C'est vrai, et à Dieu du chiffrement, nous dirons la même chose aujourd'hui.

Ce sera sur un tunnel IPv4 non chiffré, mais pas sur un « chaud et analogique », plutôt sur un « moderne et LED ». Il y aura aussi des sockets bruts et des manipulations de paquets dans l'espace utilisateur.

Il existe N protocoles de tunnelisation pour tous les goûts et couleurs :

  • chic, à la mode, jeune WireGuard
  • polyvalents, comme des couteaux suisses, OpenVPN et SSH
  • ancien et inoffensif GRE
  • maximement simple, rapide, pas du tout chiffré IPIP
  • en pleine évolution GENEVE
  • plein d'autres.

Mais je suis développeur, donc je vais augmenter N seulement d'une fraction, et je laisserai le développement de véritables protocoles aux développeurs professionnels.

Dans un projet encore non réalisé sur lequel je travaille actuellement, il faut atteindre les hôtes derrière le NAT de l'extérieur. En utilisant pour cela des protocoles avec une cryptographie mature, je n'ai pas pu m'empêcher de penser que c'était un peu comme utiliser un canon pour tuer des moineaux. Comme le tunnel est principalement utilisé juste pour percer un trou dans le NAT, le trafic interne est généralement aussi chiffré, on privilégie HTTPS.En explorant différents protocoles de tunnelisation, l'attention de mon perfectionniste intérieur était sans cesse attirée par IPIP en raison de ses frais généraux minimes. Mais il a un et demi défauts majeurs pour mes besoins :

il nécessite des IP publiques des deux côtés,

  • et aucune authentification.
  • C'est pourquoi le perfectionniste s'est replié dans un coin sombre de ma boîte crânienne, ou là où il se cache.

Et un jour, en lisant des articles sur

les tunnels pris en charge nativement dans Linux, je suis tombé sur FOU (Foo-over-UDP), c'est-à-dire du n'importe quoi emballé dans UDP. Pour l'instant, seuls IPIP et GUE (Generic UDP Encapsulation) sont pris en charge. « Voilà la balle d'argent ! Même un simple IPIP me suffira. » — pensais-je.

En réalité, la balle n'était pas tout à fait en argent. L'encapsulation dans UDP résout le premier problème — on peut se connecter aux clients derrière le NAT de l'extérieur en utilisant une connexion déjà établie, mais ici, la moitié du défaut suivant d'IPIP prend une nouvelle dimension — derrière des IP publiques visibles et le port du client, il peut y avoir n'importe qui d'un réseau privé (ce problème n'existe pas en pur IPIP).

Pour résoudre ce problème partiel est née l'utilitaire

ipipou ipipouElle implémente un mécanisme d'authentification du hôte distant, tout en maintenant le fonctionnement du FOU central, qui traitera rapidement et efficacement les paquets dans l'espace noyau.

Ton script n'est pas nécessaire !

D'accord, si tu connais le port public et l'IP du client (par exemple, il n'ira pas n'importe où, NAT essaie de mapper les ports 1 à 1), tu peux créer un tunnel IPIP-over-FOU avec les commandes suivantes, sans aucun script.

sur le serveur :

# Подгрузить модуль ядра FOU
modprobe fou

# Создать IPIP туннель с инкапсуляцией в FOU.
# Модуль ipip подгрузится автоматически.
ip link add name ipipou0 type ipip 
    remote 198.51.100.2 local 203.0.113.1 
    encap fou encap-sport 10000 encap-dport 20001 
    mode ipip dev eth0

# Добавить порт на котором будет слушать FOU для этого туннеля
ip fou add port 10000 ipproto 4 local 203.0.113.1 dev eth0

# Назначить IP адрес туннелю
ip address add 172.28.0.0 peer 172.28.0.1 dev ipipou0

# Поднять туннель
ip link set ipipou0 up

sur le client :

modprobe fou

ip link add name ipipou1 type ipip 
    remote 203.0.113.1 local 192.168.0.2 
    encap fou encap-sport 10001 encap-dport 10000 encap-csum 
    mode ipip dev eth0

# Les options local, peer, peer_port, dev peuvent ne pas être prises en charge par les anciens noyaux, elles peuvent être omises.
# peer et peer_port sont utilisés pour établir la connexion dès la création du FOU-listener.
ip fou add port 10001 ipproto 4 local 192.168.0.2 peer 203.0.113.1 peer_port 10000 dev eth0

ip address add 172.28.0.1 peer 172.28.0.0 dev ipipou1

ip link set ipipou1 up

où

  • ipipou* — nom de l'interface réseau de tunnel local
  • 203.0.113.1 — IP publique du serveur
  • 198.51.100.2 — IP publique du client
  • 192.168.0.2 — IP du client assignée à l'interface eth0
  • 10001 — port local du client pour FOU
  • 20001 — port public du client pour FOU
  • 10000 — port public du serveur pour FOU
  • encap-csum — option pour ajouter une somme de contrôle UDP dans les paquets UDP encapsulés ; peut être remplacée par noencap-csum, afin de ne pas calculer, l'intégrité est contrôlée par la couche d'encapsulation extérieure (tant que le paquet reste à l'intérieur du tunnel)
  • eth0 — interface locale à laquelle le tunnel ipip sera attaché
  • 172.28.0.1 — IP de l'interface de tunnel du client (privée)
  • 172.28.0.0 — IP de l'interface de tunnel du serveur (privée)

Tant que la connexion UDP est active, le tunnel restera fonctionnel, et s'il se rompt, cela dépendra — si l'IP : port du client reste le même — il fonctionnera, sinon — il se rompra.

Le plus simple pour tout remettre à l'endroit est de décharger les modules du noyau : modprobe -r fou ipip

Même si l'authentification n'est pas requise, les IP publiques et le port du client ne sont pas toujours connus et souvent imprévisibles ou changeants (selon le type de NAT). Si l'on omet encap-dport du côté serveur, le tunnel ne fonctionnera pas, il n'est pas suffisamment intelligent pour prendre le port distant de la connexion. Dans ce cas, ipipou peut également aider, ou WireGuard et les autres dans ce style peuvent t'assister.

Comment cela fonctionne ?

Le client (ce qui se cache généralement derrière le NAT) établit un tunnel (comme dans l'exemple ci-dessus) et envoie un paquet d'authentification au serveur pour qu'il configure le tunnel de son côté. Selon les paramètres, cela peut être un paquet vide (juste pour que le serveur voie l'IP publique : port de connexion), ou avec des données permettant au serveur d'identifier le client. Les données peuvent être une simple phrase de mot de passe en clair (je pense à l'analogie avec l'HTTP Basic Auth) ou des données signées par une clé privée spécialement formatées (similaire à l'HTTP Digest Auth mais en plus sécurisé, voir la fonction client_auth dans le code).

Sur le serveur (côté avec l'IP publique), lors du démarrage, ipipou crée un gestionnaire de file d'attente nfqueue et configure netfilter pour que les paquets nécessaires soient dirigés au bon endroit : les paquets initiaux de connexion vers la file d'attente nfqueue, et [presque] tous les autres directement vers le listener FOU.

Pour ceux qui ne sont pas au courant, nfqueue (ou NetfilterQueue) est un outil spécial pour les amateurs qui ne savent pas développer de modules noyau, qui, grâce à netfilter (nftables/iptables), permet de rediriger des paquets réseau vers l'espace utilisateur et de les traiter avec des moyens primitifs : modifier (optionnellement) et renvoyer au noyau, ou les jeter.

Il existe des liaisons pour certains langages de programmation pour travailler avec nfqueue, mais il n'y en avait pas pour bash (hé, pas étonnant), j'ai donc dû utiliser python : ipipou utilise NetfilterQueue.

Si la performance n'est pas critique, avec cet outil, il est relativement rapide et simple de créer sa propre logique de traitement des paquets à un niveau assez bas, par exemple en élaborant des protocoles de transmission de données expérimentaux ou en trollant des services locaux et distants avec un comportement non standard.

Les sockets bruts (raw sockets) fonctionnent de pair avec nfqueue, par exemple lorsque le tunnel est déjà configuré et que FOU écoute sur le port nécessaire, il n'est pas possible d'envoyer un paquet depuis ce même port de la manière ordinaire — il est occupé, mais on peut prendre et envoyer un paquet généré aléatoirement directement dans l'interface réseau en utilisant un socket brut, même si la génération d'un tel paquet nécessite un peu plus de travail. C'est ainsi que les paquets d'authentification sont créés dans ipipou.

Puisque ipipou ne traite que les premiers paquets de la connexion (et ceux qui ont réussi à entrer dans la file d'attente avant l'établissement de la connexion), la performance n'est que peu affectée.

Dès que le serveur ipipou reçoit un paquet ayant passé l'authentification, le tunnel est créé et tous les paquets suivants de la connexion sont traités par le noyau en contournant nfqueue. Si la connexion expire, le premier paquet suivant sera dirigé vers la file d'attente nfqueue, selon les paramètres, à moins que ce ne soit un paquet d'authentification, mais avec la dernière adresse IP mémorisée et le port du client ; il peut soit être laissé passer, soit être rejeté. Si un paquet authentifié arrive avec une nouvelle adresse IP et un nouveau port, le tunnel est reconfiguré pour les utiliser.

Un tunnel IPIP-over-FOU classique a un autre problème lors de l'utilisation de NAT — il ne peut pas créer deux tunnels IPIP encapsulés dans UDP avec les mêmes adresses IP, car les modules FOU et IPIP sont suffisamment isolés l'un de l'autre. Ainsi, deux clients derrière une même adresse IP publique ne peuvent pas se connecter simultanément à un même serveur de cette manière. À l'avenir, peut-être, cela sera résolu au niveau du noyau, mais ce n'est pas certain. En attendant, les problèmes de NAT peuvent être résolus par le NAT — si par hasard deux adresses IP sont déjà occupées par un autre tunnel, ipipou fera un NAT de l'adresse publique vers une alternative privée, et voilà ! — on peut créer des tunnels tant que les ports ne sont pas épuisés.

Comme tous les paquets de la connexion ne sont pas signés, cette protection simple est vulnérable aux attaques MITM, donc si un vilain se cache sur le chemin entre le client et le serveur, en pouvant écouter le trafic et le manipuler, il peut rediriger les paquets d'authentification vers une autre adresse et créer un tunnel à partir d'un hôte non fiable.

Si quelqu'un a des idées sur la manière de corriger cela tout en laissant la majeure partie du trafic dans le noyau, n'hésitez pas — faites-le savoir.

Cela dit, l'encapsulation dans UDP a très bien fonctionné. Comparé à l'encapsulation sur IP, elle est beaucoup plus stable et est souvent plus rapide malgré les coûts supplémentaires liés à l'en-tête UDP. Cela est dû au fait qu'Internet est majoritairement peuplé d'hôtes qui ne fonctionnent bien qu'avec trois protocoles les plus populaires : TCP, UDP, ICMP. Une part significative peut même ignorer tout le reste, ou traiter plus lentement, car elle est optimisée uniquement pour ces trois.

Par exemple, c'est pourquoi QUICK, sur lequel HTTP/3 est basé, a été conçu exactement au-dessus de UDP, et non sur IP.

Eh bien, assez de mots, il est temps de voir comment cela fonctionne dans le « monde réel ».

Bataille

Pour simuler le monde réel, on utilise iperf3. En termes de proximité à la réalité, c'est à peu près comme simuler le monde réel dans Minecraft, mais pour l'instant ça ira.

Les participants au concours sont :

  • la chaîne principale de référence
  • le héros de cet article ipipou
  • OpenVPN avec authentification, mais sans chiffrement
  • OpenVPN en mode « tout inclus »
  • WireGuard sans PresharedKey, avec MTU=1440 (car uniquement IPv4)

Données techniques pour les geeks
Les métriques sont relevées avec les commandes suivantes

sur le client :

UDP

CPULOG=NAME.udp.cpu.log; sar 10 6 > "$CPULOG" & iperf3 -c SERVER_IP -4 -t 60 -f m -i 10 -B LOCAL_IP -P 2 -u -b 12M; tail -1 "$CPULOG"
# Où "-b 12M" représente la bande passante de la chaîne principale divisée par le nombre de flux "-P", afin de ne pas créer de paquets inutiles et de ne pas nuire aux performances.

TCP

CPULOG=NAME.tcp.cpu.log; sar 10 6 > "$CPULOG" & iperf3 -c SERVER_IP -4 -t 60 -f m -i 10 -B LOCAL_IP -P 2; tail -1 "$CPULOG"

Latence ICMP

ping -c 10 SERVER_IP | tail -1

sur le serveur (lancé en même temps que le client) :

UDP

CPULOG=NAME.udp.cpu.log; sar 10 6 > "$CPULOG" & iperf3 -s -i 10 -f m -1; tail -1 "$CPULOG"

TCP

CPULOG=NAME.tcp.cpu.log; sar 10 6 > "$CPULOG" & iperf3 -s -i 10 -f m -1; tail -1 "$CPULOG"

Configuration des tunnels

ipipou
serveur
/etc/ipipou/server.conf:

serveur
number 0
fou-dev eth0
fou-local-port 10000
tunl-ip 172.28.0.0
auth-remote-pubkey-b64 eQYNhD/Xwl6Zaq+z3QXDzNI77x8CEKqY1n5kt9bKeEI=
auth-secret topsecret
auth-lifetime 3600
reply-on-auth-ok
verb 3

systemctl start ipipou@server

client
/etc/ipipou/client.conf:

client
number 0
fou-local @eth0
fou-remote SERVER_IP:10000
tunl-ip 172.28.0.1
# pubkey de auth-key-b64 : eQYNhD/Xwl6Zaq+z3QXDzNI77x8CEKqY1n5kt9bKeEI=
auth-key-b64 RuBZkT23na2Q4QH1xfmZCfRgSgPt5s362UPAFbecTso=
auth-secret topsecret
keepalive 27
verb 3

systemctl start ipipou@client

openvpn (sans chiffrement, avec authentification)
serveur

openvpn --genkey --secret ovpn.key  # Ensuite, il faut transmettre ovpn.key au client
openvpn --dev tun1 --local SERVER_IP --port 2000 --ifconfig 172.16.17.1 172.16.17.2 --cipher none --auth SHA1 --ncp-disable --secret ovpn.key

client

openvpn --dev tun1 --local LOCAL_IP --remote SERVER_IP --port 2000 --ifconfig 172.16.17.2 172.16.17.1 --cipher none --auth SHA1 --ncp-disable --secret ovpn.key

openvpn (avec chiffrement, authentification, via UDP, tout comme il se doit)
Configuré en utilisant openvpn-manage

wireguard
serveur
/etc/wireguard/server.conf:

[Interface]
Address=172.31.192.1/18
ListenPort=51820
PrivateKey=aMAG31yjt85zsVC5hn5jMskuFdF8C/LFSRYnhRGSKUQ=
MTU=1440

[Peer]
PublicKey=LyhhEIjVQPVmr/sJNdSRqTjxibsfDZ15sDuhvAQ3hVM=
AllowedIPs=172.31.192.2/32

systemctl start wg-quick@server

client
/etc/wireguard/client.conf:

[Interface]
Address=172.31.192.2/18
PrivateKey=uCluH7q2Hip5lLRSsVHc38nGKUGpZIUwGO/7k+6Ye3I=
MTU=1440

[Peer]
PublicKey=DjJRmGvhl6DWuSf1fldxNRBvqa701c0Sc7OpRr4gPXk=
AllowedIPs=172.31.192.1/32
Endpoint=SERVER_IP:51820

systemctl start wg-quick@client

Résultats

Un tableau brut et peu attrayant
La charge CPU du serveur n'est pas très représentative, car d'autres services fonctionnent également, et parfois ils consomment des ressources :

bande passante proto[Mbps] CPU_idle_client[%] CPU_idle_server[%]
# 20 Mbps de la micro-informatique (4 cœurs) vers VPS (1 cœur) à travers l'Atlantique
# pur
UDP 20.4      99.80 93.34
TCP 19.2      99.67 96.68
latence ICMP min/avg/max/mdev = 198.838/198.997/199.360/0.372 ms
# ipipou
UDP 19.8      98.45 99.47
TCP 18.8      99.56 96.75
latence ICMP min/avg/max/mdev = 199.562/208.919/220.222/7.905 ms
# openvpn0 (auth seulement, pas de cryptage)
UDP 19.3      99.89 72.90
TCP 16.1      95.95 88.46
latence ICMP min/avg/max/mdev = 191.631/193.538/198.724/2.520 ms
# openvpn (cryptage complet, auth, etc)
UDP 19.6      99.75 72.35
TCP 17.0      94.47 87.99
latence ICMP min/avg/max/mdev = 202.168/202.377/202.900/0.451 ms
# wireguard
UDP 19.3      91.60 94.78
TCP 17.2      96.76 92.87
latence ICMP min/avg/max/mdev = 217.925/223.601/230.696/3.266 ms

## canal d'environ 1 Gbps entre VPS en Europe et aux États-Unis (1 cœur)
# pur
UDP 729      73.40 39.93
TCP 363      96.95 90.40
latence ICMP min/avg/max/mdev = 106.867/106.994/107.126/0.066 ms
# ipipou
UDP 714      63.10 23.53
TCP 431      95.65 64.56
latence ICMP min/avg/max/mdev = 107.444/107.523/107.648/0.058 ms
# openvpn0 (auth seulement, pas de cryptage)
UDP 193      17.51  1.62
TCP  12      95.45 92.80
latence ICMP min/avg/max/mdev = 107.191/107.334/107.559/0.116 ms
# wireguard
UDP 629      22.26  2.62
TCP 198      77.40 55.98
latence ICMP min/avg/max/mdev = 107.616/107.788/108.038/0.128 ms

canal de 20 Mbps

ipipou : plus qu'un simple tunnel non chiffré

ipipou : plus qu'un simple tunnel non chiffré

canal de 1 Gbps optimiste

ipipou : plus qu'un simple tunnel non chiffré

ipipou : plus qu'un simple tunnel non chiffré

Dans tous les cas, ipipou est assez proche des performances de la bande passante de base, et c'est formidable !

Le tunnel openvpn non crypté s'est comporté de manière assez étrange dans les deux cas.

Si quelqu'un envisage de tester, serait intéressant d'entendre des retours.

Que IPv6 et NetPrickle soient avec nous !

Source : habr.com

Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS 🔥 Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster