Une des sorties de ce poste de travail est un proxy SOCKS, et OpenVPN derrière lui signale un délai de connexion dépassé sans rien d’utile. Un navigateur sur la même machine passe par ce proxy, les identifiants sont corrects et le port répond. Trois choses diffèrent assez souvent pour être vérifiées d’abord : la version SOCKS que parle le proxy, le transport choisi par le client et l’endroit où se fait la résolution de noms. Confirmez chacune d’elles avant de conclure que le pare-feu est en cause.

Aligner la version SOCKS avant le pare-feu

OpenVPN communautaire parle SOCKS5 pour socks-proxy. Pointez-le vers un proxy qui n’offre que SOCKS4 : la connexion TCP s’ouvre, puis se bloque, car le client attend une réponse à une salutation que l’autre extrémité ne sait pas analyser. Vu de l’extérieur, ce blocage ressemble exactement à un port filtré, et c’est pourquoi la question de la version passe avant toute règle de pare-feu. Une requête compatible SOCKS tranche en une seule commande.

curl --socks5-hostname 127.0.0.1:9050 gw.lab.example

Une requête aboutie prouve deux choses à la fois : le proxy a accepté la requête SOCKS et il a résolu le nom. Aucun schéma d’URL n’est nécessaire, car curl traite un hôte nu comme du HTTP, ce qui suffit pour ce test.

Cette requête écarte aussi la panne que le journal du client masque. Un proxy peut autoriser le navigateur et refuser malgré tout la destination VPN ; dans ce cas, le refus se produit au CONNECT, avant toute authentification VPN. OpenVPN ne signale qu’un délai dépassé, donc la réponse du proxy lui-même est la preuve qu’il vous faut.

Le transport doit être TCP des deux côtés

SOCKS5 transporte ici un flux TCP, donc le tunnel lui-même a besoin de proto tcp-client. Un profil encore réglé sur UDP ne fonctionnera pas par ce chemin, car le client n’utilise pas le mode d’association UDP du protocole. Changer le profil est une vraie modification et non une formalité : l’extrémité distante doit écouter en TCP sur le même port. La charge devient alors du TCP dans du TCP, où un segment perdu est retransmis deux fois et le débit se dégrade, ce que les sessions interactives remarquent bien plus que les gros transferts.

proto tcp-client
remote gw.lab.example 8443
socks-proxy 127.0.0.1 9050

La directive prend l’hôte du proxy, un port facultatif et un fichier d’identifiants facultatif. Placez-les dans le profil lui-même pour qu’une copie se comporte à l’identique ailleurs.

Confiner toute la chaîne dans un seul conteneur

Faire la chaîne sur l’hôte revient à reprendre sa route par défaut et à laisser le résolveur dans un état que personne ne saura expliquer plus tard. Un namespace ou un conteneur la borne à la place, si bien que l’hôte garde son propre chemin pendant que le saut SOCKS et le tunnel vivent ensemble. Rien n’y démarre sans un périphérique de tunnel, les droits pour le configurer et un proxy joignable. Un service Compose obtient cela avec cap_add: NET_ADMIN et un /dev/net/tun monté en bind.

cap_add:
  - NET_ADMIN
devices:
  - /dev/net/tun:/dev/net/tun

Ce couple est plus étroit que d’exécuter le conteneur avec tous les privilèges, et il couvre tout l’accès au périphérique nécessaire. Un client Tor ou un conteneur proxy séparé peut fournir l’écouteur. Dans les deux cas, liez-le à loopback et atteignez-le depuis le namespace via l’hôte plutôt que d’exposer le proxy au réseau.

La résolution de noms est le point où l’isolement échoue

Un tunnel qui transporte des paquets tout en interrogeant le résolveur de l’hôte n’a isolé personne. Deux choses rendent l’isolement réel : le namespace possède son propre résolveur, et ses requêtes passent par le proxy ou le tunnel, jamais par l’interface physique. Utilisez deux noms pour le test : un qui n’existe que derrière le tunnel, et un public qui devrait suivre le même chemin.

docker compose exec vpn cat /etc/resolv.conf
docker compose exec vpn getent hosts internal.lab.example

Si le nom distant se résout et que le public non, la chaîne est sélective plutôt que cassée, et la différence vous indique quel chemin a pris chaque requête. Si les deux se résolvent depuis l’hôte mais aucun dans le conteneur, le conteneur emprunte encore le résolveur de l’hôte. Notez le fichier de résolveur trouvé, car c’est le réglage qu’il faut changer.

Prouver chaque saut dans l’ordre

La liste est courte. Une requête SOCKS5 obtient une réponse, le tunnel atteint son écouteur via le proxy, et les paquets qui quittent le namespace portent l’adresse attendue par l’extrémité distante. Confirmez chaque point séparément au lieu d’empiler de la configuration. Quand le client dit « connecté » et que rien ne bouge, la rupture se trouve presque toujours dans le deuxième ou le troisième fait.

curl --socks5-hostname 127.0.0.1:9050 gw.lab.example
docker compose exec vpn ip route get 203.0.113.10
docker compose exec vpn getent hosts internal.lab.example

Notez la combinaison qui a fonctionné : version SOCKS, transport, résolveur et réglages du namespace. Résoudre cette chaîne une seconde fois de mémoire coûte plus cher que la note. Gardez ces quatre faits dans le ticket plutôt que dans un fil de discussion.

Une chaîne qui annonce « connecté » et ne fait rien passer a généralement échoué au saut que vous n’avez pas encore prouvé. Testez ce saut avant d’ajouter une couche de plus.

Articles similaires