OpenVPN под во Kubernetes не препраќа ништо? Проверете ip_forward
Клиентите го пингаат VPN подот и ништо понатаму. Namespace-от има исклучено препраќање, а поправката мора да го преживее admission, рестартирањата и повратниот пат.
Вашиот лаптоп прикажува зелен тунел, клиентскиот лог известува дека рутите се турнати, а единствената адреса што одговара е сопствената тунел IP на VPN серверот. Пинговите понатаму умираат без оглед на тоа во што целите, било да е ClusterIP, адреса на друг под или јавно име на домаќин. Таа комбинација е дијагностичка, а не нејасна: декапсулацијата работи, а вториот скок не.
На ова наидов на Debian јазол што извршуваше OpenVPN слика инсталирана преку chart, и тунелот беше лесниот дел. Клиентот доби закуп 10.8.0.x, 10.8.0.1 одговори, а 10.96.0.10, кластерската DNS услуга, истече. Ништо во серверскиот лог не изгледаше болно, и токму затоа луѓето почнуваат да ги препишуваат iptables правилата што никогаш не требало да ги допрат.
VPN подот е рутер, и вреди тоа да се каже директно. Шифрираните пакети пристигнуваат, се отвораат во сопствениот мрежен namespace на подот, а потоа мора да излезат преку друг интерфејс. Linux го одбива тој втор чекор освен ако препраќањето не е овозможено внатре во истиот тој namespace.
Што клиентот може, а што не може да достигне
Адресата што одговара му припаѓа на крајот на тунелот, а VPN процесот ја поседува локално, така што препраќањето воопшто не учествува. Сето друго, вклучително друг под, VIP на услуга или интернетот, бара кернелот да премести пакет од tun0 на eth0. Кога тоа преместување е одбиено, симптомот е еднообразна тишина, а не одбивање, зашто ништо не генерира ICMP грешка назад низ тунелот. Добивате истеци, а не пораки за недостапност, и тоа е трагата дека никој воопшто не рутира.
Две минути класификација го стеснуваат слојот. Ако DNS имињата паѓаат додека суровите пингови до IP на услуга успеваат, проблемот е во DNS водоводот, не во препраќањето. Ако секоја дестинација зад адресата на peer истекува, вклучително голо IP, почнете од namespace-от.
Прочитајте го поставувањето внатре во namespace-от
Секој под добива свој мрежен namespace, а net.ipv4.ip_forward е sysctl за секој namespace. Неговото менување на јазолот не ви кажува ништо за контејнерот, и тоа е сигурен начин да поминете попладне на погрешната машина.
kubectl exec -it deploy/openvpn -- sysctl net.ipv4.ip_forward
# net.ipv4.ip_forward = 0
Нулата ја потврдува дијагнозата. Вредноста исто така не се наследува од конфигурацијата при извршување на јазолот: таа почнува од стандардната вредност на сликата секој пат кога се создава контејнер. Затоа поправка применета рачно со kubectl exec ... sysctl -w исчезнува при следното рестартирање и се враќа како дефект што изгледа повремен.
Овозможување така што манифестот ќе го задржи
Трајната опција е init контејнер што работи во истиот pod sandbox и ја запишува вредноста пред да стартува VPN контејнерот. Контејнерите во подот делат мрежен namespace, така што поставувањето преминува во главниот контејнер.
initContainers:
- name: enable-forwarding
image: busybox:1.36
command: ["sysctl", "-w", "net.ipv4.ip_forward=1"]
securityContext:
privileged: true
Токму privileged: true е редот на кој рецензентите се спротивставуваат, и со право. Пишувањето sysctl бара или привилегија, или тесно ограничена способност, а кластер со ограничен Pod Security Standard може целосно да одбие привилегиран init контејнер. Ако admission го одбие подот, тоа го дознавате при создавањето, што е барем чесно и лесно за читање.
Почиста алтернатива е списокот securityContext.sysctls на ниво на под, што му овозможува на kubelet да применува namespace-ски sysctl без да дава привилегија на ниту еден контејнер. Опфаќа само имиња од списокот на дозволени на кластерот. Дали net.ipv4.ip_forward е на тој список зависи од тоа како е конфигуриран kubelet, а под што бара sysctl надвор од него се одбива при admission или останува заглавен во CreateContainerConfigError додека sandbox-от се обидува повторно. Проверете го списокот на дозволени пред да претпоставите дека полето е расипано.
Пред рачно да изградите кој било од двата пристапи, проверете дали chart-от веќе го изложува прекинувачот, често како нешто како ipForwardInitContainer: true. Користењето на вредноста од chart-от ја одржува вашата override датотека мала и преживува следното надградување, што рачно закрпените измени на Deployment не го прават.
Втората половина: повратен пат и политика
Вклученото препраќање е неопходно, но не и доволно. Ако клиентите користат приватен базен за кој кластерската мрежа нема рута, одговорите не наоѓаат пат назад во тунелот, и подот мора да го маскира тој сообраќај кон својата дестинација.
Политиката стои врз тоа. Објектите NetworkPolicy и правилата на облачниот огнен ѕид се оценуваат спрема изворната адреса што пакетот ја носи по преводот, така што правило напишано за базенот на клиенти може тивко да престане да се совпаѓа штом маскирањето е овозможено.
Кога клиентите треба да достигнат само услуги внатре во кластерот, завршувањето на VPN на VIP-от на услугата е помал дизајн отколку рутирање во под-мрежата. Тоа исто така го држи базенот на клиенти целосно надвор од табелата за рутирање на CNI.
Изолирање на скокот што паѓа од клиентот
Работете нанадвор и застанете на првиот тивок чекор. Почнете со VPN peer, потоа пробајте адреса на некој под, потоа VIP на услуга, и само потоа јавна дестинација. Секој успех ја поместува границата за еден скок, и секој неуспех го именува слојот што вреди да се прегледа. По секое распоредување влезете со exec и прочитајте го sysctl повторно, зашто заменетиот под почнува од стандардната вредност на сликата, а поправка што никогаш не била во манифестот веќе ја нема.
Документацијата на Kubernetes за користење sysctl во кластер наведува кои имиња можат да се постават преку безбедносниот контекст на подот, и тоа е првото место каде што треба да се погледне кога admission ја одбива промената.