Блокировка IP в Kubernetes: NetworkPolicy, CNI и диагностика трафика | AdminWiki

Блокировка IP в Kubernetes: NetworkPolicy, CNI и диагностика трафика

24 сентября 2026 12 мин. чтения

Где именно блокируется IP в Kubernetes: три уровня

Сетевой путь запроса в кластере проходит три зоны фильтрации, и каждая режет трафик своими средствами: NetworkPolicy внутри пода, iptables или IPVS на ноде, правила Ingress и облачного LoadBalancer снаружи. Диагностику начинайте с определения границы, до которой пакет ещё доходит.

Практическая примета для быстрого разделения: если curl из одного пода в другой висит, а тот же адрес и порт отвечает с ноды, проблема на уровне пода. Если с ноды тоже нет ответа, смотрите kube-proxy, локальный firewall и маршруты. Если изнутри кластера всё открывается, а снаружи нет, ищите причину на балансировщике и в Ingress.

УровеньЧто блокируетГде смотретьТипичный симптом
ПодNetworkPolicy, которую применяет CNI (Calico, Cilium)kubectl get networkpolicy -A, kubectl describe networkpolicy, логи пода CNIЗапрос из пода в под зависает, tcpdump показывает SYN без SYN-ACK
Нодаkube-proxy (iptables или IPVS), firewalld, ufw, security group облакаiptables-save, conntrack, kubectl get svc -o wide, kubectl get endpointsNodePort не отвечает, соединение сбрасывается, Service без endpoints
Внешний балансировщикIngress-контроллер, облачный LoadBalancer, edge-firewallkubectl get ingress -A, kubectl describe ingress, аннотации, логи контроллераHTTP 403 от Ingress, сервис работает из кластера и недоступен снаружи

Поды с hostNetwork: true используют сетевой стек ноды и не имеют собственного pod network namespace. По документации Kubernetes поведение NetworkPolicy для таких подов не определено, и на практике большинство CNI не применяют к ним политики, привязанные к интерфейсам подов. Поэтому правило может существовать, а трафик всё равно проходить. Такое поведение стоит отдельно проверить для вашего CNI.

Держите в голове два диапазона адресов: podCIDR, из которого выдаются IP подам, и nodeCIDR с адресами нод. В правилах ipBlock указывают именно их, а не DNS-имена сервисов.

Уровень пода: NetworkPolicy и CNI

NetworkPolicy это объект API networking.k8s.io/v1. Он выбирает поды по меткам через podSelector и описывает разрешённые направления в блоках ingress и egress. Правила нескольких политик, выбирающих один и тот же набор подов, складываются аддитивно: ingress-правила каждой политики комбинируются. Запрет получается только тогда, когда ни одна политика трафик не разрешает.

Если в namespace нет ни одной политики, весь ingress- и egress-трафик к подам и от них разрешён по умолчанию. Default-deny ingress создаётся политикой, которая выбирает все поды namespace, но не содержит разрешающих ingress-правил.

Сам API-сервер трафик не фильтрует. Блокировку выполняет CNI: Calico применяет правила через компонент Felix, который пишет цепочки iptables или, в eBPF-режиме, карты ядра. Cilium применяет политики eBPF-программами, привязанными к сетевым интерфейсам подов. Если CNI не поддерживает NetworkPolicy, объекты создаются и проходят валидацию, но не влияют ни на что.

Проверить, какой плагин установлен и поддерживает ли он политики:

kubectl get pods -n kube-system | grep -E 'calico|cilium|weave|flannel'

Пример политики, которая закрывает весь входящий трафик в namespace production:

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

Пустой podSelector соответствует всем подам в namespace политики, а пустой список ingress не разрешает ничего. Список политик и подробности по конкретной:

kubectl get networkpolicy -A
kubectl describe networkpolicy default-deny-ingress -n production

Синтаксис NetworkPolicy и семантику полей сверяйте с официальной документацией Kubernetes: NetworkPolicies и NetworkPolicy v1 API.

Уровень ноды: kube-proxy, iptables и NodePort

kube-proxy превращает объекты Service в правила DNAT: в режиме iptables это цепочки KUBE-SERVICES и KUBE-SVC-*, в режиме IPVS это виртуальные сервисы ядра. Разбор различий и команд диагностики есть в руководстве по маршрутизации трафика между подами и сервисами.

Если Service не отвечает, сначала проверьте, есть ли у него endpoints. Пустой список означает, что селектор не совпал с метками подов или поды не прошли readiness-пробу, и NetworkPolicy здесь не при чём:

