GKE Gateway: внутренний шлюз и Zero-Trust для east-west

Схема архитектуры GKE Gateway с внешним и внутренним балансировщиком нагрузки

В предыдущей статье «Beyond Ingress: A Deep Dive into GKE Gateway API, NEGs, and Dataplane V2» я рассказывал, как использовать GKE Gateway для публичного доступа к приложению из кластера GKE в качестве альтернативы Ingress, а также разбирал Dataplane V2 для прямой связи между подами и группы сетевых конечных точек (Network Endpoint Groups, NEGs). В этой статье мы снова обратимся к GKE Gateway, но теперь рассмотрим внутренний региональный шлюз (Internal Regional Gateway) для балансировки трафика «восток–запад» (east-west), то есть трафика между сервисами внутри кластера.

Опираясь на материал предыдущей статьи, мы включим в архитектуру внешний шлюз GKE, а для балансировки межсервисных взаимодействий в приложении Bank of Anthos воспользуемся классом шлюза (gatewayclass) внутреннего регионального балансировщика нагрузки (gke-l7-rilb).

Начнём с настройки безопасности: создадим сертификат для внешнего шлюза и секрет Kubernetes для внутреннего. В основе подхода лежит модель «Нулевого доверия» (Zero-Trust), обеспечивающая сквозную защиту приложения.

Создание внешнего и внутреннего сертификатов

В этой конфигурации мы развернём как внешний, так и внутренний шлюз: первый обеспечит доступ к приложению из интернета, второй — безопасную балансировку трафика между бэкенд-сервисами с помощью внутреннего регионального шлюза.

Сертификат для внешнего шлюза

Внешний шлюз (gke-l7-global-external-managed) смотрит в интернет, поэтому сертификат для HTTPS обязателен. Настроим его.

Включите API Certificate Manager:

gcloud services enable certificatemanager.googleapis.com

Создайте сертификат:

openssl req -x509 -newkey rsa:2048 -nodes -sha256 -days 365 \
-keyout boa-private-key.pem \
-out boa-cert.pem \
-subj "/CN=your.fqdn.com" \
-addext "subjectAltName=DNS:your.fqdn.com"

Загрузите внешний сертификат в Certificate Manager:

gcloud certificate-manager certificates create bank-external-cert \
--certificate-file="your-cert.pem" \
--private-key-file="your-private-key.pem" \
--location="global"

Создайте карту и запись Certificate Manager:

gcloud certificate-manager maps create bank-external-map --location="global"

gcloud certificate-manager maps entries create bank-external-entry \
--map="bank-external-map" \
--hostname="your.fqdn.com" \
--certificates="bank-external-cert" \
--location="global"

На этом настройка внешнего сертификата завершена.

Внутренний региональный шлюз и секрет Kubernetes

Для внутреннего регионального шлюза (gke-l7-rilb) в данной архитектуре будет использоваться секрет Kubernetes (Kubernetes Secret) для внутреннего SSL. Такой подход соответствует рекомендациям по шифрованию межсервисных взаимодействий в GKE, поскольку учётные данные хранятся непосредственно в кластере.

Создайте внутренний сертификат:

openssl req -x509 -newkey rsa:2048 -nodes -sha256 -days 365 \
  -keyout your-internal-private-key.pem \
  -out your-internal-cert.pem \
  -subj "/CN=api.internal.bank" \ #пример внутреннего API, можно использовать это значение
  -addext "subjectAltName=DNS:api.internal.bank" #пример внутреннего API, можно использовать это значение

Сначала создайте пространство имён (namespace), в котором будут размещены приложения в кластере GKE. Воспользуемся пространством имён bank-of-anthos. Затем создайте секрет Kubernetes из только что созданного файла внутреннего сертификата в пространстве имён bank-of-anthos:

kubectl create namespace bank-of-anthos

namespace/bank-of-anthos created

Теперь создайте секрет Kubernetes в этом пространстве имён:

