OpenVPN-pod i Kubernetes vidarebefordrar ingenting? Kontrollera ip_forward
Klienterna pingar VPN-poden och ingenting längre. Namespace-et har vidarebefordran avstängd, och rättelsen måste överleva admission, omstarter och returvägen.
Din laptop visar en grön tunnel, klientloggen rapporterar att rutter skickades, och den enda adressen som svarar är VPN-serverns egen tunnel-IP. Ping bortom den dör oavsett vad du siktar på, vare sig det är en ClusterIP, en annan pods adress eller ett offentligt värdnamn. Den kombinationen är diagnostisk och inte vag: dekapsuleringen fungerar, och det andra hoppet inte.
Jag stötte på detta på en Debian-nod som körde en chart-installerad OpenVPN-avbild, och tunneln var den lätta delen. Klienten fick en 10.8.0.x-lease, 10.8.0.1 svarade, och 10.96.0.10, klustrets DNS-tjänst, tog timeout. Ingenting i serverloggen såg sjukt ut, och det är precis därför folk börjar skriva om iptables-regler som de aldrig behövde röra.
En VPN-pod är en router, och det är värt att säga rakt ut. Krypterade paket anländer, packas upp i podens eget nätverksnamnrymd och måste sedan ut genom ett annat gränssnitt. Linux vägrar det andra steget om inte vidarebefordran är aktiverad inne i just den namnrymden.
Vad klienten kan och inte kan nå
Adressen som svarar tillhör tunnelns ändpunkt, och VPN-processen äger den lokalt, så vidarebefordran är inte inblandad alls. Allt annat, inklusive en annan pod, en tjänste-VIP eller internet, kräver att kärnan flyttar ett paket från tun0 till eth0. När den flytten avvisas är symtomet jämn tystnad och inte en avvisning, eftersom ingenting genererar ett ICMP-fel tillbaka nedför tunneln. Du får tidsgränser, inte oåtkomliga-meddelanden, och det är ledtråden att ingen routar alls.
Två minuters klassificering snävar in lagret. Om DNS-namn misslyckas medan råa ping mot en tjänste-IP lyckas, ligger problemet i DNS-rördragningen, inte i vidarebefordran. Om varje destination bortom peer-adressen tar timeout, även en bar IP, börja vid namnrymden.
Läs inställningen inne i namnrymden
Varje pod får sin egen nätverksnamnrymd, och net.ipv4.ip_forward är en sysctl per namnrymd. Att ändra den på noden säger dig ingenting om containern, och det är ett pålitligt sätt att tillbringa en eftermiddag på fel maskin.
kubectl exec -it deploy/openvpn -- sysctl net.ipv4.ip_forward
# net.ipv4.ip_forward = 0
En nolla bekräftar diagnosen. Värdet ärvs inte heller från nodens körtidskonfiguration: det börjar på avbildens standard varje gång en container skapas. En rättelse som använts för hand med kubectl exec ... sysctl -w försvinner därför vid nästa omstart och kommer tillbaka som ett fel som verkar intermittent.
Aktivera det så att manifestet behåller det
Det hållbara alternativet är en init-container som körs i samma pod-sandbox och skriver värdet innan VPN-containern startar. Containrar i en pod delar en nätverksnamnrymd, så inställningen följer med in i huvudcontainern.
initContainers:
- name: enable-forwarding
image: busybox:1.36
command: ["sysctl", "-w", "net.ipv4.ip_forward=1"]
securityContext:
privileged: true
Just privileged: true är raden som granskarna invänder mot, och de har rätt. Att skriva en sysctl kräver antingen privilegium eller en snävt avgränsad förmåga, och ett kluster som kör den restriktiva Pod Security Standard kan avvisa en privilegierad init-container helt. Om admission avvisar poden får du veta det vid skapandet, vilket åtminstone är ärligt och lätt att läsa.
Det renare alternativet är securityContext.sysctls-listan på podnivå, som låter kubelet tillämpa namnavgränsade sysctlar utan att ge privilegier till någon container. Den täcker bara namn på klustrets allowlist. Om net.ipv4.ip_forward står på den listan beror på hur kubelet konfigurerades, och en pod som ber om en sysctl utanför den avvisas vid admission eller fastnar i CreateContainerConfigError medan sandboxen försöks igen. Kontrollera allowlisten innan du antar att fältet är trasigt.
Innan du bygger någon av metoderna för hand, se om chartet redan exponerar strömbrytaren, ofta som något i stil med ipForwardInitContainer: true. Att använda chartvärdet håller din override-fil liten och överlever nästa uppgradering, vilket handpatchade Deployment-ändringar inte gör.
Den andra halvan: returväg och policy
Vidarebefordran på är nödvändigt, inte tillräckligt. Om klienter använder en privat pool som klusternätet inte har någon rutt till, hittar svaren inte vägen tillbaka in i tunneln, och poden måste maskera den trafiken mot sitt mål.
Policy sitter ovanpå. NetworkPolicy-objekt och molnets brandväggsregler utvärderas mot källadressen som ett paket bär efter översättning, så en regel skriven för klientpoolen kan tyst sluta matcha när maskningen är aktiverad.
När klienter bara behöver nå tjänster inne i klustret är det en mindre konstruktion att låta VPN:en termineras på tjänstens VIP än att routa in i podnätet. Det håller också klientpoolen helt utanför CNI:ns routingtabell.
Isolera det misslyckade hoppet från klienten
Arbeta utåt och stanna vid det första tysta steget. Börja med VPN-peeren, försök sedan med någon pods adress, sedan en tjänste-VIP och först därefter ett offentligt mål. Varje framgång flyttar gränsen ett hopp, och varje misslyckande namnger lagret som är värt att undersöka. Efter varje utrullning går du in och läser sysctlen igen, eftersom en ersatt pod startar från avbildens standard och en rättelse som aldrig stod i manifestet redan är borta.
Kubernetes-dokumentationen om att använda sysctls i ett kluster listar vilka namn som kan sättas genom podens säkerhetskontext, och det är första stället att titta när admission avvisar ändringen.