Un pod OpenVPN dans Kubernetes ne transmet rien ? Vérifiez ip_forward
Les clients pinguent le pod VPN et rien au-delà. L’espace de noms a le transfert désactivé, et le correctif doit survivre à l’admission, aux redémarrages et au chemin de retour.
Votre portable affiche un tunnel vert, le journal du client indique que des routes ont été poussées, et la seule adresse qui répond est l’IP de tunnel du serveur VPN lui-même. Les pings au-delà meurent quoi que vous visiez, que ce soit un ClusterIP, l’adresse d’un autre pod ou un nom d’hôte public. Cette combinaison est diagnostique et non vague : la décapsulation fonctionne, et le deuxième saut non.
Je l’ai rencontré sur un nœud Debian exécutant une image OpenVPN installée par chart, et le tunnel était la partie facile. Le client a pris un bail 10.8.0.x, 10.8.0.1 a répondu, et 10.96.0.10, le service DNS du cluster, a expiré. Rien dans le journal du serveur ne semblait malade, et c’est exactement pourquoi les gens se mettent à réécrire des règles iptables qu’ils n’ont jamais eu besoin de toucher.
Un pod VPN est un routeur, et il vaut la peine de le dire franchement. Les paquets chiffrés arrivent, sont déballés dans l’espace de noms réseau propre au pod, puis doivent sortir par une autre interface. Linux refuse cette deuxième étape tant que le transfert n’est pas activé à l’intérieur de cet espace de noms précis.
Ce que le client peut atteindre et ce qu’il ne peut pas
L’adresse qui répond appartient au point de terminaison du tunnel, et le processus VPN la possède localement, donc aucun transfert n’entre en jeu. Tout le reste, y compris un autre pod, un VIP de service ou Internet, exige que le noyau déplace un paquet de tun0 vers eth0. Quand ce déplacement est refusé, le symptôme est un silence uniforme plutôt qu’un rejet, car rien ne génère d’erreur ICMP en retour dans le tunnel. Vous obtenez des délais d’attente, pas des messages d’inaccessibilité, et c’est l’indice que personne ne route du tout.
Deux minutes de classification resserrent la couche. Si les noms DNS échouent alors que des pings bruts vers une IP de service réussissent, le problème est la plomberie DNS, pas le transfert. Si chaque destination au-delà de l’adresse du pair expire, y compris une IP nue, commencez par l’espace de noms.
Lisez le réglage à l’intérieur de l’espace de noms
Chaque pod reçoit son propre espace de noms réseau, et net.ipv4.ip_forward est un sysctl par espace de noms. Le changer sur le nœud ne vous dit rien du conteneur, et c’est un moyen fiable de passer un après-midi sur la mauvaise machine.
kubectl exec -it deploy/openvpn -- sysctl net.ipv4.ip_forward
# net.ipv4.ip_forward = 0
Un zéro confirme le diagnostic. La valeur n’est pas non plus héritée de la configuration d’exécution du nœud : elle part de la valeur par défaut de l’image à chaque création de conteneur. Un correctif appliqué à la main avec kubectl exec ... sysctl -w disparaît donc au redémarrage suivant et revient comme un défaut qui semble intermittent.
L’activer pour que le manifeste le conserve
L’option durable est un conteneur init qui s’exécute dans le même pod sandbox et écrit la valeur avant que le conteneur VPN démarre. Les conteneurs d’un pod partagent un espace de noms réseau, donc le réglage se propage au conteneur principal.
initContainers:
- name: enable-forwarding
image: busybox:1.36
command: ["sysctl", "-w", "net.ipv4.ip_forward=1"]
securityContext:
privileged: true
Ce privileged: true est la ligne à laquelle les relecteurs s’opposent, et ils ont raison. Écrire un sysctl demande soit un privilège, soit une capacité strictement délimitée, et un cluster appliquant le Pod Security Standard restreint peut refuser purement et simplement un conteneur init privilégié. Si l’admission rejette le pod, vous l’apprenez à la création, ce qui est au moins honnête et facile à lire.
L’alternative plus propre est la liste securityContext.sysctls au niveau du pod, qui permet au kubelet d’appliquer des sysctls d’espace de noms sans accorder de privilège à aucun conteneur. Elle ne couvre que les noms de la liste autorisée du cluster. Que net.ipv4.ip_forward y figure dépend de la configuration du kubelet, et un pod qui demande un sysctl hors de cette liste est refusé à l’admission ou bloqué dans CreateContainerConfigError pendant que le sandbox est réessayé. Vérifiez la liste autorisée avant de supposer que le champ est cassé.
Avant de construire à la main l’une ou l’autre approche, voyez si le chart expose déjà l’interrupteur, souvent sous une forme comme ipForwardInitContainer: true. Utiliser la valeur du chart garde votre fichier de surcharge petit et survit à la prochaine mise à niveau, ce que les modifications de Deployment patchées à la main ne font pas.
La seconde moitié : chemin de retour et politique
Le transfert activé est nécessaire, pas suffisant. Si les clients utilisent un pool privé pour lequel le réseau du cluster n’a pas de route, les réponses ne retrouvent pas le chemin du tunnel, et le pod doit masquer ce trafic vers sa destination.
La politique se superpose. Les objets NetworkPolicy et les règles de pare-feu du cloud sont évalués contre l’adresse source que porte un paquet après traduction, donc une règle écrite pour le pool de clients peut cesser silencieusement de correspondre une fois le masquage activé.
Quand les clients n’ont besoin d’atteindre que des services à l’intérieur du cluster, faire terminer le VPN sur le VIP du service est une conception plus petite que router vers le réseau des pods. Cela garde aussi le pool de clients entièrement hors de la table de routage du CNI.
Isoler le saut défaillant du client
Travaillez vers l’extérieur et arrêtez-vous au premier pas silencieux. Commencez par le pair VPN, puis essayez l’adresse d’un pod, puis un VIP de service, et seulement après une destination publique. Chaque succès déplace la frontière d’un saut, et chaque échec nomme la couche qui mérite une inspection. Après tout déploiement, entrez et relisez le sysctl, car un pod remplacé repart de la valeur par défaut de l’image et un correctif qui n’a jamais été dans le manifeste a déjà disparu.
La documentation Kubernetes sur l’utilisation des sysctls dans un cluster liste les noms qui peuvent être définis via le contexte de sécurité du pod, et c’est le premier endroit à consulter quand l’admission refuse le changement.