kubectl create secret tls boa-internal-cert-secret \
    --cert=your-internal-cert.pem \
    --key=your-internal-private-key.pem \
    --namespace bank-of-anthos

secret/your-internal-cert-secret created

Сеть и подсеть только для прокси

Внутренние региональные шлюзы основаны на Envoy. Google Cloud не использует один IP-адрес на мосту — вместо этого для запуска прокси выделяется пул IP-адресов. Этот пул и есть подсеть только для прокси (Proxy-only Subnet). Настроим её в нашей VPC.

gcloud compute networks subnets create gke-proxy-only-subnet \
    --purpose=REGIONAL_MANAGED_PROXY \
    --role=ACTIVE \
    --region=REGION \
    --network=NETWORK \
    --range=CIDR

Теперь зарезервируем внутренний IP-адрес для внутреннего регионального шлюза, чтобы адрес внутреннего API Bank of Anthos (api.internal.bank) не изменился в случае удаления и повторного создания шлюза. Укажите имя подсети VPC, в которой работают узлы GKE — не имя подсети только для прокси, созданной на предыдущем шаге, — а в качестве параметра purpose используйте ключевое слово SHARED_LOADBALANCER_VIP:

gcloud compute addresses create bank-internal-lb-ip \
--region=us-west1 \
--subnet=vpc-01-subnet-01 \
--purpose=SHARED_LOADBALANCER_VIP

Развёртывание внешнего и внутреннего шлюзов

Теперь объединим всё вместе: применим глобальный внешний шлюз с созданной картой сертификатов и региональный внутренний шлюз с секретом Kubernetes.

Создайте файл external-gateway.yaml:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: bank-external-gateway
  namespace: bank-of-anthos
  annotations:
    networking.gke.io/certmap: bank-external-map
spec:
  gatewayClassName: gke-l7-global-external-managed
  listeners:
  - name: https
    protocol: HTTPS
    port: 443
    allowedRoutes:
      namespaces:
        from: Same

Создайте файл internal-gateway.yaml:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: bank-internal-gateway
  namespace: bank-of-anthos
spec:
  gatewayClassName: gke-l7-rilb
  listeners:
  - name: https
    protocol: HTTPS
    port: 443
    tls:
      mode: Terminate
      certificateRefs:
      - name: boa-internal-cert-secret # Секрет Kubernetes, созданный на предыдущем шаге
    allowedRoutes:
      namespaces:
        from: Same
  addresses:
  - type: NamedAddress
    value: bank-internal-lb-ip

Примените созданные файлы .yaml к кластеру GKE:

kubectl apply -f external-gateway.yaml -n bank-of-anthos
kubectl apply -f internal-gateway.yaml -n bank-of-anthos

Убедитесь, что оба шлюза успешно развёрнуты:

kubectl get gateways -n bank-of-anthos
NAME                    CLASS                            ADDRESS          PROGRAMMED   AGE
bank-external-gateway   gke-l7-global-external-managed   34.149.120.99    True         20h
bank-internal-gateway   gke-l7-rilb                      192.168.15.215   True         20h

Частая точка путаницы — соотношение между IP-адресом внутреннего регионального шлюза и подсетью только для прокси. Удобно представить их как два последовательных этапа единого пути:

Точка входа: виртуальный IP-адрес шлюза (Gateway VIP)

Виртуальный IP-адрес внутреннего регионального шлюза (192.168.15.215) выделяется из основной подсети VPC — той же, где находятся узлы GKE. Этот адрес служит стабильной конечной точкой для сервисов. Когда фронтенд обращается к бэкенд-сервисам, трафик направляется именно на этот VIP.

Подсеть только для прокси

VIP — это адрес назначения, однако подсеть только для прокси (192.168.64.0/20) обеспечивает реальную «рабочую площадку» для балансировщика нагрузки. Google Cloud разворачивает пул прокси Envoy для обработки трафика; этим прокси необходимы собственные выделенные IP-адреса из указанного диапазона для работы внутри VPC.

