ipipou: plus qu'un simple tunnel non chiffré

Que dit-on au dieu IPv6?



IPv6?  Pas aujourd'hui


C'est vrai, et au dieu du cryptage, nous dirons la même chose aujourd'hui.



Il s'agira ici d'un tunnel IPv4 non chiffré, mais pas d'une "lampe chaude", mais d'une "LED" moderne. Et puis les sockets bruts clignotent, et il y a du travail avec les packages dans l'espace utilisateur.



Il existe N protocoles de tunnellisation pour chaque goût et couleur:



  • WireGuard élégant, branché et jeune
  • multifonctionnel comme les couteaux suisses, OpenVPN et SSH
  • vieux et pas mal GRE
  • l'IPIP le plus simple, intelligent et non chiffré du tout
  • développer activement GENEVE
  • beaucoup d'autres.


Mais je suis programmeur, donc je n'augmenterai N que d'une fraction, et laisserai le développement de vrais protocoles aux b-développeurs.



Dans un autre projet à naître , dans lequel je suis actuellement engagé, je dois atteindre les hôtes derrière NAT de l'extérieur. En utilisant des protocoles avec la cryptographie adulte pour cela, je n'ai jamais laissé le sentiment que c'était comme un boulet de canon. Parce que le tunnel est utilisé pour la plupart uniquement pour creuser un trou dans NAT-e, le trafic interne est généralement également crypté, mais ils sont noyés pour HTTPS.



Lors de la recherche de divers protocoles de tunneling, l'attention de mon perfectionniste intérieur a été attirée à maintes reprises sur IPIP en raison de sa surcharge minimale. Mais cela présente un inconvénient et demi significatif pour mes tâches:



  • il nécessite des adresses IP publiques des deux côtés,
  • et aucune authentification pour vous.


Par conséquent, le perfectionniste a été ramené dans le coin sombre du crâne, ou partout où il est assis là.



Et une fois, en lisant des articles sur les tunnels supportés nativement sous Linux, je suis tombé sur FOU (Foo-over-UDP), c'est-à-dire quoi qu'il en soit, enveloppé dans UDP. Jusqu'à présent, seuls IPIP et GUE (Generic UDP Encapsulation) sont pris en charge à partir de quoi que ce soit d'autre.



«Voici une solution miracle! Moi et un IPIP simple pour les yeux. " J'ai pensé.



En fait, la balle n'était pas entièrement argentée. L'encapsulation dans UDP résout le premier problème - vous pouvez vous connecter aux clients derrière NAT de l'extérieur en utilisant une connexion préétablie, mais ici la moitié du prochain inconvénient d'IPIP fleurit sous un nouveau jour - n'importe qui du réseau privé peut être caché derrière l'adresse IP publique visible et le port client (en IPIP pur Il n'y a pas de problème).



Pour résoudre ce problème d'un an et demi, l'utilitaire ipipou est né . Il implémente un mécanisme d'authentification d'un hôte distant, tout en ne perturbant pas le travail d'un FOU vigoureux, qui traitera rapidement et efficacement les paquets dans l'espace du noyau.



Pas besoin de votre script!



Ok, si vous connaissez le port public et l'adresse IP du client (par exemple, tous les vôtres, où qu'ils aillent, NAT essaie de mapper les ports 1 à 1), vous pouvez créer un tunnel IPIP-sur-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

#  local, peer, peer_port, dev     ,   .
# peer  peer_port        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




  • ipipou* - le nom de l'interface réseau du tunnel local
  • 203.0.113.1 - IP publique du serveur
  • 198.51.100.2 - IP publique du client
  • 192.168.0.2 - IP client attribuée à l'interface eth0
  • 10001 - port client local pour FOU
  • 20001 - port client public pour FOU
  • 10000 - port de serveur public pour FOU
  • encap-csum — UDP UDP ; noencap-csum, , ( )
  • eth0 — ipip
  • 172.28.0.1 — IP ()
  • 172.28.0.0 — IP ()


Tant que la connexion UDP est active, le tunnel sera en état de fonctionnement, et comment il se brise, quelle chance - si le port IP: du client reste le même - il vivra, changera - il se cassera.



