Kubernetes NetworkPolicy: как eBPF находит скрытые сетевые пути

📋 Кратко

NetworkPolicy задаёт желаемые правила, но сама не фильтрует пакеты. Решение принимает сетевой плагин, а eBPF помогает увидеть фактический путь трафика. Разбираем hostNetwork, DNS, адреса узлов и пробелы egress. Показываем, как сопоставить разрешённые потоки с политиками и проверить, кто именно открыл соединение.

⏱ 9 минут чтения│сложность
Один успешный запрос не подтверждает изоляцию
Один успешный запрос не подтверждает изоляцию

🔍 Почему зелёный тест не доказывает изоляцию

В Kubernetes часто начинают с простого теста. Из одного пода выполняют запрос к другому поду. Затем проверяют ожидаемый отказ. Если разрешённый запрос проходит, а запрещённый блокируется, тест считают успешным.

Политика существует отдельно от фактической фильтрации
Политика существует отдельно от фактической фильтрации

Такой подход проверяет только заранее выбранные маршруты. Он не показывает все направления трафика. За пределами проверки остаются DNS, адреса узлов, служебные компоненты и поды с особым сетевым режимом.

NetworkPolicy описывает желаемое правило. Сам объект Kubernetes API не фильтрует ни одного пакета. Реальную проверку выполняет реализация pod-сети. Она подключает под к сети и программирует сетевой путь на рабочем узле.

Главная мысль: успешный curl показывает только один проверенный маршрут. Изоляцию подтверждает не один ответ приложения, а согласованная картина политик, сетевого плагина и событий из ядра.

Прямо сейчас составьте список разрешённых направлений. Добавьте в него DNS, служебные адреса и доступ к узлам. Затем проверьте каждый пункт по сетевым событиям.

⚙️ Что именно делает NetworkPolicy

NetworkPolicy задаёт правила для входящего и исходящего трафика. В основном речь идёт об уровнях L3 и L4. Политика описывает адреса, поды, пространства имён и порты.

Телеметрия связывает пакет с реальным процессом
Телеметрия связывает пакет с реальным процессом

Такое правило не видит смысл запроса. Оно не определяет, какой процесс сформировал соединение. Оно также не заменяет контроль доступа к API Kubernetes, файловой системе узла или возможностям ядра.

Результат зависит от сетевой реализации. Если CNI не поддерживает NetworkPolicy, YAML можно успешно создать. При этом трафик останется открытым. Поэтому наличие объекта в API ещё не доказывает наличие фильтрации.

eBPF помогает построить сетевой путь в Linux. Он не является самостоятельным универсальным межсетевым экраном. Его польза появляется при связке правил, подходящей версии ядра, контролируемой конфигурации и телеметрии.

  • API Kubernetes хранит NetworkPolicy и сведения о подах.
  • CNI подключает поды к сети и применяет правила.
  • eBPF может участвовать в обработке пакетов и сборе событий.
  • Телеметрия показывает, почему поток разрешён или заблокирован.

Перед проверкой выясните, какой CNI работает в кластере. Отдельно подтвердите, что он применяет NetworkPolicy, а не только принимает её объект.

🛠️ Как eBPF связывает пакет с источником

Обычный сетевой тест отвечает на один вопрос: прошёл ли запрос. Для расследования нужен другой вопрос: кто его отправил и каким путём. eBPF помогает получать события из сетевого пути на узле.

HostNetwork меняет источник и ломает привычную модель
HostNetwork меняет источник и ломает привычную модель

В этих событиях можно связать поток с подом, пространством имён, службой и процессом. Такая связь показывает разницу между заявленной политикой и фактическим поведением. Она особенно важна, когда один под запускает несколько процессов.

Cilium Hubble показывает решение сетевого пути. Событие содержит статус FORWARDED или DROPPED. Также доступны идентификаторы источника и получателя. Для DNS видны запросы через прокси.

Tetragon отвечает на вопрос о процессе. Он показывает процесс, под, аргументы и пару адресов с портами. В результате аналитик видит не только сетевой адрес, но и действие внутри пода.

Разделяйте роли инструментов: Hubble показывает, пропустил ли поток сетевой путь. Tetragon помогает понять, какой процесс создал соединение. Один инструмент не заменяет другой.

Начните с небольшого набора событий. Сопоставьте один разрешённый поток и один отказ. Проверьте, видны ли под, пространство имён и процесс.

⚠️ HostNetwork ломает привычную модель

Параметр hostNetwork меняет сетевую модель пода. Процесс работает прямо в сетевом пространстве имён узла. У такого пода нет собственного виртуального интерфейса в pod-сети.

DNS раскрывает сведения даже при закрытом порте
DNS раскрывает сведения даже при закрытом порте