Передача управления: Source NAT (SNAT)

Когда запрос поступает на Gateway VIP по адресу 192.168.15.215, сетевой уровень передаёт его прокси Envoy. На этом этапе происходит трансляция исходящих адресов (Source Network Address Translation, SNAT).

В результате, когда трафик достигает подов Bank of Anthos, исходный IP-адрес, который видит под, будет адресом из подсети только для прокси, а не исходным IP-адресом клиента или Gateway VIP.

Развёртывание приложения Bank of Anthos

Инфраструктура gatewayclass готова — развернём приложение Bank of Anthos. Клонируйте приложение из публичного репозитория. Это развернёт его в конфигурации по умолчанию; необходимые изменения внесём после того, как все поды запустятся.

git clone https://github.com/GoogleCloudPlatform/bank-of-anthos
cd bank-of-anthos/

Разверните приложение Bank of Anthos в пространстве имён bank-of-anthos:

kubectl apply -f ./extras/jwt/jwt-secret.yaml -n bank-of-anthos
kubectl apply -f ./kubernetes-manifests -n bank-of-anthos

Убедитесь, что все поды находятся в состоянии RUNNING:

kubectl get pods -n bank-of-anthos
NAME                                  READY   STATUS    RESTARTS   AGE
accounts-db-0                         1/1     Running   0          19h
balancereader-5db49dfd95-9qz9t        1/1     Running   0          19h
contacts-678bb46775-d7m8p             1/1     Running   0          176m
frontend-777c7b7cf6-z8x48             1/1     Running   0          3h19m
ledger-db-0                           1/1     Running   0          19h
ledgerwriter-78f66947b6-xnnms         1/1     Running   0          19h
loadgenerator-5965569b75-9rhlx        1/1     Running   0          3h11m
transactionhistory-7c6f5d8665-cnwnq   1/1     Running   0          176m
userservice-56bdc48976-fhks4          1/1     Running   0          176m

Развёртывание HTTPRoute

Следующий шаг — развернуть маршруты HTTPRoute, которые укажут как внешнему пользовательскому шлюзу, так и внутреннему межсервисному шлюзу, к каким сервисам обращаться.

Внешний маршрут

Сервис frontend должен быть доступен снаружи, поэтому начнём с HTTPRoute для него и привяжем маршрут к внешнему шлюзу.

Создайте файл route-external.yaml:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: frontend-external-route
  namespace: bank-of-anthos
spec:
  parentRefs:
  - name: bank-external-gateway
  rules:
  - backendRefs:
    - name: frontend
      port: 80

Внутренний маршрут

Для маршрутизации трафика между frontend и внутренними сервисами через внутренний региональный шлюз определим HTTPRoute с перезаписью пути. Это гарантирует корректную маршрутизацию запросов к /balance и /ledger на корень соответствующих бэкенд-сервисов.

Создайте файл route-internal.yaml:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: backend-internal-route
  namespace: bank-of-anthos
spec:
  parentRefs:
  - name: bank-internal-gateway
  hostnames:
  - "api.internal.bank"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: "/balance"
    filters:
    - type: URLRewrite
      urlRewrite:
        path:
          type: ReplacePrefixMatch
          replacePrefixMatch: "/"
    backendRefs:
    - name: balancereader
      port: 80
  - matches:
    - path:
        type: PathPrefix
        value: "/ledger"
    filters:
    - type: URLRewrite
      urlRewrite:
        path:
          type: ReplacePrefixMatch
          replacePrefixMatch: "/"
    backendRefs:
    - name: ledgerwriter
      port: 80

Перенаправление трафика: интеграция фронтенда с внутренним шлюзом

Технические пояснения: зачем эти изменения необходимы