kubectl get svc -o wide -n production
kubectl describe svc api -n production
kubectl get endpoints api -n production

Локальные фильтры ноды смотрят отдельно. Классический сценарий: NodePort открыт в кластере, но порт из диапазона 30000-32767 закрыт в security group облака или в firewalld на ноде.

iptables-save | grep -n 203.0.113.10

Уровень внешнего балансировщика: Ingress и LoadBalancer

Ingress-контроллер умеет резать трафик собственными правилами. У ingress-nginx ограничение доступа по IP задаётся аннотацией nginx.ingress.kubernetes.io/whitelist-source-range, значением которой служит список CIDR. Облачный LoadBalancer добавляет ещё один слой: security group, network ACL и firewall провайдера.

Посмотреть все Ingress и их аннотации:

kubectl get ingress -A
kubectl describe ingress api -n production
kubectl logs -n ingress-nginx deploy/ingress-nginx-controller --tail=100

Пример из практики: клиент получает HTTP 403 от Ingress-контроллера, при этом NetworkPolicy разрешает трафик с его адреса. Причина в whitelist-source-range, который ограничивает доступ корпоративной подсетью. Описание аннотации приведено в документации Ingress-Nginx Controller.

NetworkPolicy в Kubernetes: как посмотреть и настроить блокировку IP

По умолчанию в кластере нет default-deny: пока политик нет, все поды общаются между собой свободно. Запрет появляется только после создания политики, которая выбирает поды и не разрешает нужное направление.

Команды kubectl для вывода NetworkPolicy

Базовый набор команд, который закрывает большинство задач просмотра:

  • kubectl get networkpolicy -A: список всех политик во всех namespace.
  • kubectl describe networkpolicy <имя> -n <ns>: podSelector, policyTypes, ingress, egress, ipBlock и порты.
  • kubectl get networkpolicy -A -o yaml: полный YAML для аудита и переноса в Git.
  • kubectl get pods -n <ns> --show-labels: метки подов, чтобы понять, кого выбирает политика.
  • kubectl get pods -o wide -A: IP подов и ноды, на которых они работают.

Блок ipBlock в выводе describe показывает, какие подсети разрешены, а какие исключены через except. Если подсеть клиента не попала ни в один разрешающий блок, соединение будет отброшено.

Пример YAML: блокировка IP на ingress и egress

Политика ниже разрешает входящий трафик к подам app=api только из корпоративной сети 10.0.0.0/8, кроме подсети 10.10.0.0/16, и только на порт 8080. Всё остальное блокируется.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-corp-only
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
  - Ingress
  ingress:
  - from:
    - ipBlock:
        cidr: 10.0.0.0/8
        except:
        - 10.10.0.0/16
    ports:
    - protocol: TCP
      port: 8080

Для исходящего трафика логика та же, но правило описывается в egress. Пример политики, которая запрещает подам app=api ходить в подсеть 203.0.113.0/24:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-egress-to-range
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
        except:
        - 203.0.113.0/24

Применение и проверка:

kubectl apply -f policy.yaml
kubectl describe networkpolicy deny-egress-to-range -n production

Комбинируйте селекторы, когда нужно соединить внутренние и внешние источники:

  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: monitoring
    - podSelector:
        matchLabels:
          role: jump-host
    - ipBlock:
        cidr: 198.51.100.0/24

Условия внутри одного элемента from складываются по ИЛИ, а namespaceSelector и podSelector в одном элементе списка работают по И. ipBlock описывает CIDR (например, 192.168.1.0/24 или 2001:db8::/64), а поле except задаёт CIDR, исключаемые из правила; DNS-имена ipBlock не понимает. Поле endPort задаёт диапазон портов от port до endPort включительно, разрешённый политикой; его поддержка зависит от CNI.

Если поле policyTypes не указано, оно по умолчанию определяется наличием ingress- и egress-правил: политики с секцией egress считаются влияющими на egress, а все политики — на ingress.

Default-deny и типичные ошибки

Самая жёсткая и самая полезная конфигурация: политика с пустым podSelector и policyTypes Ingress и Egress без единого правила. Она блокирует все входящие и исходящие соединения выбранных подов.

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

Дальше выдавайте разрешения точечно. Схема с default-deny и постепенным расширением прав разобрана в материале о безопасности Kubernetes: RBAC, NetworkPolicies и управлении секретами.

