Learning Platform
Глоссарий Troubleshooting
Урок 11.04 · 22 мин
Продвинутый
CNIiptableseBPFCalicoCiliumL7 policy

Как CNI реализует NetworkPolicy

Знать NetworkPolicy API недостаточно — на собеседованиях, в production и иногда даже на CKAD задают вопросы вида «policy применилась, но Pod-Pod трафик не блокируется, что не так». Чтобы понимать такие баги, нужно знать как CNI plugins реально применяют policy на уровне Linux. Они все реализуют один и тот же K8s API, но через разные dataplanes.


eBPF: безопасная инспекция kernel в runtime без модулей

Архитектура enforcement: общая схема

Любой CNI с поддержкой NetworkPolicy состоит из двух уровней:

CNI policy enforcement: control plane → dataplane
API serverХранит NetworkPolicy в etcd. Через watch отдаёт изменения подписчикам.
CNI control planeDaemon на каждом node (или central component). Подписан на NetworkPolicy, Pods, Namespaces через informer. Считает effective rules для Pods на своём node.
DataplaneРеальный механизм фильтрации трафика. Зависит от CNI: iptables, eBPF, OVS flows. Применяется на network namespace Pods через veth pair.
PacketsРеальный трафик между Pods и наружу. Проходит через dataplane, попадает в DROP или ACCEPT в зависимости от правил.

Control plane — Go-агент, который через informer от API server получает все NetworkPolicies и labels Pods, считает, какие Pods должны быть isolated и какие источники разрешены. Dataplane — программа на уровне ядра Linux (или близко к нему), которая реально режет пакеты.


Calico: iptables и filter table

Calico — самый распространённый CNI с поддержкой NetworkPolicy. Дефолтный mode — iptables. Каждый node имеет calico-node DaemonSet, который:

  1. Через informer подписан на NetworkPolicy.
  2. Для каждого Pod на своём node генерирует набор iptables правил.
  3. Размещает их в filter table, в специальных chains (cali-*), которые подключены к стандартным chains FORWARD, INPUT, OUTPUT.

Когда пакет идёт от Pod A к Pod B через bridge/veth, он проходит через FORWARD chain, оттуда переходит в cali-fw-* chains для каждого Pod и проверяется на match. Если ни одного match — DROP.

# На node с Calico:
sudo iptables -L -n -t filter | grep cali | head -20
# Видны chains вида cali-fw-<endpoint-id>

Calico также поддерживает eBPF dataplane (опционально включается). В eBPF mode он размещает программы на TC ingress hook каждого veth и фильтрует там — быстрее, чем iptables, особенно при большом количестве правил.

NOTE

iptables работает линейно: чем больше правил, тем дольше каждый пакет идёт через chain. В большом кластере с сотнями NetworkPolicies это становится заметным накладным расходом. eBPF и OVS обходят это через программирование, а не линейные lookup-ы.


Cilium: eBPF и L7 policy

Cilium с самого начала строился на eBPF — Berkeley Packet Filter, виртуальной машине в ядре Linux, через которую можно прикрепить программы к различным hook-ам: TC, XDP, socket, kprobe.

В Cilium на каждый Pod (точнее, его veth) прикреплены eBPF программы, которые:

  • При входящем пакете смотрят source IP, lookup-ят его в eBPF map «identity → labels», проверяют по правилам policy, ACCEPT или DROP.
  • Identity Pod-а вычисляется из labels (а не из IP) — это важно, потому что Pod IP эфемерные, но labels стабильные. Cilium хранит mapping «identity → Pods» отдельно.

Помимо стандартного NetworkPolicy, Cilium поддерживает свой CiliumNetworkPolicy CRD, который расширяется на L7:

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: l7-http-policy
spec:
  endpointSelector:
    matchLabels:
      app: api
  ingress:
  - fromEndpoints:
    - matchLabels:
        app: web
    toPorts:
    - ports:
      - port: "8080"
        protocol: TCP
      rules:
        http:
        - method: GET
          path: "/api/v1/users"

«Разрешить web → api на 8080 ТОЛЬКО для GET /api/v1/users, остальные HTTP-методы и paths — DROP». Это L7 policy — стандартный K8s NetworkPolicy так не умеет.

L7 реализуется через Envoy proxy, который Cilium встраивает в network path трафика, плюс eBPF redirect-ы.


Antrea: Open vSwitch flows