По умолчанию сервисы Bank of Anthos взаимодействуют через стандартный Kubernetes DNS (например, http://balancereader:8080). Описанные ниже изменения «ломают» это поведение по умолчанию, принудительно направляя весь трафик «восток–запад» от frontend через безопасные управляемые прокси Envoy внутреннего шлюза.

В продуктивной среде доверенный корневой центр сертификации (Root CA) обычно встраивается в хранилище доверия пода. В рамках данной лабораторной работы установка NODE_TLS_REJECT_UNAUTHORIZED="0" — стандартный способ обойти проверку TLS и позволить приложению принять самоподписанный сертификат внутреннего шлюза.

Сначала зададим переменные окружения, указывающие на наш HTTPS-эндпоинт. Эта команда перенастраивает приложение frontend для отправки запросов к бэкенду через внутренний шлюз с использованием новых HTTPS-эндпоинтов и пользовательского имени хоста. Кроме того, она отключает проверку TLS-сертификата, чтобы приложение принимало самоподписанные сертификаты, используемые в данной лабораторной среде, без ошибок безопасности:

kubectl set env deployment/frontend -n bank-of-anthos \
  BALANCEREADER_API_ADDR="https://api.internal.bank/balance" \
  LEDGERWRITER_API_ADDR="https://api.internal.bank/ledger" \
  NODE_TLS_REJECT_UNAUTHORIZED="0"

Затем необходимо применить патч к деплойменту frontend. Этот патч заставляет приложение frontend разрешать имя хоста api.internal.bank в VIP-адрес внутреннего шлюза без изменения DNS-зон VPC. Обновляя файл /etc/hosts пода, мы гарантируем корректную маршрутизацию всех внутренних межсервисных вызовов через региональный балансировщик нагрузки для SSL-терминации и перезаписи путей:

kubectl patch deployment frontend -n bank-of-anthos --type='merge' -p '
{
  "spec": {
    "template": {
      "spec": {
        "hostAliases": [
          {
            "ip": "192.168.15.215", #замените на свой IP-адрес
            "hostnames": ["api.internal.bank"]
          }
        ]
      }
    }
  }
}'

Проверка: верификация архитектуры «Нулевого доверия»

Инфраструктура развёрнута в соответствии с целевой архитектурой, приложение Bank of Anthos пропатчено — пора убедиться, что трафик проходит через шлюзы так, как ожидается.

Поскольку контейнер frontend использует минималистичный образ, для проверки задействуем эфемерный отладочный контейнер Kubernetes (ephemeral debug container) с подробным тестированием через curl. Это позволит проверить SSL-рукопожатие и маршрутизацию по путям с точки зрения приложения.

Запустите отладочный контейнер, прикреплённый к поду frontend:

kubectl debug -it $(kubectl get pods -l app=frontend -n bank-of-anthos -o jsonpath='{.items[0].metadata.name}') \
  -n bank-of-anthos \
  --image=curlimages/curl \
  --profile=general \
  --target=front -- sh

Оказавшись внутри отладочного контейнера — вы увидите приглашение /home/curl_user $ — выполните curl к внутреннему эндпоинту баланса:

curl -kv https://api.internal.bank/balance

В выводе должны присутствовать HTTP/1.1 200 OK и подробная информация о процессе рукопожатия.

Теперь проверим сквозной пользовательский сценарий, открыв внешний шлюз в браузере. Совет: не забудьте обновить записи DNS, иначе разрешение имён не будет работать.

Откройте https://your-fqdn.com.

Войдите с учётными данными по умолчанию (admin/password).

Если отображаются общий баланс и история транзакций, вся цепочка работает корректно:

Пользователь → Внешний шлюз (публичный SSL) → Под frontend → Внутренний шлюз (приватный SSL) → Бэкенд-сервисы.

Заключение

Перейдя от Ingress к GKE Gateway API, мы реализовали надёжную сетевую модель «Нулевого доверия», обеспечивающую безопасность трафика как на периметре, так и внутри кластера. Эта архитектура предоставляет масштабируемый способ управления сложными межсервисными взаимодействиями, сохраняя централизованный контроль над SSL/TLS-терминацией и маршрутизацией трафика.

© 2026 meganuke