Un pod OpenVPN în Kubernetes nu redirecționează nimic? Verificați ip_forward
Clienții fac ping la podul VPN și nimic mai departe. Namespace-ul are redirecționarea oprită, iar remedierea trebuie să supraviețuiască admission, repornirilor și căii de întoarcere.
Laptopul tău arată un tunel verde, jurnalul clientului raportează că rutele au fost împinse, iar singura adresă care răspunde este IP-ul tunelului serverului VPN însuși. Pingurile dincolo de el mor indiferent de țintă, fie că este un ClusterIP, adresa altui pod sau un nume de gazdă public. Combinația aceea este diagnostică, nu vagă: decapsularea funcționează, iar al doilea salt nu.
Am întâlnit asta pe un nod Debian care rula o imagine OpenVPN instalată prin chart, iar tunelul era partea ușoară. Clientul a primit un lease 10.8.0.x, 10.8.0.1 a răspuns, iar 10.96.0.10, serviciul DNS al clusterului, a expirat. Nimic din jurnalul serverului nu părea bolnav, și exact de aceea oamenii încep să rescrie reguli iptables de care nu aveau niciodată nevoie să se atingă.
Un pod VPN este un router, și merită spus direct. Pachetele criptate sosesc, sunt despachetate în namespace-ul de rețea al podului însuși, iar apoi trebuie să iasă printr-o altă interfață. Linux refuză acel al doilea pas dacă redirecționarea nu este activată în interiorul aceluiași namespace.
Ce poate și ce nu poate atinge clientul
Adresa care răspunde aparține punctului final al tunelului, iar procesul VPN o deține local, așa că redirecționarea nu este implicată deloc. Orice altceva, inclusiv alt pod, un VIP de serviciu sau internetul, are nevoie ca nucleul să mute un pachet de la tun0 la eth0. Când acea mutare este refuzată, simptomul este tăcere uniformă, nu un refuz, pentru că nimic nu generează o eroare ICMP înapoi pe tunel. Primești timeout-uri, nu mesaje de inaccesibil, și acesta este indiciul că nimeni nu rutează deloc.
Două minute de clasificare îngustează stratul. Dacă numele DNS eșuează în timp ce pingurile brute către un IP de serviciu reușesc, problema este instalația DNS, nu redirecționarea. Dacă fiecare destinație dincolo de adresa peer-ului expiră, inclusiv un IP gol, începe cu namespace-ul.
Citește setarea din interiorul namespace-ului
Fiecare pod primește propriul namespace de rețea, iar net.ipv4.ip_forward este un sysctl pe namespace. Schimbarea lui pe nod nu-ți spune nimic despre container, și este o modalitate sigură de a petrece o după-amiază pe mașina greșită.
kubectl exec -it deploy/openvpn -- sysctl net.ipv4.ip_forward
# net.ipv4.ip_forward = 0
Un zero confirmă diagnosticul. Valoarea nu este moștenită nici din configurația de rulare a nodului: pornește de la valoarea implicită a imaginii de fiecare dată când este creat un container. O remediere aplicată manual cu kubectl exec ... sysctl -w dispare deci la următoarea repornire și revine ca o defecțiune care pare intermitentă.
Activarea ca manifestul să o păstreze
Opțiunea durabilă este un container init care rulează în același pod sandbox și scrie valoarea înainte ca containerul VPN să pornească. Containerele dintr-un pod partajează un namespace de rețea, așa că setarea trece în containerul principal.
initContainers:
- name: enable-forwarding
image: busybox:1.36
command: ["sysctl", "-w", "net.ipv4.ip_forward=1"]
securityContext:
privileged: true
Tocmai privileged: true este linia la care recenzenții obiectează, și au dreptate. Scrierea unui sysctl necesită fie privilegiu, fie o capabilitate strict delimitată, iar un cluster care aplică Pod Security Standard restrictiv poate refuza de plano un container init privilegiat. Dacă admission respinge podul, afli la creare, ceea ce este cel puțin onest și ușor de citit.
Alternativa mai curată este lista securityContext.sysctls la nivel de pod, care permite kubelet-ului să aplice sysctl-uri cu namespace fără a acorda privilegii niciunui container. Acoperă doar nume din lista de permisiuni a clusterului. Dacă net.ipv4.ip_forward este pe acea listă depinde de modul în care a fost configurat kubelet-ul, iar un pod care cere un sysctl în afara ei este respins la admission sau rămâne blocat în CreateContainerConfigError în timp ce sandbox-ul este reîncercat. Verifică lista de permisiuni înainte de a presupune că câmpul este stricat.
Înainte de a construi manual oricare dintre abordări, vezi dacă chart-ul expune deja comutatorul, adesea ca ceva de genul ipForwardInitContainer: true. Folosirea valorii din chart păstrează fișierul tău de suprascriere mic și supraviețuiește următoarei actualizări, ceea ce modificările de Deployment peticite manual nu fac.
A doua jumătate: calea de întoarcere și politica
Redirecționarea pornită este necesară, nu suficientă. Dacă clienții folosesc un pool privat pentru care rețeaua clusterului nu are rută, răspunsurile nu găsesc drumul înapoi în tunel, iar podul trebuie să mascheze acel trafic spre destinația sa.
Politica stă deasupra. Obiectele NetworkPolicy și regulile de firewall din cloud sunt evaluate față de adresa sursă pe care o poartă un pachet după traducere, așa că o regulă scrisă pentru pool-ul de clienți poate înceta în liniște să se potrivească odată ce mascarea este activată.
Când clienții trebuie să atingă doar servicii din interiorul clusterului, terminarea VPN-ului la VIP-ul serviciului este un design mai mic decât rutarea în rețeaua de poduri. Ține și pool-ul de clienți complet în afara tabelei de rutare a CNI.
Izolarea saltului care eșuează de client
Lucrează spre exterior și oprește-te la primul pas silențios. Începe cu peer-ul VPN, apoi încearcă adresa unui pod, apoi un VIP de serviciu și abia după aceea o destinație publică. Fiecare succes mută granița un salt, iar fiecare eșec numește stratul care merită inspectat. După orice implementare, intră cu exec și citește din nou sysctl-ul, pentru că un pod înlocuit pornește de la valoarea implicită a imaginii, iar o remediere care nu a fost niciodată în manifest a dispărut deja.
Documentația Kubernetes despre folosirea sysctl-urilor într-un cluster enumeră ce nume pot fi setate prin contextul de securitate al podului, și este primul loc unde te uiți când admission refuză schimbarea.