Antrea — CNI, разработанный VMware на основе Open vSwitch (OVS). На каждом node работает antrea-agent, который:

  • Создаёт OVS bridge.
  • При создании Pod добавляет его veth как OVS port.
  • Для NetworkPolicy генерирует OVS flows в специальных таблицах bridge.

OVS — это более «программируемый» switch, чем Linux bridge. Flows организованы в pipeline (несколько таблиц последовательно), и можно довольно эффективно фильтровать трафик.

Antrea также поддерживает AntreaNetworkPolicy CRD с L7 и приоритетами правил (deny over allow).


Weave Net: iptables, но с подводными камнями

Weave Net использует iptables аналогично Calico, но исторически:

  • Поддержка namespaceSelector была неполной до версии 2.4.
  • Не все edge cases сочетания selectors поддерживались.
  • Производительность iptables как у Calico (линейная).

На CKAD vanilla Weave обычно не встречается, но в legacy кластерах — да.


Killer момент: bypass policy между Pods на одном node

Subtle bug, который встречается в нестандартных setups: трафик между Pods на одном node может идти через Linux bridge без прохождения через FORWARD chain, если bridge настроен без hairpin mode и без br_netfilter модуля.

Что происходит:

  • Pod A и Pod B на одном node.
  • A → B: пакет идёт через veth-A → bridge → veth-B.
  • Если bridge не отправляет пакет через iptables FORWARD chain — Calico iptables правила его не видят. Policy не применяется.

Это решается включением br_netfilter:

sudo modprobe br_netfilter
sudo sysctl -w net.bridge.bridge-nf-call-iptables=1

Стандартные kubelet setups (kubeadm, managed K8s) уже включают это. Но если кто-то сделал ручную инсталляцию и забыл — policy «по необъяснимой причине» не работает между Pods на одной машине. Calico проверяет это при старте и warning-ит в логах.

WARNING

Cilium с eBPF dataplane обходит эту проблему — он не зависит от br_netfilter, программы прикреплены прямо к TC hook veth каждого Pod. Это одна из причин, почему Cilium считается «надёжнее» в edge cases.


Limitations: L3/L4 only

Стандартный Kubernetes NetworkPolicy работает только на L3/L4:

Что NetworkPolicy умеет и не умеет
УмеетСтандартный NetworkPolicy API контролирует трафик по IP-адресам источника/назначения, TCP/UDP/SCTP портам, и через labels (которые транслируются в IP).
Не умеетСтандартный NetworkPolicy ничего не знает про HTTP method, path, Host header, gRPC method, Kafka topic, mTLS identity. Эти вещи находятся выше L4, в payload пакета.
Решения для L7Cilium CRD (eBPF + Envoy), Istio AuthorizationPolicy (mTLS + sidecar), Linkerd policies, Kuma, OPA Gatekeeper для admission control. Все вне core K8s API.

Это значит, что задача «разрешить GET /healthz но запретить /admin» не решается стандартным NetworkPolicy. Нужны:

  • CiliumNetworkPolicy с http rules (если установлен Cilium);
  • Istio AuthorizationPolicy с request.* атрибутами (если установлен service mesh);
  • Open Policy Agent (OPA) как Admission Controller (но это про создание API объектов, а не runtime трафик).

На CKAD вопросы про L7 редкие, но осознавать ограничение — обязательно.


Egress через NAT в Internet

Когда Pod выходит наружу (в Internet), пакет проходит через node IP — обычно с MASQUERADE (SNAT) на исходящем интерфейсе. То есть source IP пакета меняется с PodIP на node IP. Это делает kube-proxy или CNI plugin.

Важные следствия для NetworkPolicy:

  1. Внутренний egress ipBlock matches на PodIP target Pod, не на ClusterIP (мы это упоминали в прошлом уроке). Это потому что DNAT происходит до policy enforcement.
  2. Egress в Internet — нужен ipBlock с 0.0.0.0/0 (или конкретные CIDR). namespaceSelector/podSelector бесполезны, потому что внешние сервисы — не Pods.
  3. Внешние сервисы видят node IP, не Pod IP. Поэтому если ваш ingress firewall в облаке хочет allowlist Pod IP — это не сработает, нужно whitelist node IPs или фиксированный SNAT (Egress Gateway в Cilium, EgressNetworkPolicy в OpenShift).

Testing NetworkPolicy: инструменты

