Выберите продукт

Kubernetes NetworkPolicy: default deny, egress и DNS без сюрпризов

Пошагово настраиваем Kubernetes NetworkPolicy: вводим default deny для ingress/egress в namespace, отдельно разрешаем DNS (UDP/TCP 53) до CoreDNS и нужный egress. Разбираем нюансы Calico/Cilium, типовые ошибки селекторов и быстрый troubleshoot через kubectl describe и тестовый pod.
Kubernetes NetworkPolicy: default deny, egress и DNS без сюрпризов

Зачем вообще нужен default deny в Kubernetes

Во многих кластерах Kubernetes модель «разрешено всё» действует до тех пор, пока вы не применили ни одной NetworkPolicy: поды могут общаться друг с другом и выходить наружу (egress) без ограничений. На старте это удобно, но в продакшене повышает риск: скомпрометированный контейнер получает простор для lateral movement и вывода данных.

Паттерн default deny делает сеть «закрытой по умолчанию»: сначала запрещаем всё, затем точечно открываем только те направления, которые реально нужны приложению. В итоге проще аудит и меньше «радиус поражения».

NetworkPolicy влияет на трафик только если ваш CNI-плагин реально применяет политики. Если поддержка отсутствует, манифесты создадутся, но связность не изменится.

Calico и Cilium: что важно знать заранее

Для стандартных Kubernetes NetworkPolicy (ingress/egress + селекторы) и Calico, и Cilium обычно подходят. Но на практике чаще всего всплывают три момента:

  • Egress должен быть поддержан CNI (в актуальных Calico/Cilium это норма, но в «упрощённых» установках встречаются ограничения).
  • DNS ломается первым: после default deny egress резолв имён перестаёт работать, пока вы явно не разрешите UDP/TCP 53 до CoreDNS/kube-dns или NodeLocal DNSCache.
  • Расширения вендоров: Cilium умеет L7/FQDN-политики, у Calico есть свои CRD. Здесь — только стандартная NetworkPolicy, чтобы не привязываться к конкретному CNI.

Как понять, применяются ли NetworkPolicy в вашем кластере

Начните с банального: посмотрите, какой CNI у вас установлен и есть ли его поды в kube-system.

kubectl get nodes -o wide
kubectl get pods -n kube-system -o wide

Для Calico часто увидите calico-node, для Cilium — cilium (DaemonSet) и cilium-operator.

Дальше — быстрый sanity check: примените deny-политику в тестовом namespace и попробуйте «прострелить» соединение между подами. Если связность вообще не изменилась, обычно причина одна из двух: политики не применяются CNI или селекторы не совпали с целевыми подами.

Схема default deny в Kubernetes: изоляция pod-to-pod и запрет egress по умолчанию

Default deny: базовые политики для ingress и egress

Частая ошибка: включить только запрет входящих (ingress) и ожидать, что исходящий (egress) тоже закроется. В Kubernetes это независимые направления: Ingress и Egress. Для «полной изоляции» задайте оба.

Default deny для ingress (запрет входящих)

Политика ниже запрещает весь входящий трафик ко всем подам в namespace app.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: app
spec:
  podSelector: {}
  policyTypes:
  - Ingress

Default deny для egress (запрет исходящих)

Эта политика запрещает весь исходящий трафик от всех подов в namespace app.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
  namespace: app
spec:
  podSelector: {}
  policyTypes:
  - Egress

Применение:

kubectl apply -f default-deny-ingress.yaml
kubectl apply -f default-deny-egress.yaml
FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Почему после default deny «падает интернет»: DNS и egress

Почти любое приложение сначала резолвит имена. После default deny egress DNS-запросы до CoreDNS (обычно сервис kube-dns в namespace kube-system) блокируются. Симптомы выглядят как «не подключается база/внешний API», хотя первопричина — отсутствует резолв.

Разрешаем DNS: минимальная egress policy для namespace

Базовый и обычно самый устойчивый вариант — разрешить egress из вашего namespace к подам CoreDNS по UDP и TCP 53. Привязка к IP сервиса тоже возможна, но селекторы обычно долговечнее.