Ошибки, которые чаще всего ломают работающий сервис после включения политик:

  • Не разрешён DNS. После default-deny поды не резолвят имена, потому что исходящие запросы на 53-й порт в kube-system заблокированы. Добавьте отдельное egress-правило с namespaceSelector по метке kubernetes.io/metadata.name: kube-system и портами 53 UDP и TCP.
  • Не разрешён трафик от Ingress-контроллера. Поды приложения получают запросы из namespace с контроллером, и без правила from по namespaceSelector внешние запросы отбиваются на уровне CNI.
  • Расчёт на hostNetwork. Как сказано выше, такие поды живут в сети ноды, и большинство CNI не применяют к ним под сетевые политики.
  • Забытая нумерация портов. Приложение слушает 8080, а политика разрешает 80, и соединение отбивается без внятного сообщения в логах приложения.

Перед применением в проде проверяйте манифест на стороне сервера и держите план отката:

kubectl apply -f policy.yaml --dry-run=server
kubectl delete networkpolicy default-deny-all -n production

Сначала прогоняйте политики в staging на копии манифестов приложения, потом переносите в прод. Полезно также проверять селекторы в визуальном редакторе NetworkPolicy, если такой есть в вашей консоли управления кластером, но применять изменения только через Git.

CNI и блокировка IP: Calico и Cilium

Разница между плагинами проявляется в том, как именно применяется правило и что вы можете увидеть при отладке.

CNIМеханизм примененияCLI и APIОсобенности
CalicoFelix, iptables или eBPFcalicoctl, kubectl, FelixConfigurationGlobalNetworkPolicy с order и правилами action, BGP-маршруты
CiliumeBPF-программы в ядреcilium, hubbleCiliumNetworkPolicy с ограничениями по CIDR, наблюдение за дропами через Hubble

Микросегментация с примерами политик для обоих плагинов и стратегией внедрения без остановки продакшена разобрана в гайде по микросегментации Kubernetes и модели Zero Trust с Cilium и Calico.

Calico: GlobalNetworkPolicy и Felix

Calico понимает стандартные NetworkPolicy, а сверху даёт GlobalNetworkPolicy — не namespaced ресурс, представляющий упорядоченный набор правил, которые применяются к коллекции endpoints, соответствующих selector. Порядок вычисления задаётся полем order: Calico применяет политику с наименьшим значением первой. Правила внутри политики выполняются по порядку, и каждое правило применяет некоторое действие (action) к совпавшим пакетам.

apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: deny-egress-to-range
spec:
  order: 100
  selector: app == "api"
  types:
  - Egress
  egress:
  - action: Deny
    destination:
      nets:
      - 203.0.113.0/24

Просмотр конфигурации и активных правил:

calicoctl get networkpolicy -A
calicoctl get globalnetworkpolicy
calicoctl get policy -o yaml
kubectl get felixconfiguration default -o yaml

Поле в FelixConfiguration подскажет, работает ли кластер в eBPF-режиме или на iptables. От этого зависит, где искать правила: в цепочках cali-* или в картах ядра. Описание ресурса приведено в документации Calico Global network policy.

Cilium: eBPF и Hubble

Cilium применяет политики на уровне ядра. Политика может ограничивать трафик по CIDR: добавление префикса в FromCIDR или в FromCIDRSet без ExcludeCIDRs эквивалентно, а FromCIDRSet поддерживает ExcludeCIDRs. Расширенная политика с ограничением по CIDR выглядит так:

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: allow-api-from-corp
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: api
  ingress:
  - fromCIDRSet:
    - cidr: 10.0.0.0/8
      except:
      - 10.10.0.0/16
    toPorts:
    - ports:
      - port: '8080'
        protocol: TCP

Диагностика в Cilium строится на наблюдаемости. После применения политики, блокирующей внешний трафик, запросы извне кластера отклоняются, а в логах cilium-agent виден вердикт DROPPED для http-request:

cilium policy get
cilium endpoint list
cilium monitor
kubectl logs -n kube-system -l k8s-app=cilium --tail=100

Для работы hubble observe нужен включённый Hubble в конфигурации Cilium. Если команда недоступна, проверьте параметры установки плагина. Пример политики и вердикт DROPPED описаны в документации Cilium по Ingress и Network Policy.

Диагностика: почему трафик блокируется - пошаговый чек-лист

Порядок шагов важен: идём от отправителя к получателю и на каждом шаге отвечаем на один вопрос, доходит ли пакет.

  1. Убедитесь, что CNI поддерживает NetworkPolicy и поды плагина в статусе Running.
  2. Выведите все политики в namespace приложения и посмотрите, какие из них выбирают его поды.
  3. Сверьте метки подов с podSelector, а порты приложения с портами в правилах.
  4. Проверьте DNS: резолвится ли имя сервиса изнутри пода.
  5. Проверьте kube-proxy и endpoints сервиса, затем локальные фильтры ноды.
  6. Проверьте Ingress, аннотации whitelist и security groups облака.
  7. Подтвердите вывод tcpdump: видно ли входящие пакеты на интерфейсе получателя.

