Le conteneur n’atteint pas l’API réservée au VPN ? Faites un proxy sur l’hôte
Le tunnel vit sur l’hôte, donc le conteneur n’a pas de route vers l’adresse privée. Publiez un proxy inverse étroit et appelez-le via host.docker.internal.
Votre service Compose expire face à une API que le navigateur de la même machine ouvre sans se plaindre. Cette différence est l’indice : la requête qui fonctionne part d’un hôte doté d’un tunnel VPN, et celle qui bloque part d’un conteneur avec sa propre pile réseau. Rien dans le conteneur ne connaît les routes du tunnel, donc la destination privée reste injoignable quelle que soit l’écriture du nom. La solution est un saut délibéré par l’hôte, pas une table de routage plus habile.
Commencez par pourquoi les deux chemins diffèrent, puis décidez où le tunnel doit vivre.
Pourquoi l’hôte fonctionne et pas le conteneur
Monter le tunnel donne à l’hôte une interface, un jeu de routes et, généralement, une entrée de résolveur fournie par le VPN. Un conteneur n’hérite de rien de tout cela : il démarre avec une interface sur le pont du moteur et une route par défaut qui pointe vers ce pont. Les routes installées sur l’hôte sont invisibles pour tout ce qui s’exécute dans cet espace de noms.
C’est pourquoi les symptômes paraissent incohérents plutôt que simplement cassés. L’hôte résout le nom privé et se connecte, parce que le nom et la route y existent tous deux. Le conteneur envoie son paquet vers une adresse privée qu’aucun élément du chemin du pont ne peut atteindre, et la requête expire. Ajouter le nom au fichier hosts du conteneur ne change rien, car l’adresse elle-même est injoignable depuis l’endroit où se trouve le conteneur.
Atteindre l’hôte depuis l’intérieur du conteneur
Un processus du conteneur qui compose localhost compose le conteneur. Docker Desktop publie host.docker.internal comme un nom qui renvoie à la machine exécutant le moteur ; les moteurs Linux le laissent indéfini sauf si le service le demande, ce que fait le mappage ci-dessous. Avec cela, l’application vise un nom et un port au lieu de l’adresse privée.
services:
app:
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
INTERNAL_API_HOST: "host.docker.internal:8666"Le tunnel reste du côté hôte du saut, et le port de cette variable est celui sur lequel le proxy écoutera. Un conteneur qui perd le tunnel échoue désormais à un seul saut connu au lieu de tenter une route qu’il n’a jamais eue.
Choisir où vit le tunnel
Trois formes sont possibles pour un service Compose qui a besoin d’une API privée, et elles diffèrent par le processus qui possède le tunnel. Un client VPN peut tourner dans l’image du conteneur, ce qui exige d’exposer un périphérique de tunnel et d’accorder la capacité d’administration réseau qui l’accompagne. L’application peut partager l’espace de noms réseau d’un conteneur de tunnel séparé, de sorte que les deux processus voient une pile et une table de routage. Ou le tunnel peut rester sur l’hôte, avec un étroit redirecteur de l’hôte vers le conteneur.
Les deux premières tirent des fichiers de profil, un comportement de reconnexion et une politique de résolveur dans le projet, et rendent chaque service du fichier dépendant de la santé du tunnel. C’est beaucoup de couplage pour un seul appel d’API. Pour une pile de développement qui lit un unique point de terminaison interne, le redirecteur côté hôte est le plus petit changement qui garde néanmoins les identifiants là où l’équipe plateforme les gère.
Publier un seul chemin étroit sur l’hôte
Partez de ce dont le redirecteur hérite : exécutez-le avec le compte qui possède le tunnel et ses sockets sortantes reprennent les routes de l’hôte et le résolveur de l’hôte. Un bloc location de nginx couvre un chemin.
server {
listen 8666;
location /search/ {
proxy_pass http://10.20.30.40:8090/;
proxy_set_header Host internal-api.example;
proxy_read_timeout 60s;
}
}Traitez l’adresse et le port comme des valeurs de remplacement et mettez le couple sur lequel l’API écoute réellement. Chaque upstream supplémentaire mérite son propre bloc location, et chaque bloc ne devrait admettre que les chemins appelés par ce service, afin que le proxy ne devienne pas une porte générale vers le réseau privé. Liez l’écouteur à l’adresse du pont pour les clients qui arrivent via host-gateway, au loopback seulement quand le client partage l’espace de noms réseau de l’hôte, et jamais à 0.0.0.0. Choisissez aussi un port encore libre : un numéro déjà pris laisse le proxy sans écoute pendant que le conteneur continue d’expirer, ce qui est indistinguable de la panne réseau qui vous a fait commencer.
Ports, noms et la décision TLS
La résolution de noms se divise comme le routage. Les recherches publiques du conteneur passent par le résolveur du moteur, donc un nom privé qui se résout sur l’hôte ne se résoudra pas dans le conteneur. Le proxy est le composant qui résout le nom privé, parce qu’il hérite du résolveur de l’hôte.
TLS est la décision que l’on reporte et qui explique ensuite la panne suivante. Si le proxy transmet les octets intacts, le conteneur voit le certificat propre à l’API et a besoin de l’AC interne dans son magasin de confiance. Si le proxy termine TLS, cette confiance passe sur l’hôte et l’application commence à construire des liens à partir de l’en-tête host qu’elle reçoit. Choisissez un seul arrangement et appliquez-le à chaque ressource, car une redirection émise par l’autre pointe vers un nom d’hôte que le conteneur ne peut pas résoudre.
Vérifier depuis l’intérieur du service
Envoyez la requête depuis le conteneur, pas depuis l’hôte : un simple curl sur le chemin du proxy, puis le contrôle de santé de l’application elle-même. Quand le proxy répond mais que l’application échoue encore, comparez trois choses : l’URL de base qu’elle construit, toute variable de proxy dans son environnement et l’AC à laquelle elle fait confiance. Toutes se trouvent dans l’environnement et les journaux du conteneur, et corriger l’une d’elles laisse le VPN tranquille.
Écrivez l’arrangement choisi dans le README du projet tant qu’il est frais. Sinon, la personne suivante qui tombera sur ce délai passera le même après-midi à redécouvrir que le tunnel vit sur l’hôte.