Le moyen le plus simple de changer les choses est de décharger les modules du noyau: modprobe -r fou ipip



même si l'authentification n'est pas requise, l'adresse IP publique et le port client ne sont pas toujours connus et sont souvent imprévisibles ou modifiables (selon le type de NAT). Si vous l'omettez encap-dportcôté serveur, le tunnel ne fonctionnera pas, il n'est pas assez intelligent pour prendre le port de connexion à distance. Dans ce cas, ipipou peut également aider, eh bien, ou WireGuard et d'autres comme lui pour vous aider.



Comment ça fonctionne?



Le client (qui est généralement derrière NAT) met en place un tunnel (comme dans l'exemple ci-dessus) et envoie un paquet authentifié au serveur afin qu'il puisse configurer le tunnel de son côté. Selon les paramètres, cela peut être un paquet vide (juste pour que le serveur voie l'adresse IP publique: port de connexion), ou avec des données par lesquelles le serveur peut identifier le client. Les données peuvent être une simple phrase de passe en clair (une analogie avec HTTP Basic Auth vient à l'esprit) ou des données spécialement formatées signées avec une clé privée (par analogie avec HTTP Digest Auth, seulement plus fort, voir la fonction client_authdans le code).



Sur le serveur (côté IP public), au démarrage, ipipou crée un gestionnaire de files nfqueue et configure netfilter pour que les paquets nécessaires soient envoyés là où ils doivent aller: les paquets initialisant la connexion à la file nfqueue, et [presque] tout le reste directement à l'écouteur FOU.



Qui n'est pas dans le sujet, nfqueue (ou NetfilterQueue) est une chose si spéciale pour les amateurs qui ne savent pas comment développer des modules de noyau , qui au moyen de netfilter (nftables / iptables) vous permet de rediriger les paquets réseau vers l'espace utilisateur et de les traiter avec des moyens improvisés primitifs : modifier (facultatif ) et redonner au noyau, ou jeter.



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



Si les performances ne sont pas critiques, avec l'aide de cette chose, vous pouvez cuire relativement rapidement et facilement votre propre logique pour travailler avec des paquets à un niveau assez bas, par exemple, sculpter des protocoles de transfert de données expérimentaux ou troller des services locaux et distants avec un comportement non standard.



Les sockets bruts fonctionnent main dans la main avec nfqueue, par exemple, lorsque le tunnel est déjà configuré et que FOU écoute sur le port souhaité, cela ne fonctionnera pas de la manière habituelle pour envoyer un paquet depuis le même port - il est occupé, mais vous pouvez prendre et déclencher un paquet généré aléatoirement directement dans le réseau interface utilisant une socket brute, bien que la génération d'un tel paquet demandera un peu plus de travail. C'est ainsi que les paquets avec authentification sont créés dans ipipou.



Comme ipipou ne traite que les premiers paquets de la connexion (enfin, ceux qui ont réussi à s'infiltrer dans la file d'attente avant que la connexion ne soit établie), les performances n'en souffrent guère.



Dès que le serveur ipipou reçoit un paquet authentifié, un tunnel est créé et tous les paquets suivants dans la connexion sont déjà traités par le noyau en contournant nfqueue. Si la connexion est mauvaise, alors le premier paquet du suivant sera envoyé à la file d'attente nfqueue, en fonction des paramètres, s'il ne s'agit pas d'un paquet d'authentification, mais à partir de la dernière adresse IP et du dernier port client mémorisés, il peut être transmis ou rejeté. Si un paquet authentifié provient d'une nouvelle adresse IP et d'un nouveau port, le tunnel est reconfiguré pour les utiliser.



L'IPIP-over-FOU habituel a un autre problème lorsque vous travaillez avec NAT - vous ne pouvez pas créer deux tunnels IPIP encapsulés dans UDP avec la même IP, car les modules FOU et IPIP sont assez isolés l'un de l'autre. Ceux. une paire de clients derrière la même IP publique ne pourra pas se connecter simultanément au même serveur de cette manière. Dans le futur, il pourra être résolu au niveau du noyau, mais ce n'est pas certain. En attendant, les problèmes de NAT peuvent être résolus par NAT - s'il arrive qu'une paire d'adresses IP soit déjà occupée par un autre tunnel, ipipou effectuera un NAT du public vers une IP privée alternative, voila! - vous pouvez créer des tunnels jusqu'à épuisement des ports.



Parce que tous les paquets de la connexion ne sont pas signés, alors une protection aussi simple est vulnérable au MITM, donc si un méchant se cache entre le client et le serveur, qui peut écouter et contrôler le trafic, il peut rediriger les paquets authentifiés via une autre adresse et créer un tunnel à partir d'un hôte non approuvé ...



Si quelqu'un a des idées sur la façon de résoudre ce problème tout en laissant l'essentiel du trafic au cœur, n'hésitez pas - parlez-en.



D'ailleurs, l'encapsulation UDP a très bien fait ses preuves. Comparé à l'encapsulation sur IP, il est beaucoup plus stable et souvent plus rapide malgré la surcharge d'en-tête UDP supplémentaire. Cela est dû au fait que la majorité des hôtes sur Internet fonctionnent assez bien avec seulement les trois protocoles les plus courants: TCP, UDP, ICMP. La partie tangible peut généralement rejeter tout le reste, ou traiter plus lentement, car elle n'est optimisée que pour ces trois éléments.



Par exemple, par conséquent, QUICK, sur la base duquel HTTP / 3 a été créé, a été créé sur UDP, pas sur IP.



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



Bataille



Utilisé pour imiter le monde réel iperf3. En termes de degré de proximité avec la réalité, il s'agit de l'émulation du monde réel dans Minecraft, mais pour l'instant, cela fera l'affaire.



Le concours implique:

  • canal maître de référence
  • le héros de cet article est ipipou
  • OpenVPN avec authentification mais pas de cryptage
  • OpenVPN tout compris
  • WireGuard sans PresharedKey, avec MTU = 1440 (pour IPv4 uniquement)


Données techniques pour les geeks
Les métriques sont prises par 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"
#  "-b 12M"     ,     "-P",         .


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


( ):



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"




ipipou



/etc/ipipou/server.conf:

server
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





/etc/ipipou/client.conf:

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


systemctl start ipipou@client



openvpn ( , )



openvpn --genkey --secret ovpn.key  #    ovpn.key 
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




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 (c , , UDP, )

openvpn-manage



wireguard



/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





/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



Tablette laide crue
CPU , .. :



proto bandwidth[Mbps] CPU_idle_client[%] CPU_idle_server[%]
# 20 Mbps    (4 core)  VPS (1 core)  
# pure
UDP 20.4      99.80 93.34
TCP 19.2      99.67 96.68
ICMP latency 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
ICMP latency min/avg/max/mdev = 199.562/208.919/220.222/7.905 ms
# openvpn0 (auth only, no encryption)
UDP 19.3      99.89 72.90
TCP 16.1      95.95 88.46
ICMP latency min/avg/max/mdev = 191.631/193.538/198.724/2.520 ms
# openvpn (full encryption, auth, etc)
UDP 19.6      99.75 72.35
TCP 17.0      94.47 87.99
ICMP latency 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
ICMP latency min/avg/max/mdev = 217.925/223.601/230.696/3.266 ms

## -1Gbps   VPS    (1 core)
# pure
UDP 729      73.40 39.93
TCP 363      96.95 90.40
ICMP latency 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
ICMP latency min/avg/max/mdev = 107.444/107.523/107.648/0.058 ms
# openvpn0 (auth only, no encryption)
UDP 193      17.51  1.62
TCP  12      95.45 92.80
ICMP latency 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
ICMP latency min/avg/max/mdev = 107.616/107.788/108.038/0.128 ms




canal pour 20 Mbps



comparaison de bande passante à 20 Mbps



comparaison de la latence à 20 Mbps



canal pour 1 Gbps optimiste



Comparaison de la bande passante 1 Gbps



Comparaison de l'efficacité du processeur: Mbps / CPU_usage



Dans tous les cas, ipipou est assez proche en termes de performances du canal de base, et c'est super!



Le tunnel openvpn non chiffré s'est comporté assez bizarrement dans les deux cas.



Si quelqu'un veut le tester, il sera intéressant d'entendre des commentaires.



Que IPv6 et NetPrickle soient avec nous!



All Articles