Самый простой способ протестировать policy — поднять test Pod и сделать nc или wget:

# Запускаем test Pod
kubectl run test --rm -it --image=busybox --restart=Never -- sh

# Внутри Pod
nc -zv 10.0.5.10 5432    # TCP check на IP
nc -zv postgres 5432     # через DNS
wget -O- --timeout=3 http://api:8080/healthz

Если policy блокирует — nc -zv зависнет до timeout. Если разрешает — nc: open ... succeeded.

Полезные инструменты:

  • calicoctlcalicoctl get networkpolicy, debugging Calico-specific.
  • cilium connectivity test — комплексный тест matrix Pod↔Pod в кластере с Cilium.
  • np-viewer / kubectl-np-viewer plugin — визуализатор effective policy для каждого Pod.
  • netshoot image (nicolaka/netshoot) — Pod с pre-installed tools (nc, dig, tcpdump, wireshark).
kubectl run netshoot --rm -it --image=nicolaka/netshoot --restart=Never -- bash
# Внутри: dig postgres, tcpdump -i eth0, mtr 10.0.5.10

Killer момент: kubectl exec НЕ через policy

kubectl exec -it pod -- nc target 80 — это не «pod connects to target». Это:

  1. ваш CLI отправляет HTTP request на API server;
  2. API server открывает поток к kubelet на узле, где Pod;
  3. kubelet через CRI открывает exec-stream в контейнере;
  4. в этом stream выполняется nc от лица процессов Pod.

Когда nc делает connect(), уже изнутри Pod-а, сетевой трафик идёт от Pod к target. Этот трафик проходит через NetworkPolicy enforcement, как и любой другой Pod-Pod трафик. Так что тестирование через kubectl exec валидно.

Но: сам по себе exec-канал (ваш SSH-подобный stream) идёт через API server, не через Pod-сеть. Если бы NetworkPolicy «запретила доступ к Pod», это не помешало бы kubectl exec, потому что API server подключается к kubelet, а kubelet — к контейнеру через CRI socket, а не сеть. Это часто путает: «policy блокирует ingress, но я могу зайти exec-ом» — да, потому что это разные слои.

TIP

Это объясняет, почему kubectl exec всегда работает даже на «полностью изолированный» Pod. Policy не контролирует control-plane operations. Если хотите ограничить exec — это RBAC и authorization, не NetworkPolicy.


Проверка знанийKnowledge check
В чём принципиальная разница между Calico iptables и Cilium eBPF dataplane?
ОтветAnswer
Calico iptables генерирует линейные правила в filter table, которые проверяются последовательно — производительность падает с ростом числа policies. Cilium eBPF прикрепляет программы непосредственно к TC hook veth каждого Pod, использует hash maps для identity lookup — O(1) проверка. eBPF также поддерживает L7 (через Envoy), iptables — нет.
Проверка знанийKnowledge check
Pods на одном node продолжают общаться, несмотря на NetworkPolicy. CNI — Calico с iptables. Возможная причина?
ОтветAnswer
Не загружен модуль br_netfilter или не выставлен sysctl net.bridge.bridge-nf-call-iptables=1. Без них трафик между Pods на одном bridge не идёт через FORWARD chain — iptables не видит пакет, policy не применяется. Лечение: modprobe br_netfilter + sysctl. Стандартный kubeadm это делает, ручные setups могут забыть.
Проверка знанийKnowledge check
Хотите разрешить GET /api/v1/users, но запретить POST /api/v1/admin. Достаточно ли стандартного NetworkPolicy?
ОтветAnswer
Нет. Стандартный NetworkPolicy работает на L3/L4 (IP + port), HTTP method/path он не знает. Нужны: CiliumNetworkPolicy с http rules (если стоит Cilium), Istio AuthorizationPolicy или другой service mesh. Это L7 policy, вне core K8s API.
Проверка знанийKnowledge check
Pod в кластере делает curl https://api.example.com. Какой ipBlock в egress правильно настроить?
ОтветAnswer
cidr 0.0.0.0/0 с except для внутренних диапазонов (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) и хорошо бы 169.254.169.254/32 для метаданных облака. Плюс отдельный egress на kube-dns:53 для name resolution. namespaceSelector/podSelector бесполезны для external endpoints — они не Pods.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 5. Чем eBPF dataplane Cilium принципиально отличается от iptables dataplane Calico?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5