Модуль 33. Политики и безопасность на уровне сети
Цель: чётко ограничить, кто с кем и по каким протоколам общается внутри кластера и наружу, и шифровать трафик «по умолчанию».
Результат:
- Во всех «прод» namespace действует default-deny (ingress+egress).
- Разрешены только необходимые коммуникации: DNS, Ingress→App, App→DB/кэш/шина, мониторинг.
- Для сервисов в мэше — mTLS STRICT + Istio AuthorizationPolicy на методы/пути/принципы.
- Внешний трафик — через Egress-шлюз (mesh) или через egress-политику (CNI).
- Наблюдаемость: flow-логирование/хабблы + Kiali/Kibana/Grafana; регрессии ловим тестами «policy-probe».
Две «плоскости» защиты: как они сочетаются
- L3/L4 (NetworkPolicy) — ограничение IP/портов между Pod’ами/Namespace (enforce CNI: Calico, Cilium и др.).
- L7/идентичность (Service Mesh: Istio) — mTLS (шифрование + аутентификация Workload’ов), политики доступа по ServiceAccount/SPIFFE-идентичности, HTTP-пути/методы/хедеры.
- Принцип: «сначала L3/L4 закрыть всё, затем L7 разрешить нужное и детализировать». Это оборонительные слои, а не взаимоисключающие механизмы.
Проектируем модель изоляции между namespace
Шаги:
- Размечаем namespace: tenant=<команда>, роли: role=app/db/ingress/ops.
- Включаем default-deny в каждом «прод» ns (и ingress, и egress).
- Даём общие «шлюзы»: к DNS (CoreDNS), к Ingress (чтобы трафик извне попадал к backend), к мониторингу/логам.
- Для кросс-тенантных сервисов (например, общий Trino, ClickHouse, Kafka) — точечные разрешения: только из нужных ns в нужные Pods/порты.
- Внешний доступ: либо Egress Gateway (Istio), либо egress-policy/FQDN-policy (Cilium/Calico Enterprise), либо статические ipBlock.
Важно знать о NetworkPolicy:
- Политика действует только на Pods, которые она выбирает (по podSelector).
- Как только у Pod появляется хотя бы одна Ingress-policy — весь прочий вход блокируется, если явно не разрешён; аналогично по Egress.
- «Порядка» у NetworkPolicy нет — это совокупное «разрешение»/«запрещение» по всем подходящим политикам.
Мини-шаблоны (только самое главное)
Default-deny + DNS + «внутри своего namespace»
# 1) Default deny: ingress + egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: default-deny }
spec:
podSelector: {} # все Pod'ы в ns
policyTypes: ["Ingress","Egress"]
# 2) Разрешить DNS (CoreDNS в kube-system) и внутринеймспейсовые связи
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: allow-dns-and-same-ns }
spec:
podSelector: {}
policyTypes: ["Egress","Ingress"]
ingress:
- from:
- namespaceSelector: { matchLabels: { kubernetes.io/metadata.name: "<текущий-ns>" } }
egress:
- to: # DNS (CoreDNS)
- namespaceSelector: { matchLabels: { kubernetes.io/metadata.name: "kube-system" } }
podSelector: { matchLabels: { k8s-app: "kube-dns" } }
ports: [{ protocol: UDP, port: 53 }, { protocol: TCP, port: 53 }]
- to:
- namespaceSelector: { matchLabels: { kubernetes.io/metadata.name: "<текущий-ns>" } }
Разрешить трафик только от Ingress-контроллера
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: allow-from-ingress }
spec:
podSelector: { matchLabels: { app: my-api } }
policyTypes: ["Ingress"]
ingress:
- from:
- namespaceSelector: { matchLabels: { role: ingress } }
podSelector: { matchLabels: { app.kubernetes.io/name: ingress-nginx } }
ports: [{ protocol: TCP, port: 8080 }]
Egress только к БД/кэшу (и ни шагу больше)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: allow-egress-db-cache }
spec:
podSelector: { matchLabels: { app: my-api } }
policyTypes: ["Egress"]
egress:
- to:
- namespaceSelector: { matchLabels: { role: db } }
podSelector: { matchLabels: { app: postgres } }
ports: [{ protocol: TCP, port: 5432 }]
- to:
- namespaceSelector: { matchLabels: { role: cache} }
podSelector: { matchLabels: { app: redis } }
ports: [{ protocol: TCP, port: 6379 }]
Примечание: пробы kubelet/Ingress-health-checks идут с IP ноды/ingress-pod, это учитываем в from. Для внешних адресов используйте ipBlock (CIDR офис/DMZ) — аккуратно, это «жёсткая» привязка.
Istio: mTLS, авторизация и egress
mTLS (шифрование + идентичность)
- SPIFFE-идентичность вида spiffe://<trust-domain>/ns/<namespace>/sa/<serviceaccount>.
- Включаем STRICT mTLS на весь mesh или на ns/workload: PeerAuthentication со STRICT.
- Для внешних клиентов — Ingress Gateway (TLS от внешнего до гейтвея и mTLS от гейтвея к сервису).
L7-доступ (кто к чему может ходить)
- AuthorizationPolicy: разрешения на основе principal (SA/группа), namespace, IP, путь/метод/заголовки.
- RequestAuthentication: валидация JWT/OIDC; далее AuthorizationPolicy по claims (роль/tenant).
Управляем исходящим трафиком
- Egress Gateway + ServiceEntry: белые списки внешних хостов, TLS origination на гейтвее, аудит и троттлинг.
- Sidecar (ресурс) — «урежьте» конфигурацию прокси, чтобы pod видел только нужные кластеры (ускоряет и повышает безопасность).
Как комбинировать с NetworkPolicy
- Сначала NetworkPolicy ограничивает L3/L4: кто может вообще «достучаться» до pod или egress’а.
- Затем Istio внутри этого контекста решает: кто именно (SA/tenant) и что именно (URI/метод) может.
- Для сервисов вне mesh — NetworkPolicy остаётся основной защитой.
Мини-эскизы (идея):
PeerAuthentication STRICT (namespace-wide):
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata: { name: default, namespace: team-a }
spec: { mtls: { mode: STRICT } }
AuthorizationPolicy: разрешить только сервисам из team-a бить в my-api на /api/
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata: { name: allow-team-a, namespace: apps }
spec:
selector: { matchLabels: { app: my-api } }
rules:
- from:
- source: { principals: ["cluster.local/ns/team-a/sa/*"] }
to:
- operation: { paths: ["/api/*"], methods: ["GET","POST"] }
Egress-стратегии: что выбрать
- Mesh-подход (рекомендуем для прод): весь исходящий трафик — через Egress Gateway. Плюсы: аудит, централизованные сертификаты, SNI-контроль, политика «белого списка».
- CNI-подход: NetworkPolicy egress c ipBlock/FQDN-policy (зависит от CNI). Плюсы: проще, минусы: сложнее контролировать TLS, SNI, динамику IP.
- Гибрид: критичные домены — через Egress GW; остальное — egress-policy с ограничениями.
Наблюдаемость и тестирование политики
-
L3/L4 потоки:
- Calico: flow logs (Felix), политика-аудит (GlobalNetworkPolicy с режимом log).
- Cilium: Hubble (UI/CLI) — видимость потоков/вердиктов «ALLOW/DENY».
- L7:
- Kiali: сервис-карта, мьютексы, mTLS-статус, запреты по AuthorizationPolicy.
- kubectl run -it --rm netshoot --image nicolaka/netshoot → curl, dig, nc.
- Cilium connectivity test; Calico networkPolicy tooling.
- Автотесты в CI: «пробники» из тестового ns, которые уверяют, что запрещённое действительно запрещено (negative tests).
- Проверки:
Риски и анти-паттерны
|
Риск |
Проявление |
Что делать |
|---|---|---|
|
Политика «не работает» |
Pod видит всех |
Вы не выбрали Pod’ы (podSelector пустой? лейблы не совпали?) |
|
Закрыли DNS |
Внезапные таймауты |
Явно разрешить egress к CoreDNS (TCP/UDP 53) |
|
Пробы/Ingress «упали» |
readiness/liveness 5xx |
Разрешить ingress от ns ingress и/или ipBlock нод |
|
Сервис-мэш «шунтирует» NPolicy |
Неожиданные обходы |
Учитывайте redirection iptables; egress/ingress через sidecar/шлюз; проверяйте совместимость CNI+Istio |
|
ipBlock на динамику |
Меняются IP, политика «рвётся» |
Mesh Egress или FQDN-policy (Cilium/Calico Ent) |
|
Лишние «дыры» на общий сервис |
Любой tenant бьёт в БД |
Ограничить по namespaceSelector/podSelector + Istio AuthorizationPolicy по SA |
|
«Открыли интернет на 0.0.0.0/0» |
Данные утекли наружу |
Белые списки доменов/подсетей, Egress GW, аудит |
|
mTLS PERMISSIVE навсегда |
Нет гарантии шифрования |
Временно на миграцию; финал — STRICT mesh-wide |
Практика (лабораторка 1–2 дня)
- Пометить namespace: tenant=team-a, role=app; для Ingress-ns — role=ingress.
- Включить default-deny + DNS + «внутри ns».
- Разрешить Ingress→App и App→DB/кэш точечно.
- Установить Istio (или использовать имеющийся): включить mTLS STRICT в ns, применить AuthorizationPolicy «только team-a».
- Поднять Egress Gateway и разрешить внешний доступ лишь к 2 доменам.
- Проверить: netshoot-под → запрещённые/разрешённые направления, Kiali/Hubble — граф и вердикты.
- Включить «аудит-политики» (логгирование отказов) и настроить алерты на всплеск DENY.
Чек-лист «готово к продакшену»
- Во всех прод-ns действует default-deny (Ingress+Egress).
- DNS, Ingress, мониторинг/логирование — явно разрешены.
- Кросс-тенантные доступы — точечно по ns/pod/портам.
- Istio: mTLS STRICT, AuthorizationPolicy по SA/пути/методу; Ingress/Egress Gateway.
- Egress наружу — только белыми списками (Egress GW или egress-policy/FQDN).
- Наблюдаемость: flow-логи/Hubble + Kiali; алерты на всплески DENY.
- Автотесты «policy-probe» в CI; документация «кто к кому может».
- Runbook’и: «упали пробы/Ingress», «сломался DNS», «блокировался Egress», «миграция PERMISSIVE→STRICT».
Вопрос-ответ
В: Если у нас Istio с mTLS, NetworkPolicy уже не нужна?
О: Нужна. Istio решает кто (SA/тенант) и что (URI/метод) на L7 и шифрует трафик; NetworkPolicy — куда/откуда по IP/порту на L3/L4. Это разные уровни защиты, их комбинируют.
В: Как аккуратно включить mTLS STRICT, чтобы не «уронить» старые клиенты?
О: Делайте поэтапно: mesh-wide PERMISSIVE, проверка совместимости, затем по namespace/workload перевод в STRICT, мониторьте 4xx/5xx и Hubble/Kiali. Финал — STRICT по всему пути.
В: Как дать Prometheus собирать метрики из всех ns и не пробить изоляцию?
О: Разрешите Ingress в таргеты только от prometheus-Pod’ов (namespace monitoring) и на порт метрик. В Istio — отдельная AuthorizationPolicy по SA Prometheus.
В: Что делать с внешними API по FQDN (динамические IP)?
О: В мэше — Egress Gateway + ServiceEntry. В CNI — FQDN-policy (если поддерживается, напр., Cilium). Статические ipBlock годятся только если IP предсказуемы.
В: У нас Jobs/CronJobs, им тоже нужны правила?
О: Да. Политики применяются к Pod’ам, не к «долгоживущим» Deployment. Учитывайте, что Jobs создают новые Pod’ы — метки/селекторы должны их «подхватывать».
В: Пробы от kubelet/ingress ломаются после включения политики. Почему?
О: Источник — IP ноды или ingress-Pod. Разрешите from для соответствующего ns/подсети (ipBlock), либо привяжите по меткам Pod’ов Ingress.
В: Мэш «замедляет» трафик. Что с производительностью?
О: mTLS и Envoy добавляют накладные расходы. Снизить: Sidecar-ресурс (урезать конфиг), отключить лишние фильтры, использовать ambient/пер-ns там, где поддерживается, и обязательно тестировать p95/p99.
Надёжная сетевая безопасность в Kubernetes — это многоуровневая дисциплина: NetworkPolicy закрывает избыточные соединения на уровне IP/портов; Istio даёт mTLS и тонкую авторизацию на уровне идентичностей и HTTP. Добавьте Egress-стратегию, наблюдаемость потоков и автотесты политик — и ваш кластер перестанет быть «плоской сетью», превращаясь в предсказуемую и управляемую среду для BI/DWH.



