Цикл: «Сетевая безопасность Kubernetes: от основ до практической защиты» · материал 8 из 12
Формат: практический разбор
Для кого: ИБ-специалист, DevOps-инженер, архитектор и разработчик.
Кратко
В Kubernetes словом ingress называют и входящий трафик к workload, и API-объект для публикации HTTP/HTTPS. Это не одно и то же. Ingress API направляет внешний HTTP(S)-трафик к Service; он не заменяет NetworkPolicy, WAF, RBAC и контроль исходящих соединений.
Egress — трафик из Pod к другим системам: DNS, БД, Kubernetes API, внутреннему proxy, облачному metadata service или Интернету. Без отдельной egress-политики скомпрометированный Pod сохраняет канал для загрузки инструмента, командного управления и вывода данных, даже если вход к приложению хорошо защищён.
Безопасная цель — не «запретить Интернет везде», а описать каждую необходимую связь: источник, назначение, протокол, порт, владелец, способ проверки и допустимый путь. Для стандартной NetworkPolicy это обычно Pod/namespace/IP/CIDR и порт; правило «разрешить только api.example.com» требует функции конкретного CNI либо контролируемого proxy/gateway.
Не смешивайте три границы
Рассмотрим опубликованное приложение frontend.
Internet → Gateway / Ingress controller → frontend Pod → backend Pod → внешняя платёжная система
Здесь есть три разные задачи:
| Граница | Вопрос | Типичные механизмы |
|---|---|---|
| Внешняя публикация | Кто и по какому HTTP(S)-маршруту приходит к приложению? | Gateway API или Ingress controller, WAF, TLS, LoadBalancer |
| Внутренний ingress | Какие источники могут подключиться к Pod? | NetworkPolicy ingress, Service mesh, CNI |
| Egress | Куда Pod может установить соединение? | NetworkPolicy egress, egress proxy/gateway, CNI-specific policy, NGFW |
Ingress API предназначен для HTTP/HTTPS-доступа к Service извне. Kubernetes рекомендует для новой функциональности Gateway API: Ingress API остаётся поддерживаемым, но заморожен и не развивается. Это не повод срочно переписывать работающий Ingress, но новый дизайн лучше строить с учётом Gateway API и конкретной реализации контроллера.
Главная ошибка — считать, что закрытый Ingress закрывает приложение целиком. NetworkPolicy ingress не ограничивает egress, а default deny только для ingress не меняет исходящие соединения.
Почему egress — реальная граница ущерба
После выполнения кода в контейнере нарушитель действует от имени этого Pod. Если он может обращаться куда угодно, ему доступны внешние каналы. В зависимости от окружения это могут быть загрузка дополнительного кода, обратное соединение, передача данных, обращение к внутренним административным сервисам и туннелирование через DNS.
Но и без атаки открытый egress мешает расследованию: нет ожидаемого набора направлений, поэтому новое TLS-соединение к незнакомому адресу трудно отличить от рабочей зависимости. Egress allow-list одновременно уменьшает достижимость и делает аномалию заметной.
Это не означает, что нужно выдавать один шаблон deny all всему кластеру. У приложений есть зависимости, которые команда часто не учитывает: DNS, proxy, IdP, БД, мониторинг, журналирование, backup, внутренний API, обновление сертификатов. Блокировка без карты потоков быстро приводит к аварийному allow all.
Начинайте с карты, а не с YAML
Для одного workload составьте минимальную таблицу до изменения политики.
| Источник | Назначение | Протокол/порт | Зачем | Допустимый путь | Как проверить |
|---|---|---|---|---|---|
frontend |
CoreDNS | UDP+TCP/53 | разрешение имён | прямо к DNS Pod | nslookup |
frontend |
backend |
TCP/8080 | API приложения | Service внутри кластера | HTTP-тест |
backend |
PostgreSQL | TCP/5432 | данные | private subnet | сценарий приложения |
backend |
платёжный API | TCP/443 | бизнес-функция | только через proxy | тестовая транзакция |
«Нужен Интернет» — не строка матрицы. Нужны конкретная роль, направление и путь. Если назначение меняет IP или обслуживается SaaS, сначала определяют, чем именно будут выражать правило: устойчивым private endpoint, корпоративным proxy, egress gateway или FQDN-функцией конкретного CNI. Стандартный Kubernetes API здесь не даёт универсального решения.
Базовая модель egress в NetworkPolicy
Pod становится изолированным для egress, когда его выбирает политика с policyTypes: [Egress]. Разрешения всех применимых политик складываются. Следовательно, политика с пустым egress не является «приоритетным запретом», а переводит выбранные Pod в состояние, где допускаются только соединения, разрешённые хотя бы одной подходящей egress-политикой.
Минимальный запрет исходящего трафика для одного namespace выглядит так:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
namespace: app
spec:
podSelector: {}
policyTypes:
- Egress
Этот манифест не затрагивает ingress. Он также блокирует DNS. Поэтому применять его в production до готовых разрешений и тестового окна нельзя.
DNS — первое обязательное разрешение
В кластере приложения обычно используют имена Service. После egress deny curl http://backend:8080 может сообщать не об ошибке backend, а о невозможности резолвинга. Kubernetes прямо предупреждает: default deny egress требует отдельной политики к cluster DNS.
Ниже — типовой вариант только для кластера, где DNS Pod находятся в kube-system и имеют метку k8s-app: kube-dns. Перед применением проверьте реальные labels и endpoint своего DNS.
kubectl -n kube-system get svc kube-dns
kubectl -n kube-system get pods -l k8s-app=kube-dns --show-labels
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: app
spec:
podSelector: {}
policyTypes: [Egress]
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
Проверка должна идти из Pod целевого namespace, а не с node администратора:
kubectl -n app run dnsutils --image=registry.k8s.io/e2e-test-images/agnhost:2.39 \
--restart=Never --command -- sleep infinity
kubectl -n app wait --for=condition=Ready pod/dnsutils --timeout=60s
kubectl -n app exec dnsutils -- nslookup backend.app.svc.cluster.local
Если dnsutils не проходит, не добавляйте 0.0.0.0/0. Сначала проверьте selector, namespace, UDP и TCP/53, фактические DNS endpoints и поддержку NetworkPolicy используемым CNI.
Как разрешить конкретный внешний адрес
Для стабильного корпоративного сервиса или egress proxy стандартный API позволяет использовать ipBlock. Пример ниже не является готовой конфигурацией: адрес 10.90.10.25 и порт 443 должны быть заменены на подтверждённые параметры вашего proxy.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-to-corporate-proxy
namespace: app
spec:
podSelector:
matchLabels:
app: backend
policyTypes: [Egress]
egress:
- to:
- ipBlock:
cidr: 10.90.10.25/32
ports:
- protocol: TCP
port: 443
Такое правило ограничивает destination IP и порт, но не доказывает, что на 443 расположен именно ожидаемый сервис и не описывает HTTP host/path. Проверку проводят настоящим бизнес-запросом через предназначенный proxy, а затем повторяют отрицательный тест к другому адресу/порту. Для ipBlock не используйте Pod CIDR: IP Pod динамичны, а поведение при Service/NAT зависит от реализации сети.
FQDN, proxy и egress gateway: где заканчивается стандарт Kubernetes
Стандартная NetworkPolicy не умеет правило по DNS-имени. Если внешний API часто меняет адреса, выбор между вариантами архитектурный:
- Corporate egress proxy. Workload соединяется только с одним контролируемым proxy, а разрешения по имени, TLS и журналирование выполняются там. Это самый переносимый способ для HTTP(S).
- Private endpoint или фиксированный CIDR. Подходит, если поставщик гарантирует сеть и есть процесс изменения адресов.
- FQDN policy конкретного CNI. Например, Cilium имеет DNS-based policy, но это расширение CNI: нужно отдельно проверять DNS-путь, кэширование, наблюдаемость и отказоустойчивость.
- Egress gateway конкретной реализации. Он может вывести выбранные Pod через выделенный node/IP. Это не встроенная функция Kubernetes NetworkPolicy и не заменяет policy на самом workload.
Не выбирайте FQDN policy только потому, что она звучит точнее. DNS становится частью security boundary: нужно сохранять события разрешения имени, понимать время жизни ответа и проверять, что приложение не обходит ожидаемый DNS/proxy путь.
Проверка до и после изменения
Минимальная приёмка egress-policy состоит из пяти наблюдений:
- Разрешённый внутренний вызов приложения проходит.
- DNS резолвит нужное имя.
- Разрешённая внешняя зависимость проходит только ожидаемым путём.
- Неподтверждённое назначение или порт не проходят.
- В flow telemetry или журнале CNI видны источник, назначение, время и verdict.
Не используйте ping вместо проверки приложения: для ICMP поведение NetworkPolicy зависит от сетевой реализации. Проверяйте TCP/UDP-протокол, который реально использует зависимость. И не тестируйте только один Pod: после пересоздания Deployment повторите allow/deny-проверки, чтобы убедиться, что policy привязана к labels, а не к исчезнувшему IP.
Что часто ломают
Самая опасная ошибка — начать с allow all egress, а потом обещать «ужесточить позднее». Это почти всегда становится постоянным исключением. Вторая ошибка — полагаться только на внешний firewall, который видит worker node как источник и не различает Pod/ServiceAccount. Третья — разрешить 0.0.0.0/0:443 «потому что всё сейчас HTTPS». Это оставляет почти неограниченный канал наружу.
Отдельно проверяйте cloud metadata service. Kubernetes рекомендует ограничивать доступ Pod к metadata API, если он не нужен: такие данные могут дать доступ к инфраструктурным учётным данным. Это отдельная строка матрицы и отдельное отрицательное испытание.
Итог
Ingress определяет, как запрос приходит к приложению. Egress определяет, куда приложение может уйти. Устойчивая защита требует обеих границ, карты зависимостей и функциональных allow/deny-проверок.
Начинайте с одного namespace и одной понятной зависимости. Сначала наблюдение, затем узкие разрешения, пилот и только потом enforcement. Следующая статья покажет, какую телеметрию нужно собрать, чтобы не строить эти политики вслепую.
Официальные источники
- Kubernetes: Network Policies
- Kubernetes: Ingress
- Kubernetes: Security Checklist
- Kubernetes: Multi-tenancy and network isolation
- Cilium: DNS-based policies
- Cilium: Egress Gateway
Навигация по циклу:
← ServiceAccount и RBAC: почему сетевая защита Kubernetes невозможна без контроля API · Начало цикла · Как увидеть, что происходит внутри Kubernetes: flow logs, Audit, DNS и observability →