проверять и ограничивать каждое соединение между подами
Сеть с нулевым доверием в Kubernetes: сетевые политики и mTLS с Cilium
14 минут
В Kubernetes по умолчанию любой под достигает любого другого. Сочетайте запрет по умолчанию через NetworkPolicy, взаимную аутентификацию на SPIRE и принуждение в eBPF через Cilium — сегментируйте внутрикластерный трафик и доказывайте идентичность сервиса без sidecar на каждом поде.
Почему «плоская» сеть Kubernetes не тянет продакшен
Большинство кластеров всё ещё работают в режиме «разрешено по умолчанию»: любой под может достичь любого другого в соседних пространствах имён, пока это явно не запрещено. В небольшом staging с доверенными сервисами это удобно. В продакшене с десятками команд, границами соответствия требованиям и мультитенантными нагрузками — уязвимость. Три проблемы ведут к инцидентам. Горизонтальное перемещение: скомпрометированный под в платежах сканирует базы, внутренние API и кэши в других namespace, потому что граница доверия по умолчанию плоская. Нет идентичности сервиса: TLS на ingress защищает трафик «север–юг», а вызовы «восток–запад» часто идут по под-сети без шифрования и без доказательства, что вызывающий авторизован — любая нагрузка в сети может выдать себя за клиента. Операционное сопротивление: сегментация на ручных правилах iptables или закрытых API вендора не успевает за ежедневными деплоями. Нужна политика рядом с кодом приложения, в Git, с единым принуждением на всех кластерах. Без слоя сети с нулевым доверием периметр крепкий, а двор внутри открыт.
Авторизация, идентичность и принуждение в eBPF — одна модель
Нулевое доверие здесь — три дополняющих слоя. NetworkPolicy — авторизация: какие поды с какими собеседниками, на каких портах и из каких namespace; правила файрвола в терминах объектов Kubernetes, а не таблиц IP. Запрет по умолчанию на уровне namespace плюс явные разрешения по меткам дают сильную мультитенантную сегментацию без неуправляемых списков CIDR. mTLS — идентичность: политика говорит, кому можно подключаться, но только взаимный TLS доказывает, кто реально шлёт байты. Обе стороны предъявляют сертификаты; подмена и атаки «запутанного заместителя» на транспортном уровне отсекаются до логики приложения. Cilium — движок принуждения: программы eBPF применяют NetworkPolicy, взаимную аутентификацию и наблюдаемость Hubble на уровне сокета, без длинных цепочек iptables. Правила опираются на метки подов, а не на хрупкие диапазоны IP. Hubble показывает потоки L3/L4/L7 и вердикты политик без лишних агентов. Прозрачная взаимная аутентификация через SPIRE избавляет от sidecar на каждом поде для базового доверия «восток–запад», а встроенный Envoy включает L7-правила HTTP там, где нужны метод и путь.
Этап 0: карта реального трафика в Hubble до любых запретов
Главная причина провала миграции — запрет по умолчанию до понимания легитимных потоков. Поставьте Cilium с Hubble relay, UI и метриками отброшенных пакетов, наблюдайте минимум один полный бизнес-цикл — пакетные задания, ночные cron, обработка конца месяца. Выгрузите уникальные пары «источник — назначение» в чертёж политик. Только после этого включайте принуждение по одному namespace за раз.
helm repo add cilium https://helm.cilium.io
helm repo update
helm upgrade --install cilium cilium/cilium \
--version 1.16.0 \
--namespace kube-system \
--set hubble.enabled=true \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true \
--set hubble.metrics.enabled="{dns,drop,tcp,flow,port-distribution}"hubble observe --since 168h --output json | \
jq -r '[.source.namespace, .source.pod.name, .destination.namespace, .destination.pod.name] | @tsv' | \
sort -u > observed-flows.tsvЭтапы 1–2: запрет по умолчанию в namespace, затем явные разрешения
Включайте deny-all для ingress и egress в одном прикладном namespace за раз. Обязательно добавьте явное egress-правило на kube-dns — без него имя не резолвится, и это ломает всё молча. Проверяйте через hubble observe, что легитимные потоки проходят, прежде чем переходить к следующему namespace. По файлу observed-flows.tsv добавляйте точечные ingress-разрешения: шлюз frontend к API на 8080, API к Postgres на 5432. Для правил между namespace используйте метки namespace, внутри namespace — метки подов: метки namespace меняются редко, метки подов задают границы сервисов.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: payments
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: payments
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: 53apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-api
namespace: payments
spec:
podSelector:
matchLabels:
app: payments-api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: frontend
podSelector:
matchLabels:
app: web-gateway
ports:
- protocol: TCP
port: 8080apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-api-to-postgres
namespace: payments
spec:
podSelector:
matchLabels:
app: postgres
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: payments-api
ports:
- protocol: TCP
port: 5432Этап 3: взаимная аутентификация через SPIRE и Cilium
Взаимная аутентификация Cilium (Beta) проверяет идентичность нагрузки через SPIFFE/SPIRE до передачи данных. Включите authentication и установку SPIRE в Helm, затем добавьте authentication.mode required в ingress-правила CiliumNetworkPolicy. Первый пакет нового соединения может кратко отбрасываться, пока агенты завершают рукопожатие — на холодных соединениях возможен короткий retry. Выпуск и ротация сертификатов — у агента Cilium и SPIRE; в образах приложений не храните файлы сертификатов. Для требований шифрования транспорта дополнительно к идентичности включите WireGuard или IPsec в Helm.
helm upgrade cilium cilium/cilium \
--namespace kube-system \
--reuse-values \
--set authentication.enabled=true \
--set authentication.mutual.spire.enabled=true \
--set authentication.mutual.spire.install.enabled=trueapiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: mtls-payments-api
namespace: payments
spec:
endpointSelector:
matchLabels:
app: payments-api
ingress:
- fromEndpoints:
- matchLabels:
app: web-gateway
io.kubernetes.pod.namespace: frontend
authentication:
mode: "required"
toPorts:
- ports:
- port: "8080"
protocol: TCPЭтап 4: L7-правила HTTP для контроля поверхности API
Сетевые разрешения отсекают чужих собеседников, но не мешают авторизованному поду вызвать админские эндпоинты. Для HTTP CiliumNetworkPolicy задаёт метод и путь через встроенный Envoy на выбранных портах. Даже когда mTLS доказал идентичность, L7 ограничивает, какие URL ей доступны — глубина защиты для внутренних API.
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: l7-payments-api
namespace: payments
spec:
endpointSelector:
matchLabels:
app: payments-api
ingress:
- fromEndpoints:
- matchLabels:
app: web-gateway
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: "GET"
path: "/api/v1/(balance|transactions).*"
- method: "POST"
path: "/api/v1/transfer"Практика: Git, уровни политик, мониторинг и безопасность выполнения
Храните NetworkPolicy и CiliumNetworkPolicy в том же Git-репозитории, что Helm-чарты или Kustomize — каждое изменение через ревью запроса на слияние с историей отката. Метрики drop и TCP-потоков Hubble включайте с первого дня: политика, блокирующая readiness probe, каскадом вызывает сбой — алертируйте на новые вердикты DROPPED в критичных namespace. В общих кластерах платформенные ограничения выносите в CiliumClusterwideNetworkPolicy — блок metadata облака, неожиданный egress из kube-system — чтобы политики арендаторов не ослабляли глобальную сегментацию. Ротацию сертификатов автоматизируйте: короткоживущие идентичности SPIRE лучше годовых сертификатов, после которых отзыв бессмысленен. Новые политики гоняйте в staging на захваченных логах потоков до продакшена; регрессии в три часа ночи дороги. Дополняйте сегментацию сети инструментами выполнения — Falco, Tetragon — для неожиданных процессов, записи в ФС и доступа к учётным данным внутри подов. Admission Policy as Code в Kyverno или Gatekeeper может требовать метки NetworkPolicy на каждом namespace до деплоя нагрузок — чтобы стандарты в документации совпадали с принуждением. Нулевое доверие проверяется на каждом слое, а не только на ingress.
Запрет по умолчанию дополняет базовую линию из практического руководства по усилению безопасности Kubernetes в продакшене.
Потоки в Hubble и datapath на eBPF подробнее разобраны в гайде по наблюдаемости ядра с eBPF и Cilium Hubble.
