ADG Оставить заявку
Блог Информационная безопасность 5 мин чтения

Микросегментация в Kubernetes через Cilium: модель угроз, правила и проверка через Hubble

Внедрили Cilium Network Policy в K8s-кластере после рекомендаций НКЦКИ по lateral movement: описываем модель угроз, правила Ingress/Egress и как Hubble помогает проверить изоляцию.

Контекст момента

Рост атак через lateral movement - НКЦКИ и BSS рекомендуют микросегментацию как ключевую меру 2024

НКЦКИ в этом году прямым текстом включил микросегментацию в список приоритетных мер против атак через lateral movement. BSS в своём отчёте за первое полугодие нарисовал примерно ту же картину: как только атакующий получает точку входа в периметр, дальше он движется по внутренней сети практически без сопротивления. В большинстве Kubernetes-кластеров, с которыми мы работаем, это ровно так и устроено - поды общаются со всеми подряд, и никакого принципа least privilege внутри кластера нет.

Решили закрыть этот пробел на одном из проектов. Инструмент выбрали Cilium: он работает на уровне eBPF, не требует sidecar-проксей, и в нём есть Hubble - встроенный инструмент наблюдаемости, без которого проверить, что политики реально работают, было бы заметно сложнее.

Модель угроз: что мы защищали

Прежде чем писать правила, разобрались - от чего именно защищаемся. Три сценария, которые нас беспокоили.

Первый - компрометация одного пода и горизонтальное движение. Атакующий получает RCE в поде, например через уязвимость в web-приложении. Без сегментации у него сразу доступ ко всем остальным подам в кластере: базам данных, сервисам внутреннего API, очередям сообщений. Задача - чтобы скомпрометированный под мог общаться только с тем, с чем ему положено по логике приложения.

Второй - утечка данных через неожиданный egress. Под отправляет данные на внешний адрес. В стандартной конфигурации Kubernetes это никак не ограничено. Мы хотим, чтобы поды с доступом к чувствительным данным не могли выходить в интернет напрямую - только через явно разрешённые маршруты.

Третий - атака на управляющую плоскость кластера. Скомпрометированный под не должен иметь возможности обращаться к API-серверу Kubernetes или etcd. Это базовая изоляция, которую нередко забывают сделать.

Как устроены политики

Cilium Network Policy работает поверх стандартных Kubernetes NetworkPolicy, но с более богатой семантикой: можно фильтровать не только по namespace и label, но и по DNS-именам, HTTP-путям, протоколам L7. Для большинства наших случаев хватило L3/L4.

Базовый принцип - default deny для всего namespace, потом явные разрешения. Выглядит это примерно так: сначала вешается CiliumNetworkPolicy с policyTypes Ingress и Egress без единого правила - всё запрещено. Потом для каждого деплоймента прописываются точные разрешения.

Для backend-сервиса, который общается только с базой данных и очередью:

  • Ingress - разрешить трафик только от frontend-namespace по label app: frontend.
  • Egress - разрешить только до подов с label app: postgres на порт 5432 и до подов с label app: rabbitmq на 5672. Плюс CoreDNS на 53/UDP - без этого DNS не работает, и поды не могут ресолвить имена вообще.

Для web-frontend отдельная история: ему нужен egress наружу для запросов к внешним API. Здесь мы использовали Cilium-specific возможность - FQDN-based policy. Вместо разрешения по IP прописываем разрешённые DNS-имена. Это чище, чем таскать за собой список IP, которые имеют привычку меняться.

Политику для доступа к API-серверу решили отдельно: явный блок egress на CIDR control-plane нод для всех namespace, кроме системных. Это немного грубо, но надёжно.

DNS - самый неочевидный момент

Первые тесты показали: поды теряют DNS сразу после включения default deny, даже если CoreDNS разрешён. Причина - CoreDNS сам ходит наружу за ответами на рекурсивные запросы. Нужно разрешить Egress из namespace kube-system к upstream DNS-серверам, иначе резолюция ломается для всего кластера.

Второй момент с DNS: FQDN-политики Cilium работают через механизм перехвата DNS-ответов. Cilium видит, какой IP вернул DNS для разрешённого имени, и временно добавляет его в разрешённый список. Если TTL у записи короткий и IP меняется быстро - бывают небольшие окна, когда трафик блокируется. В нашем случае это не было проблемой, но знать стоит.

Hubble: как проверяли, что работает

Писать политики вслепую - это боль. Hubble решает именно эту задачу: он показывает реальные потоки трафика между подами с пометкой forwarded / dropped и ссылкой на конкретную политику, которая заблокировала или пропустила трафик.

Мы использовали два инструмента в связке. Hubble UI - для первичного осмотра: видно граф зависимостей, можно быстро найти неожиданные соединения, которые мы не учли в политиках. Hubble CLI - для точечной диагностики: hubble observe --namespace production --verdict DROP выдаёт в реальном времени всё заблокированное. Это позволило поймать несколько соединений, которые мы честно не знали, что они существуют - оказалось, что один сервис периодически обращается к соседнему namespace за конфигом, и это нигде не было задокументировано.

После включения политик прогнали smoke-тесты приложения и параллельно смотрели в Hubble на DROP-события. Если что-то падало - сразу видели, какое именно соединение заблокировалось, и либо добавляли правило, либо шли разбираться, зачем вообще это соединение существует.

Что получилось

Политики работают, приложение работает, Hubble показывает ожидаемую картину. Несколько соединений, которые мы обнаружили в процессе, оказались техническим долгом: сервисы общались напрямую там, где должны были ходить через API-gateway. Часть исправили сразу, часть зафиксировали как задачу.

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

Главный вывод из этого проекта: микросегментация в Kubernetes технически не сложна. Сложная часть - инвентаризация реальных зависимостей перед тем, как писать правила. Если этот шаг пропустить и сразу включать default deny - получите шквал дропов и сломанное приложение. Если сначала дать Hubble понаблюдать за трафиком в режиме без блокировок, а потом переходить к политикам - всё становится значительно управляемее.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.