OpenVPN pod в 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 да прилага namespaced sysctl, без да дава привилегия на контейнер. Той покрива само имена от allowlist на клъстъра. Дали net.ipv4.ip_forward е в този списък зависи от конфигурацията на kubelet, а под, който поиска sysctl извън него, се отхвърля при admission или засяда в CreateContainerConfigError, докато sandbox се повтаря. Проверете allowlist, преди да приемете, че полето е счупено.
Преди да изградите ръчно който и да е от двата подхода, вижте дали chart вече не излага превключвателя, често като нещо от рода на ipForwardInitContainer: true. Използването на стойността от chart поддържа override файла ви малък и оцелява при следващото обновяване, което ръчно кърпените промени в Deployment не правят.
Втората половина: обратен път и политика
Включеното препредаване е необходимо, но не достатъчно. Ако клиентите използват частен пул, за който клъстърната мрежа няма маршрут, отговорите не могат да намерят пътя обратно в тунела и подът трябва да маскира този трафик към своята дестинация.
Политиката стои отгоре. Обектите NetworkPolicy и правилата на облачния защитен щит се оценяват по адреса на източника, който пакетът носи след транслация, така че правило, написано за клиентския пул, може тихо да спре да съвпада, след като маскирането е включено.
Когато клиентите трябва да достигнат само услуги в клъстъра, спирането на VPN на VIP на услуга е по-малък дизайн от рутиране в мрежата на подовете. Той също така държи клиентския пул напълно извън таблицата за маршрутизация на CNI.
Изолиране на отказалия хоп от клиента
Работете навън и спрете на първата тиха стъпка. Започнете с VPN peer, след това опитайте адреса на някой под, после VIP на услуга и само след това публична дестинация. Всеки успех мести границата с един хоп, а всеки провал назовава слоя, който си струва да се прегледа. След всяко разгръщане влезте и прочетете sysctl отново, защото замененият под започва от подразбиращата се за образа стойност и поправка, която никога не е била в манифеста, вече липсва.
Документацията на Kubernetes за използване на sysctl в клъстър изброява кои имена могат да се задават чрез контекста за сигурност на пода и това е първото място, което да погледнете, когато admission откаже промяната.