Проверка на уровне пода: curl, nc, tcpdump

Начните с доступности из пода-источника и заодно зафиксируйте, на какой стадии рвётся соединение:

kubectl exec -it client-0 -n production -- curl -v http://10.244.3.15:8080
kubectl exec -it client-0 -n production -- nc -zv 10.244.3.15 8080

Дальше смотрите пакеты на стороне получателя:

kubectl exec -it api-7f9c -n production -- tcpdump -i any host 10.244.1.7 -n

Как читать результат. SYN без SYN-ACK означает, что пакет ушёл и был отброшен на входящем направлении: смотрите ingress-правила получателя. Отсутствие пакетов в tcpdump при живом curl говорит о блокировке на стороне отправителя, то есть в egress. Мгновенный RST вместо таймаута обычно указывает на reject на ноде или на отсутствие слушателя на порту.

Проверка на уровне ноды: iptables и conntrack

Если пакет не покидает под или теряется между нодами, смотрите правила и состояние соединений на ноде:

iptables-save | grep 10.244.3.15
conntrack -L | grep 10.244.3.15
kubectl get svc -o wide -n production
kubectl describe svc api -n production

Отдельно проверьте таблицы маршрутов пода и ноды: как CNI строит маршруты и какие ошибки их ломают, подробно разобрано в статье про таблицы маршрутов между подами и нодами. Если endpoints у сервиса пустые, проблема в селекторах или readiness-пробе, а не в сетевых политиках.

Логи самого CNI часто дают ответ быстрее, чем ручной разбор цепочек:

kubectl logs -n kube-system -l k8s-app=calico-node --tail=100
kubectl logs -n kube-system -l k8s-app=cilium --tail=100 --since=10m
kubectl get events -n production --sort-by=.lastTimestamp | tail -20

Проверка на уровне внешнего балансировщика

Последний шаг: убедиться, что запрос не отбит до попадания в кластер.

kubectl get ingress -A
kubectl describe ingress api -n production
kubectl logs -n ingress-nginx deploy/ingress-nginx-controller --tail=200

Ищите в аннотациях whitelist-source-range, а в консоли облака правила security group для портов LoadBalancer и NodePort. Признак этого уровня: одинаковый запрос изнутри кластера проходит, а с внешнего адреса возвращает 403 или таймаут.

Чек-лист и шпаргалка: блокировка IP в Kubernetes

Сведите проверки в одну таблицу и держите её под рукой во время разбора инцидента.

УровеньКомандаЧто проверить
Политикиkubectl get networkpolicy -AКакие объекты выбирают поды приложения, есть ли default-deny
Селекторыkubectl describe networkpolicy <имя> -n <ns>podSelector, policyTypes, from/to, ipBlock и except, порты
Меткиkubectl get pods -n <ns> --show-labelsСовпадают ли метки подов с политикой
Сервисkubectl get endpoints -n <ns>Есть ли адреса в endpoints, не пустой ли селектор
Нодаiptables-save, conntrack -L | grep <ip>Правила DROP и REJECT, состояние соединений
CNIcalicoctl get globalnetworkpolicy, логи cilium-agentАктивные правила и причина дропа
Внешний контурkubectl describe ingress, security groupsWhitelist, аннотации, правила облачного firewall

Обязательный минимум по NetworkPolicy: apiVersion networking.k8s.io/v1, kind NetworkPolicy, metadata с namespace, spec с podSelector и policyTypes, а также правила в ingress или egress. Поле endPort и расширенные селекторы проверяйте на поддержку в конкретной версии плагина. Примеры в статье ориентированы на Kubernetes 1.29+, NetworkPolicy API v1, Calico 3.27+ и Cilium 1.15+, поведение более старых версий CNI может отличаться.

Синтаксис политик и набор поддерживаемых полей сверяйте с официальной документацией Kubernetes, Calico и Cilium для своей версии. Для плановой проверки сетевых правил в кластере используйте аудит безопасности Kubernetes за пять шагов.

Начните с команды kubectl get networkpolicy -A в проблемном namespace: чаще всего причина находится уже на этом шаге, а default-deny без правила для DNS объясняет внезапную потерю связности после планового деплоя.

Поделиться:
Сохранить гайд? В закладки браузера