Gateway API и Cilium Network Policies. Как ресурсы влияют друг на друга

Одновременная работа Gateway API и Cilium Network Policies в кластере, почему публикация через Gateway может внезапно сломаться после настройки политик, и что нужно знать, чтобы этого избежать.

В кластере могут быть одновременно настроены:

Gateway проводит трафик через свой прокси, а политики решают, можно ли этому трафику пройти дальше. В платформе «Штурвал» используется Cilium с его встроенным контроллером Gateway API на основе Envoy. Поэтому описанная ниже модель с идентичностью reserved:ingress работает именно для этого сценария. Если вы используете сторонний Gateway-контроллер (например, Traefik, Istio, Envoy Gateway), механизмы будут другими — см. Настройка политик, если контроллер не Сilium.

Если настроить Gateway, но не создавать сетевых политик — трафик будет проходить. Проблемы могут возникнуть, когда на бэкенд уже действует запрещающая политика, а разрешение для Gateway не добавлено.

Подробнее о Gateway API на страницах:


1. Как это устроено

1.1. Что происходит с трафиком, когда он проходит через Gateway

Обычный Ingress-контроллер (например, Nginx) работает так: есть отдельные поды контроллера, которые принимают запросы и перенаправляют их в бэкенд. Политики безопасности в этом случае пишутся в стиле: “разрешить подам контроллера общаться с подами приложения”.

В Cilium Gateway API встроен в сетевой стек:

  1. Запрос от клиента приходит на Service Gateway.
  2. Cilium перехватывает этот трафик через eBPF и направляет его во встроенный прокси Envoy, который работает на каждом узле кластера.
  3. Envoy применяет правила маршрутизации из HTTPRoute/GRPCRoute.
  4. Трафик, который прошёл через Gateway, Cilium присваивает специальную идентичность — reserved:ingress (или просто ingress).

Если вы не используете Gateway для публикации приложения — идентичность ingress не нужна. Она существует, чтобы связать Gateway API с сетевыми политиками.

Что это значит для политик:

  • Для бэкенда (вашего приложения) источником трафика становится Gateway, а не IP-адрес клиента.
  • Поэтому правило “разрешить доступ с IP-адреса клиента” на бэкенде не сработает — политика увидит источник ingress, а не клиентский IP.
  • Чтобы разрешить доступ от Gateway к бэкенду, нужно написать политику, которая разрешает идентичность ingress.

1.2. Два уровня контроля

При использовании Gateway API возможно контролировать трафик политиками Cilium в двух точках:

УровеньЧто контролируемКакую политику использовать
1. Доступ к GatewayКто может обращаться к самому Gateway (например, только из определённых сетей)CiliumClusterwideNetworkPolicy на reserved:ingress
2. Доступ от Gateway к бэкендуК каким бэкендам Gateway может направлять трафикCiliumNetworkPolicy с fromEntities: [ingress]

Если политика выбирает поды бэкенда, для входящего трафика включается режим default-deny. В этом случае трафик от Gateway будет разрешён только при наличии явного правила fromEntities: [ingress] в секции ingress политики. Без этого правила запросы от Gateway будут отклоняться.


2. Влияние ресурсов

СитуацияЧто происходитПочему
Есть Gateway и HTTPRoute, но нет сетевых политикПубликация работаетПо умолчанию, если не создано никаких политик, весь трафик разрешён
Добавили политику default-deny на бэкенд, но забыли разрешить ingressПубликация через Gateway ломаетсяПолитика блокирует весь трафик, включая от Gateway
Добавили кластерную политику “запретить внешний трафик” (world) без исключения для GatewayВнешний доступ к Gateway даёт ошибкуCilium блокирует трафик из интернета
Включили изоляцию неймспейса, но забыли разрешить DNSПриложение не может резолвить имена, даже если Gateway работаетDNS-запросы блокируются политикой
Пытаетесь ограничить доступ к бэкенду по IP-адресу клиента через fromCIDRНе работаетИсточником для политики становится ingress, а не клиент
Запрос изнутри кластера (pod → Service Gateway) идёт через GatewayТрафик тоже получает identity ingressТрафик через Gateway также маскируется
Сменили GatewayClass с cilium на сторонний контроллерПолитики перестают работатьМодель reserved:ingress действует только для Cilium Gateway

Модель, основанная на идентичности reserved:ingress, является внутренней функцией Cilium и не работает для других реализаций Gateway API.


3. Identity reserved:ingress

Это ключевая концепция:

ВопросОтвет
Кто является источником для бэкенда за Cilium Gateway?Identity ingress, а не исходный IP-адрес клиента
Можно ли использовать стандартный Kubernetes NetworkPolicy?Ограниченно. Он может сработать через селекторы по меткам, но это ненадёжно. Лучше использовать CiliumNetworkPolicy
Одна identity на все Gateway?Да. reserved:ingress — общая для всех Gateway прокси в кластере
Где увидеть реальный IP клиента?В заголовках X-Forwarded-For или X-Envoy-External-Address, но не как L3-источник в политике