В EndpointSlice такой под попадает с IP узла. При этом обычные pod-политики могут не охватить его трафик. Для NetworkPolicy источник выглядит не так, как обычный под с собственным сетевым интерфейсом.

На тестовом стенде обычные проверки сначала показали ожидаемый результат. Клиент обращался к backend. Чужой под не получал доступ. Backend подключался к базе на разрешённом порту.

Затем проверка включила под с hostNetwork. Он подключился к базе, к которой политика разрешала доступ только backend. Hubble показал FORWARDED от имени host. При этом в событии не было пода и пространства имён.

\p>

Этот результат не означает, что eBPF автоматически исправляет проблему. Он показывает расхождение между моделью политики и фактическим источником. Событие помогает увидеть, что решение принято для узла, а не для ожидаемого пода.

Проверьте все манифесты на hostNetwork. Для каждого такого пода отдельно определите допустимые адреса и порты. Не считайте обычную pod-политику достаточной.

🌐 DNS показывает путь, который забывают проверить

DNS часто считают вспомогательным трафиком. На практике он раскрывает имена сервисов и помогает проводить разведку внутри кластера. Закрытый порт базы не мешает узнать имя сервиса.

Изоляция требует постоянного сопоставления правил и потоков
Изоляция требует постоянного сопоставления правил и потоков

Примером служит имя db.shop.svc.cluster.local. Даже если соединение с портом базы запрещено, разрешённый DNS-запрос раскрывает внутреннее имя. Это не доказывает доступ к базе, но показывает дополнительный путь получения сведений.

Отдельная проблема возникает, когда политика разрешает DNS без точного контроля резолвера. В кластере может работать NodeLocal DNS. Его адрес находится вне обычной pod-сети. В тестовом стенде использовался адрес 169.254.25.10, который передавал запросы CoreDNS.

Поэтому нужно проверять не только наличие разрешения на порт DNS. Важно увидеть фактический адрес назначения. Также нужно понять, какой компонент отвечает на запрос и связан ли он с ожидаемым маршрутом.

Практический ориентир: разрешение DNS должно быть частью проверяемой матрицы. Фиксируйте резолвер, адрес назначения и пространства имён, которым разрешён такой трафик.

Сейчас соберите события DNS из eBPF-телеметрии. Сравните резолверы с утверждённым списком. Отдельно проверьте обращения к именам сервисов из разных пространств имён.

🧭 Где ещё появляются скрытые направления

Pod-политика не описывает всю сеть узла. За пределами pod-сети находятся NodeLocal DNS, kubelet и адреса нод. Эти компоненты могут появиться в событиях вместо ожидаемого пода.

Особого внимания требуют NodePort, LoadBalancer и Ingress. Эти механизмы меняют путь между внешним клиентом, узлом и сервисом. Простая проверка pod-to-pod не показывает такой маршрут.

То же относится к внешним IP-адресам. Правило может разрешать выход из пода, но не объяснять, почему приложение установило соединение с конкретным внешним адресом. Без потоковой телеметрии источник и назначение легко перепутать.

Нужно учитывать и взаимодействие с системными компонентами. Политика может действовать в одном пространстве имён, но не охватывать системные поды. Поэтому deny-by-default в прикладном пространстве не означает изоляцию всего кластера.

  • Проверьте hostNetwork и доступ к сети узла.
  • Сопоставьте NodePort, LoadBalancer и Ingress с фактическими потоками.
  • Выделите NodeLocal DNS, CoreDNS, kubelet и адреса нод.
  • Проверьте внешние IP и все правила egress.
  • Отдельно рассмотрите системные пространства имён.

Начните аудит с карты сетевых точек входа и выхода. Для каждой точки запишите источник, назначение и компонент, который принимает решение.

📊 Что показывает испытательный стенд

Проверка проходила на стенде разработки в облаке. В нём было 11 узлов Kubernetes. Три узла выполняли роль control-plane, пять обслуживали рабочую нагрузку, ещё три предназначались для инфраструктурных компонентов.

На виртуальных машинах работали Ubuntu 24.04 и ядро 6.8. Сетевым плагином был Cilium 1.20.4. Он использовал замену kube-proxy и VXLAN. В стенде также работали Hubble Relay и Hubble UI.

В кластере разместили frontend, backend, db и client. Отдельно добавили обычный под attacker. Для проверки сетевого режима использовали hostnet-same и hostnet-remote. Эти поды работали с hostNetwork на разных узлах.

Политики задали через deny-all для входящего и исходящего трафика. Затем добавили точечные разрешения. Client мог обращаться к backend на порту 80. Backend мог обращаться к db на порту 5432. DNS разрешили для нужных компонентов.

Пять базовых проверок совпали с ожиданиями. Client получил доступ к backend. Attacker не получил доступ к backend. Client не подключился к db. Backend подключился к db. DNS-запрос client к backend прошёл.

