Comment un client OpenVPN derrière NAT devient la sortie Internet
La sortie compose elle-même, donc son routeur n'a besoin d'aucun port entrant. Suivez ce que le concentrateur route, ce que la sortie transmet et d'où vient le chemin de retour.
Trois hôtes Linux, une conception : un concentrateur OpenVPN accessible publiquement, un ordinateur portable grand public et une machine de sortie derrière un routeur domestique ordinaire qui ne transmet rien vers l'intérieur. La sortie compose elle-même vers le concentrateur. Le trafic Internet du consommateur entre alors dans le tunnel sur le portable et ressort par le lien montant propre à la sortie. Tout ce qui surprend dans cette disposition découle d'un détail : la sortie a initié la connexion, donc le chemin vers l'intérieur existe déjà et aucune redirection de port n'est nécessaire pour le créer.
Ce qui suit suit le chemin du paquet plutôt que l'ordre de configuration, car la configuration n'a de sens qu'une fois que l'on sait quel composant possède quelle décision. Chaque couche possède exactement une décision, et les erreurs se concentrent là où deux couches sont prises pour une seule.
Deux sessions de transport, un chemin intérieur
Chaque hôte conserve sa propre connexion extérieure vers le concentrateur, et aucune de ces connexions ne connaît l'autre. Le trafic du consommateur vers Internet est un flux intérieur distinct, transporté dans le tunnel du consommateur puis réémis depuis le tunnel de la sortie. C'est ce flux intérieur que le concentrateur doit router et que la sortie doit transmettre.
outer: consumer --TLS--> hub
outer: exit --TLS--> hub
inner: consumer --tun--> hub --internal--> exit --tun--> uplink
Comme les deux connexions extérieures partent du côté client, le routeur de la sortie ne voit jamais de SYN entrant et n'a jamais besoin d'une règle NAT de destination. Il doit seulement maintenir le transport établi en vie, ce qu'il fait déjà pour toute connexion durable. Rien sur l'Internet public n'a besoin d'atteindre la sortie d'abord, c'est pourquoi une adresse dynamique sur ce lien montant n'est pas un problème. Le routeur doit toujours autoriser la session sortante, mais c'est la même autorisation qu'un navigateur utilise déjà.
Ce qu'iroute installe réellement dans le serveur
Le concentrateur est le seul appareil qui décide quel pair possède quelle destination, et il prend cette décision dans sa propre table de routage en espace utilisateur, non dans celle du noyau. Une entrée client-config-dir pour la sortie nommée exit-node peut y revendiquer le préfixe par défaut :
# /etc/openvpn/server/ccd/exit-node
iroute 0.0.0.0 0.0.0.0
Cette ligne dit au processus serveur « les paquets intérieurs avec n'importe quelle destination appartiennent à ce client ». Elle n'ajoute pas de route par défaut au système d'exploitation du concentrateur et ne fait pas du concentrateur une passerelle Internet. Référence OpenVPN 2.6 maintient le routage interne propre au client et le routage du noyau comme des mécanismes séparés, et cette séparation est la raison pour laquelle la conception fonctionne sans toucher à la route par défaut du concentrateur.
Le consommateur a besoin de l'instruction en miroir, livrée en push, pour que son propre trafic entre dans le tunnel au lieu de sortir par sa passerelle locale. Avec la topologie de sous-réseau, le pool est une route connectée sur l'interface tun de la sortie, donc la direction de retour n'a pas besoin de push supplémentaire ; ce que la sortie ne doit pas recevoir, c'est la redirection ci-dessous, qui renverrait son propre trafic montant dans le tunnel qu'elle essaie d'utiliser. Envoyez-la uniquement au consommateur et lisez les routes appliquées sur le client au lieu de supposer que le fichier CCD a été sélectionné.
# /etc/openvpn/server/ccd/consumer-01
push "redirect-gateway def1"
Le noyau de la sortie fait la transmission
Dans la sortie, un paquet du consommateur arrive sur l'interface tun, adressé à un hôte Internet quelconque. Le noyau consulte sa table de routage, trouve le vrai lien montant comme meilleure correspondance, puis applique deux politiques indépendantes : ce paquet peut-il être transmis du tout, et peut-il sortir par cette interface avec cette adresse source. La transmission est un sysctl, et elle est désactivée par défaut sur de nombreuses distributions.
La sortie a donc besoin de la transmission IPv4 activée en permanence, d'une règle qui admet les sources du tunnel du consommateur vers le lien montant, et du chemin de réponse par le tun. Activer la transmission pour tout l'hôte change aussi le comportement de tout ce qui route à travers lui, c'est pourquoi la règle devrait nommer les sources et les interfaces au lieu d'accepter n'importe quoi.
La traduction de source est ce qui achète le chemin de retour
Un hôte Internet n'a aucune route vers une adresse 10.90.0.x, donc les réponses seraient éliminées avant même d'atteindre la sortie. Réécrire la source lorsque le paquet quitte le lien montant résout cela, et le suivi de connexion annule la réécriture au retour.
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -s 10.90.0.0/24 -o enp1s0 -j MASQUERADE
ip -4 route get 10.90.0.9 # run on the exit: must point at the tun
La règle ci-dessus est un point de départ pour une interface montante, pas une politique de pare-feu. Restreignez la source aux affectations que vous avez réellement autorisées, et gardez la politique d'entrée propre à la sortie séparée, pour que le masquage du trafic du tunnel n'expose pas discrètement des services locaux. Sur un hôte à adresse montante dynamique, le masquage vous épargne aussi le suivi manuel de l'adresse.
Où le concentrateur peut imposer et où il ne peut pas
Avec client-to-client et le chemin de données traditionnel en espace utilisateur, le concentrateur remet les paquets intérieurs entre pairs à l'intérieur du processus serveur. Ils ne traversent jamais la chaîne FORWARD du noyau du concentrateur, donc une règle du noyau à cet endroit ne peut pas restreindre le transit entre pairs, aussi soigneusement soit-elle écrite. L'application doit vivre dans la politique propre du serveur : qui peut se connecter, quel fichier CCD il reçoit, et si la propriété par défaut de la sortie est accordée.
C'est aussi pourquoi un iroute par défaut est une décision de confiance large. La sortie doit légitimement accepter le trafic de retour d'adresses Internet arbitraires, et elle doit être une machine que vous contrôlez, corrigez et surveillez. Accorder la même propriété à un pair à qui vous ne confieriez pas un routeur est l'erreur à laquelle cette conception invite.
L'adhésion est le vrai contrôle. Tout client qui peut configurer ses propres routes peut essayer d'utiliser la sortie, donc restreindre qui peut se connecter, ou quels pairs la sortie servira, fait partie de la conception et non d'une réflexion après coup.
Échouer proprement quand la sortie disparaît
Coupez la sortie et la propriété par défaut intérieure disparaît avec sa session. Ce qui ne doit pas arriver, c'est que le concentrateur devienne discrètement la sortie de remplacement : aucune route par défaut du pool VPN vers le lien montant du concentrateur, et aucune règle de masquage large sur le concentrateur qui transmettrait volontiers le même trafic. Testez cette défaillance volontairement, puis testez la reprise avec un flux neuf et non avec une page déjà ouverte.
# on the consumer, with the exit up and then down
ip -4 route get 1.1.1.1
curl -sS -o /dev/null -w '%{http_code}\n' https://example.com
Si le trafic du consommateur atteint encore Internet après la disparition de la sortie, autre chose le transporte et la frontière voulue n'existe pas. Traitez aussi DNS et IPv6 comme des décisions séparées : une route IPv4 plus le masquage ne disent rien du trafic AAAA, et un résolveur poussé par le concentrateur répondra d'où la requête sort réellement.