Нельзя через одну entity ingress сказать "разрешить только Gateway A, но не Gateway B". fromEntities: [ingress] разрешает трафик от всех Gateway прокси в кластере, а не от конкретного Gateway.

Особенности настройки при использовании стороннего GatewayClass

Если в качестве GatewayClass выбран не cilium, а сторонний контроллер (например, Traefik, Istio, Envoy Gateway), в этом случае:

  1. Идентичность reserved:ingress не применяется — трафик идёт от обычных подов контроллера со своими метками.
  2. Политики Cilium с fromEntities: [ingress] перестают работать — их нужно переписать под селекторы нового контроллера.

Как переписать политики

Было (для Cilium Gateway):

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: allow-from-gateway
  namespace: my-app
spec:
  endpointSelector:
    matchLabels:
      app: my-backend
  ingress:
    - fromEntities:
        - ingress   # ← не работает для стороннего контроллера

Стало (для стороннего контроллера):

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-gateway
  namespace: my-app
spec:
  podSelector:
    matchLabels:
      app: my-backend
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: gateway-system   # неймспейс контроллера
          podSelector:
            matchLabels:
              app: my-gateway-controller   # метки подов контроллера

Альтернативный вариант — через CiliumNetworkPolicy с селекторами

Если вы хотите остаться в рамках CiliumNetworkPolicy, используйте селекторы по меткам вместо fromEntities:

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: allow-from-gateway
  namespace: my-app
spec:
  endpointSelector:
    matchLabels:
      app: my-backend
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: my-gateway-controller
            io.kubernetes.pod.namespace: gateway-system

Что остаётся без изменений

КомпонентНужно ли переделывать
DNS-правила (порт 53)Нет
FQDN-правила (toFQDNs)Нет
Политики между микросервисамиНет
Egress к внешним APIНет

При смене GatewayClass с Cilium на сторонний контроллер все политики, использующие fromEntities: [ingress], перестают действовать. Их необходимо переписать на селекторы по меткам подов контроллера или по неймспейсу.


4. Рекомендации

4.1. Для бэкенда: всегда разрешать ingress

Основной способ: разрешить доступ от Gateway к приложению при настроенных политиках Cilium.

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: allow-from-gateway
  namespace: my-app
spec:
  description: "Разрешить трафик от Cilium Gateway"
  endpointSelector:
    matchLabels:
      app: my-backend
  ingress:
    - fromEntities:
        - ingress
      toPorts:
        - ports:
            - port: "8080"
              protocol: TCP

4.2. Для Gateway: контролировать, кто может до него достучаться

Пример: разрешить доступ к Gateway только из определённой подсети

apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
  name: allow-cidr-to-ingress
spec:
  description: "Разрешить доступ к Gateway только из подсети X"
  endpointSelector:
    matchExpressions:
      - key: reserved:ingress
        operator: Exists
  ingress:
    - fromCIDRSet:
        - cidr: 203.0.113.10/32

4.3. Для Gateway: разрешить ходить в кластер

Если в кластере включена изоляция, Gateway должен иметь возможность направлять трафик к бэкендам:

apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
  name: allow-ingress-to-cluster
spec:
  description: "Разрешить Gateway ходить в кластер"
  endpointSelector:
    matchExpressions:
      - key: reserved:ingress
        operator: Exists
  egress:
    - toEntities:
        - cluster

4.4. Вводить изоляцию по шагам

Не включайте жёсткую изоляцию сразу:

  1. Сначала добейтесь рабочего пути Gateway → бэкенд.
  2. Добавьте политику allow from ingress.
  3. Включите default-deny или примените селекторы.
  4. Проверяйте Hubble до и после каждого шага.

4.5. Отлаживать через Hubble

Hubble показывает реальные потоки трафика и вердикты политик:

# Посмотреть все заблокированные потоки в неймспейсе
hubble observe --verdict DROPPED --namespace my-app --last 50

# Посмотреть трафик от Gateway (identity ingress)
hubble observe --identity ingress --last 50

# Проверить статусы ресурсов
kubectl get gateway -A
kubectl get httproute -A
kubectl get cnp -n my-app

Ищите, на каком этапе обрыв: world ↔ ingress или ingress ↔ app.


4.6. Чего не стоит делать

Не делайтеПочему
Контролировать трафик Cilium Gateway → бэкенд через стандартный NetworkPolicyНет identity reserved:ingress, работает ненадёжно
Применять endpointSelector: {} и fromEntities: [world] на весь кластер без необходимостиОткрываете внешний доступ слишком широко
Включать deny-all до того, как подготовлены все allow-правила (Gateway, DNS, зависимости)Получите простой outage
Пытаться фильтровать клиентов по IP на бэкенде через fromCIDRИсточник в политике — ingress, а не клиент. Фильтруйте клиентов на уровне Gateway или по HTTP-заголовкам (X-Forwarded-For, X-Envoy-External-Address) в приложении
Пытаться разграничить Gateway A и Gateway B только через fromEntities: [ingress]Одна общая identity на все Gateway