Во многих кластерах CoreDNS помечен лейблом k8s-app: kube-dns. Проверьте это перед применением политики:

kubectl get pods -n kube-system -l k8s-app=kube-dns --show-labels
kubectl get svc -n kube-system kube-dns -o wide

Пример политики, разрешающей DNS только до kube-dns/CoreDNS:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns-egress
  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

Если namespaceSelector не матчится (например, нет лейбла kubernetes.io/metadata.name), добавьте собственный лейбл на namespace kube-system и используйте его в matchLabels.

Разрешаем egress наружу: что можно сделать стандартной NetworkPolicy

Стандартная Kubernetes NetworkPolicy ограничивает egress по:

  • подам/неймспейсам (через селекторы);
  • IP-блокам (через ipBlock);
  • портам/протоколам.

При этом она не умеет разрешать трафик «по доменному имени». Если приложение ходит в сторонний API по FQDN, обычно выбирают одно из решений:

  • разрешить egress на подсети через ipBlock (если диапазоны известны и стабильны);
  • использовать возможности конкретного CNI (например, FQDN-политики) или выводить egress через контролируемую точку (прокси/egress gateway) как часть архитектуры.

Пример: временно разрешить HTTPS «куда угодно» (осознанно)

Иногда нужно быстро вернуть работоспособность и собрать фактический список зависимостей. Тогда можно временно разрешить исходящий трафик на 443 везде, оставив запрет на остальные порты.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-https-egress-any
  namespace: app
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
    ports:
    - protocol: TCP
      port: 443

Это лучше, чем «разрешить всё», но в долгую обычно переходят к allowlist по подсетям или к управляемому egress.

Комбинируем: безопасный базовый набор для namespace

Типовой минимум после включения default deny egress:

  • разрешить DNS к kube-dns/CoreDNS (или NodeLocal DNSCache);
  • разрешить egress к внутренним сервисам по селекторам (БД, очередь, кэш);
  • разрешить egress наружу только на нужные порты и адресные диапазоны.

Для внутреннего доступа удобнее привязываться к лейблам подов, а не к IP. Пример: приложение в namespace app ходит в PostgreSQL в namespace db на 5432, а поды БД имеют лейбл app: postgres.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-egress-to-postgres
  namespace: app
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: db
      podSelector:
        matchLabels:
          app: postgres
    ports:
    - protocol: TCP
      port: 5432

Если часть ваших workloads живёт не в Kubernetes (например, выделенная БД или внешний балансировщик), часто удобнее вынести такие компоненты на отдельный VDS и уже его подсети явно добавить в ipBlock allowlist. Так проще контролировать адреса и маршрутизацию egress.

Проверка и диагностика: kubectl describe и быстрые тесты

Классическая ситуация: «правила созданы, но трафик не ходит». Ниже — чек-лист, который обычно находит причину за минуты.

1) Смотрим, какие политики вообще есть

kubectl get networkpolicy -n app
kubectl describe networkpolicy -n app default-deny-egress
kubectl describe networkpolicy -n app allow-dns-egress

В kubectl describe быстро сверяйте:

  • policy в нужном namespace (ошибка №1);
  • podSelector действительно выбирает нужные поды (пустой {} означает «все поды»);
  • policyTypes содержит нужное направление (если ждёте контроль исходящего, должен быть Egress);
  • селекторы namespace/pod реально совпадают с лейблами на объектах.

2) Проверяем лейблы, на которые вы ссылаетесь

kubectl get ns --show-labels
kubectl get pods -n kube-system --show-labels
kubectl get pods -n app --show-labels

Частая причина «поломанного DNS»: у CoreDNS другой лейбл, чем вы ожидали, или namespaceSelector не совпал.

Диагностика NetworkPolicy: kubectl describe, проверка лейблов и тест DNS/egress из pod

3) Запускаем тестовый pod и проверяем DNS/egress

Для диагностики удобно поднять временный pod в проблемном namespace и запускать тесты оттуда.

kubectl run -n app net-debug --rm -it --restart=Never --image=busybox:1.36 -- sh

Внутри pod:

