Ihr Laptop zeigt einen grünen Tunnel, das Client-Log meldet, dass Routen gepusht wurden, und die einzige Adresse, die antwortet, ist die eigene Tunnel-IP des VPN-Servers. Pings darüber hinaus sterben, egal worauf Sie zielen, ob ClusterIP, die Adresse eines anderen Pods oder ein öffentlicher Hostname. Diese Kombination ist diagnostisch und nicht vage: Die Dekapsulierung funktioniert, und der zweite Hop nicht.

Ich bin darauf auf einem Debian-Node mit einem per Chart installierten OpenVPN-Image gestoßen, und der Tunnel war der einfache Teil. Der Client bekam ein 10.8.0.x-Lease, 10.8.0.1 antwortete, und 10.96.0.10, der DNS-Dienst des Clusters, lief in einen Timeout. Im Server-Log sah nichts ungesund aus, und genau deshalb beginnen Leute, iptables-Regeln neu zu schreiben, die sie nie hätten anfassen müssen.

Ein VPN-Pod ist ein Router, und das sollte man klar sagen. Verschlüsselte Pakete kommen an, werden im eigenen Netzwerk-Namespace des Pods entpackt und müssen dann über eine andere Schnittstelle hinaus. Linux verweigert diesen zweiten Schritt, solange die Weiterleitung nicht innerhalb genau dieses Namespace aktiviert ist.

Was der Client erreicht und was nicht

Die antwortende Adresse gehört zum Tunnel-Endpunkt, und der VPN-Prozess besitzt sie lokal, sodass gar keine Weiterleitung beteiligt ist. Alles andere, einschließlich eines anderen Pods, eines Service-VIP oder des Internets, braucht den Kernel, um ein Paket von tun0 nach eth0 zu bewegen. Wird diese Bewegung verweigert, ist das Symptom gleichmäßige Stille statt einer Ablehnung, weil nichts einen ICMP-Fehler den Tunnel hinunter erzeugt. Sie bekommen Timeouts, keine Unerreichbar-Meldungen, und das ist der Hinweis, dass überhaupt niemand routet.

Zwei Minuten Klassifikation grenzen die Schicht ein. Wenn DNS-Namen scheitern, während rohe Pings auf eine Service-IP gelingen, liegt das Problem in der DNS-Verkabelung, nicht in der Weiterleitung. Wenn jedes Ziel jenseits der Peer-Adresse in einen Timeout läuft, auch eine nackte IP, fangen Sie beim Namespace an.

Lesen Sie die Einstellung im Namespace

Jeder Pod bekommt seinen eigenen Netzwerk-Namespace, und net.ipv4.ip_forward ist ein Sysctl pro Namespace. Ihn auf dem Node zu ändern sagt Ihnen nichts über den Container, und es ist ein zuverlässiger Weg, einen Nachmittag auf der falschen Maschine zu verbringen.

kubectl exec -it deploy/openvpn -- sysctl net.ipv4.ip_forward
# net.ipv4.ip_forward = 0

Eine Null bestätigt die Diagnose. Der Wert wird auch nicht aus der Laufzeitkonfiguration des Nodes geerbt: Er startet bei jedem Erstellen eines Containers vom Image-Standard. Ein manuell mit kubectl exec ... sysctl -w gesetzter Fix verschwindet daher beim nächsten Neustart und kommt als scheinbar sporadischer Fehler zurück.

Aktivieren, damit das Manifest es behält

Die dauerhafte Option ist ein Init-Container, der in derselben Pod-Sandbox läuft und den Wert schreibt, bevor der VPN-Container startet. Container in einem Pod teilen sich einen Netzwerk-Namespace, also trägt die Einstellung in den Hauptcontainer.

initContainers:
- name: enable-forwarding
  image: busybox:1.36
  command: ["sysctl", "-w", "net.ipv4.ip_forward=1"]
  securityContext:
    privileged: true

Genau privileged: true ist die Zeile, gegen die Reviewer Einwände erheben, und zu Recht. Einen Sysctl zu schreiben braucht entweder ein Privileg oder eine eng gefasste Capability, und ein Cluster mit dem eingeschränkten Pod Security Standard kann einen privilegierten Init-Container rundweg ablehnen. Wenn die Admission den Pod ablehnt, erfahren Sie es beim Erstellen, was wenigstens ehrlich und leicht zu lesen ist.

Die sauberere Alternative ist die securityContext.sysctls-Liste auf Pod-Ebene, mit der der kubelet namespacede Sysctls anwenden kann, ohne einem Container Privilegien zu geben. Sie deckt nur Namen auf der Allowlist des Clusters ab. Ob net.ipv4.ip_forward auf dieser Liste steht, hängt davon ab, wie der kubelet konfiguriert wurde, und ein Pod, der einen Sysctl außerhalb davon anfordert, wird bei der Admission abgelehnt oder hängt in CreateContainerConfigError, während die Sandbox erneut versucht wird. Prüfen Sie die Allowlist, bevor Sie annehmen, das Feld sei defekt.

Bevor Sie einen der beiden Ansätze von Hand bauen, sehen Sie nach, ob das Chart den Schalter bereits anbietet, oft als etwas wie ipForwardInitContainer: true. Den Chart-Wert zu nutzen hält Ihre Override-Datei klein und übersteht das nächste Upgrade, was handgepatchte Deployment-Änderungen nicht tun.

Die zweite Hälfte: Rückweg und Policy

Weiterleitung an ist notwendig, nicht ausreichend. Wenn Clients einen privaten Pool nutzen, für den das Clusternetz keine Route hat, finden Antworten nicht zurück in den Tunnel, und der Pod muss diesen Verkehr zu seinem Ziel maskieren.

Die Policy sitzt obendrauf. NetworkPolicy-Objekte und Cloud-Firewall-Regeln werden gegen die Quelladresse geprüft, die ein Paket nach der Übersetzung trägt, sodass eine für den Client-Pool geschriebene Regel still aufhören kann zu passen, sobald das Maskieren aktiv ist.

Wenn Clients nur Dienste innerhalb des Clusters erreichen müssen, ist es ein kleineres Design, das VPN am Service-VIP enden zu lassen, als in das Pod-Netz zu routen. Es hält den Client-Pool auch vollständig aus der Routing-Tabelle des CNI.

Den fehlerhaften Hop vom Client isolieren

Arbeiten Sie nach außen und stoppen Sie beim ersten stillen Schritt. Beginnen Sie beim VPN-Peer, versuchen Sie dann die Adresse irgendeines Pods, dann einen Service-VIP und erst danach ein öffentliches Ziel. Jeder Erfolg verschiebt die Grenze um einen Hop, und jeder Fehlschlag benennt die Schicht, die es zu untersuchen lohnt. Nach jedem Rollout gehen Sie hinein und lesen den Sysctl erneut, denn ein ersetzter Pod startet vom Image-Standard, und ein Fix, der nie im Manifest stand, ist bereits weg.

Die Kubernetes-Dokumentation zu Sysctls in einem Cluster listet, welche Namen über den Security-Kontext des Pods gesetzt werden können, und dort schauen Sie zuerst nach, wenn die Admission die Änderung ablehnt.

Ähnliche Artikel