5. Примеры: манифесты для типовых сценариев

5.1. Gateway

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: my-gateway
  namespace: gateway-system
spec:
  gatewayClassName: cilium
  listeners:
    - name: http
      protocol: HTTP
      port: 80
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              gateway-access: "true"
    - name: https
      protocol: HTTPS
      port: 443
      hostname: app.example.com
      tls:
        mode: Terminate
        certificateRefs:
          - name: app-tls
            kind: Secret
      allowedRoutes:
        namespaces:
          from: Same

5.2. HTTPRoute

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: my-route
  namespace: my-app
spec:
  parentRefs:
    - name: my-gateway
      namespace: gateway-system
      sectionName: https
  hostnames:
    - "app.example.com"
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /api
      backendRefs:
        - name: api-service
          port: 8080

5.3. Изоляция неймспейса с доступом от Gateway + DNS

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: isolate-allow-gateway
  namespace: my-app
spec:
  description: "Default deny для неймспейса; разрешены Gateway и DNS"
  endpointSelector: {}
  ingress:
    - fromEntities:
        - ingress
      toPorts:
        - ports:
            - port: "8080"
              protocol: TCP
  egress:
    - toEndpoints:
        - matchLabels:
            io.kubernetes.pod.namespace: kube-system
            k8s-app: kube-dns
      toPorts:
        - ports:
            - port: "53"
              protocol: UDP
            - port: "53"
              protocol: TCP
        rules:
          dns:
            - matchPattern: "*"

5.4. Минимальный набор “Gateway + политики”

# Бэкенд принимает трафик от Cilium Gateway
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: app-allow-gateway
  namespace: my-app
spec:
  endpointSelector:
    matchLabels:
      app: my-app
  ingress:
    - fromEntities:
        - ingress
  egress:
    - toEndpoints:
        - matchLabels:
            io.kubernetes.pod.namespace: kube-system
            k8s-app: kube-dns
      toPorts:
        - ports:
            - port: "53"
              protocol: UDP
          rules:
            dns:
              - matchPattern: "*"
---
# Gateway может ходить в кластер
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
  name: allow-ingress-to-cluster
spec:
  endpointSelector:
    matchExpressions:
      - key: reserved:ingress
        operator: Exists
  egress:
    - toEntities:
        - cluster

6. Проверки после настройки

Что проверяемКак проверитьОжидаемый результат
Gatewaykubectl get gateway -AСтатус Programmed / Ready
HTTPRoutekubectl get httproute -AAccepted, ResolvedRefs
Политикиkubectl get cnp -n <ns>, kubectl get ccnpОбъекты на месте
Доступ через Gatewaycurl -vk https://<gateway-host>/...Ожидаемый HTTP-код приложения
Прямой доступ к pod IPcurl <pod-ip>:8080Timeout / DROP (если включена изоляция)
Трафик от Gatewayhubble observe --identity ingressALLOW к бэкенду
Нет лишних блокировокhubble observe --verdict DROPPED -n <ns>Нет неожиданных DROP
DNS работаетkubectl exec -it <pod> -- nslookup kubernetesРезолвится

7. Коротко

  1. Gateway API и Cilium Network Policies — одна система datapath, а не два независимых слоя.
  2. Между клиентом и приложением появляется промежуточное звено — Gateway-контроллер с identity ingress. Нужно разрешать трафик на двух участках: доступ к Gateway и доступ от Gateway к бэкенду.
  3. Для бэкенда используйте CiliumNetworkPolicy с fromEntities: [ingress]. Стандартный NetworkPolicy работает ограниченно и ненадёжно.
  4. Не фильтруйте клиентов по IP на бэкенде — источником для политики становится ingress. Делайте это на уровне Gateway, внешнего firewall или по HTTP-заголовкам (X-Forwarded-For, X-Envoy-External-Address) в приложении.
  5. Не ожидайте per-Gateway разграничения через одну entity ingress — в текущей модели Cilium это пока невозможно.
  6. Применяйте политики постепенно и проверяйте путь в Hubble.

Рекомендуется сначала добиться рабочего Gateway-пути, затем добавьте точечный allow от ingress, при необходимости делайте строже изоляцию.


Справка

Identity (сущности Cilium)

Что этоIdentity
Внешний мир (интернет)world
Cilium Ingress/Gateway проксиingress / reserved:ingress
Любой под в кластереcluster
Kubernetes API Serverkube-apiserver
Узел кластераhost

API-версии ресурсов

РесурсAPI Group
CiliumNetworkPolicycilium.io/v2
CiliumClusterwideNetworkPolicycilium.io/v2
GatewayClass, Gateway, HTTPRoute, GRPCRoutegateway.networking.k8s.io/v1

Примеры команд

# Проверка политик
kubectl get cnp -A
kubectl get ccnp

# Проверка Gateway
kubectl get gateway,httproute -A
kubectl describe gateway <name> -n <ns>

# Отладка через Hubble
hubble observe --verdict DROPPED --namespace <ns>
hubble observe --identity ingress --last 50