nslookup kubernetes.default.svc.cluster.local
nslookup example.com
wget -S -O - https://example.com 2>&1 | head

Если nslookup не работает — сначала чините DNS policy. Если DNS работает, но HTTPS нет — проверяйте egress правила на 443/подсети и наличие NAT/маршрутизации для выхода из кластера (это уже вне зоны NetworkPolicy).

4) Помним про Service vs Pod: куда реально идёт трафик

NetworkPolicy применяется на уровне pod. Когда вы обращаетесь к сервису (ClusterIP), фактическое соединение устанавливается до выбранного endpoint-pod. Поэтому в egress-правилах чаще нужно разрешать именно pod-цели через podSelector, а не «IP сервиса».

5) Отдельный класс проблем: DNS топология нестандартная

Иногда в кластере включён NodeLocal DNSCache или CoreDNS развёрнут нестандартно. Тогда политика должна отражать реальную схему:

  • При NodeLocal DNSCache резолв идёт на локальный IP ноды (часто из link-local диапазона). Может понадобиться egress на этот IP и порт 53 через ipBlock.
  • Если CoreDNS в другом namespace или с другими лейблами — исправляйте namespaceSelector/podSelector.

Рекомендации по эксплуатации: как не превратить политики в «хрупкий пазл»

Договоритесь о лейблах

NetworkPolicy держится на селекторах. Если лейблы ставятся хаотично, политики неизбежно становятся «разовыми» и ломкими. Практичный минимум:

  • app — имя приложения/компонента;
  • role — web/api/worker/db;
  • team или owner — владелец.

Храните политики рядом с приложением

Если манифесты NetworkPolicy лежат рядом с Helm chart/деплойментом, изменения зависимостей (добавился внешний API, поменялась БД) легче отражать сразу в том же PR.

Избегайте «0.0.0.0/0 навсегда»

Широкий egress (например, на 0.0.0.0/0) иногда оправдан как временная мера. Но в проде он часто «прилипает». Лучше хотя бы частично перейти на allowlist: порты + подсети, а затем уточнять диапазоны по реальным потребностям.

Короткий чек-лист troubleshoot NetworkPolicy

  1. CNI точно поддерживает NetworkPolicy и egress?
  2. Политика применена в нужном namespace?
  3. podSelector выбирает нужные pod?
  4. policyTypes содержит нужное направление (Ingress/Egress)?
  5. DNS разрешён (UDP и TCP 53) до CoreDNS/NodeLocal DNS?
  6. В правилах вы разрешаете трафик к pod/endpoints (а не к абстрактному «сервису»)?
  7. Тест изнутри pod подтверждает проблему: сначала nslookup, затем wget или nc?

Итог

Kubernetes NetworkPolicy — рабочий способ ввести минимально необходимую сетевую модель: default deny + точечные разрешения для ingress/egress. В 80% случаев после запрета исходящего трафика первым делом нужно явно открыть DNS (UDP/TCP 53) и убедиться, что селекторы совпадают с реальными лейблами. Для диагностики держите под рукой kubectl describe и тестовый pod: это самый быстрый путь понять, где именно пропала связность.

Поделиться статьей

Вам будет интересно

Debian/Ubuntu: mount: wrong fs type, bad option, bad superblock — как быстро найти и исправить причину OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: mount: wrong fs type, bad option, bad superblock — как быстро найти и исправить причину

Ошибка mount: wrong fs type, bad option, bad superblock в Debian/Ubuntu может означать и простую опечатку в имени раздела, и пробл ...
Debian/Ubuntu: XFS metadata corruption и emergency read-only — пошаговое восстановление OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: XFS metadata corruption и emergency read-only — пошаговое восстановление

Если XFS-раздел внезапно стал доступен только для чтения, а сервер ушёл в emergency mode, главное — не спешить. Разберём безопасны ...
Debian/Ubuntu: как исправить Failed to fetch при apt update OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: как исправить Failed to fetch при apt update

Ошибка Failed to fetch при apt update в Debian и Ubuntu обычно связана не с самим APT, а с DNS, сетью, зеркалом, прокси, временем ...