На этом этапе тест выглядел успешным. Но отдельная проверка hostNetwork показала другую картину. Такой под смог подключиться к базе, хотя правило разрешало доступ только backend.

Вывод из стенда: правильная матрица разрешений не гарантирует правильную модель источника. Нужно проверять не только результат соединения, но и то, как сетевой путь идентифицирует отправителя.

Повторите проверку в своём тестовом кластере. Добавьте обычный под и под с hostNetwork. Сравните их события в Hubble и привязку к процессам.

🔬 Как читать расхождение между политикой и потоком

Начинайте с ожидаемой матрицы. В ней укажите источник, назначение, порт и причину разрешения. Затем добавьте фактический статус потока. Так видно не только нарушение, но и лишнее разрешение.

Если поток получил статус DROPPED, это ещё не объясняет всю ситуацию. Нужно проверить источник и получателя. Ошибка может быть связана с неверной меткой, пространством имён или отсутствием поддержки политики в CNI.

Статус FORWARDED также не означает проблему. Поток может быть разрешён намеренно. Важно понять, соответствует ли он действующему правилу. Особое внимание уделяйте событиям, где вместо пода указан host.

Сопоставляйте четыре уровня данных:

  1. Желаемое правило NetworkPolicy.
  2. Конфигурацию сетевого плагина и режим его работы.
  3. Сетевое событие с адресами и статусом.
  4. Процесс, под и пространство имён, создавшие соединение.

Такой порядок снижает риск ошибочного вывода. Например, разрешённый DNS не равен разрешённому доступу к базе. А отказ для одного пода не доказывает отказ для hostNetwork.

Возьмите один поток с неожиданным назначением. Найдите его в телеметрии. Затем сравните источник, процесс и правило, которое должно было его ограничить.

🛡️ Чек-лист защиты и постоянной проверки

Сначала проведите инвентаризацию. Соберите все NetworkPolicy, пространства имён, сервисы и поды. Отдельно отметьте системные компоненты и поды с hostNetwork.

Затем проверьте модель отказа. Для прикладных пространств имён должен существовать понятный deny-by-default. После этого правила ingress и egress нужно добавлять точечно. Каждое исключение должно иметь владельца и объяснение.

Проверьте CNI. Уточните, поддерживает ли он NetworkPolicy. Зафиксируйте режим работы, версию ядра и включённые функции наблюдаемости. Нельзя переносить возможности одного сетевого плагина на другой.

Для каждой проверки собирайте сетевые события. Смотрите FORWARDED и DROPPED. Проверяйте identity источника и получателя. Для DNS фиксируйте запросы. Для спорных случаев связывайте поток с процессом через Tetragon.

  • Найти все политики и проверить их область действия.
  • Подтвердить deny-by-default для ingress и egress.
  • Проверить hostNetwork, привилегированные контейнеры и сеть узла.
  • Проверить NodePort, LoadBalancer, Ingress и внешние IP.
  • Проверить DNS и адреса NodeLocal DNS.
  • Сопоставить каждый разрешённый поток с конкретным правилом.
  • Повторить тесты после изменения CNI, ядра или сетевой схемы.

Сделайте этот список частью приёмки кластера. После каждого изменения запускайте тесты из обычного пода и из системных сценариев.

🚀 Как встроить проверку в рабочий процесс

Проверку лучше проводить не разово, а при изменении архитектуры. Новый сервис меняет не только приложение. Он может добавить Ingress, внешний адрес, DNS-запросы или новое правило egress.

Для каждой заявки на изменение создавайте короткую сетевую матрицу. В ней должны быть разрешённые источники, назначения, порты и пространства имён. После применения правил сравнивайте матрицу с событиями из ядра.

Не ограничивайтесь тестом из одного контейнера. Проверяйте обычный под, под из другого пространства имён и системный сценарий. Если в архитектуре есть hostNetwork, тестируйте его отдельно.

Телеметрия должна помогать разбирать инциденты. Сохраняйте статус потока, адреса, identity и сведения о процессе. Без этих данных команда видит факт соединения, но не всегда понимает его источник.

При этом eBPF не заменяет другие меры. Он не даёт контроль над RBAC, образом контейнера, файловой системой узла и возможностями ядра. NetworkPolicy также не решает все задачи контроля доступа.

Рабочая модель: NetworkPolicy задаёт границы, CNI применяет их, eBPF показывает фактический путь. Только вместе эти уровни дают проверяемую картину сетевой изоляции.

Внедрите контроль расхождений в процесс изменений. Считайте проблему найденной, если разрешённый поток не объясняется правилом или его источник не совпадает с ожидаемым подом.

📚 Читайте также

📖 Термины

Container Security · Kubernetes · Threat Hunting · Микросегментация · Сетевой трафик

🔗 Источники