Управляемый SSL сертификат для TCP лоадбалансера в Kubernetes

от автора

В Altenar мы часто используем RabbitMQ как точку входа для наших продуктов. В данном случае речь пойдет о системе, которая рассчитывает спортивные данные и предоставляет их как внутренним системам организации, так и внешним пользователям за её пределами. При проектировании системы для компонента RabbitMQ были определены следующие требования:

  • Endpoint защищён SSL-сертификатом.

  • RabbitMQ размещён в Kubernetes. Мы используем RabbitMQ cluster operator и RabbitMQ messaging topology operator.

  • Для этого проекта мы используем Google Cloud. Как и в случае с Let’s Encrypt, вы можете использовать управляемый SSL-сертификат Google Managed SSL Certificate для любого публичного HTTP Load Balancer в Google Cloud. Однако у такого подхода есть несколько ограничений, основные из которых:

    • Работает только с HTTP Load Balancer.

    • Работает только с публичными Load Balancer.

Согласно документации RabbitMQ, существует два распространённых подхода для подключения клиентов:

  1. Настроить RabbitMQ для обработки TLS-подключений.

  2. Использовать прокси или load balancer (например, HAproxy) для выполнения TLS-терминации клиентских подключений и использовать обычные TCP-подключения к узлам RabbitMQ.

Мы планировали использовать Google Managed Certificate для TCP/SSL Proxy Load Balancer в Google Cloud. Однако из-за вышеупомянутых ограничений управляемые сертификаты не поддерживаются для TCP Load Balancer. С другой стороны, Google позволяет использовать SSL-сертификаты для нескольких типов Load Balancer, что мы решили изучить. В результате итоговый проект выглядел бы следующим образом:

Мы хотели создать стандартный HTTP-сервис заглушку, который был бы доступен на порту 443 и использовался для управления SSL-сертификатом и его автоматического обновления.

При этом мы планировали иметь отдельный endpoint, который бы использовал тот же IP-адрес и тот же SSL-сертификат.

Рассмотрим каждую часть решения подробнее.


GKE Кластер

В качестве основной хостинг-платформы мы используем GKE (Google Kubernetes Engine). Мы используем Google Terraform module для настройки приватного GKE-кластера, конфигурируем Workload Identity и устанавливаем AutoNEG controller в кластер.

Мы не будем углубляться в детали работы Workload Identity с AutoNEG в этой статье, так как они используются в соответствии с официальной документацией.

После выполнения всех подготовительных шагов мы разворачиваем сервис-заглушку “Hello World” в кластере и подключаем его к Google Load Balancer с помощью AutoNEG. Ниже представлен YAML-конфигурация для этого:

apiVersion: apps/v1 kind: Deployment metadata:   name: gcp-tls-certificate-issuer   labels:     app: gcp-tls-certificate-issuer   annotations:     deployment.kubernetes.io/revision: '1' spec:   replicas: 2   selector:     matchLabels:       app: gcp-tls-certificate-issuer   template:     metadata:       labels:         app: gcp-tls-certificate-issuer     spec:       containers:         - name: ok           image: assemblyline/ok:latest           ports:             - containerPort: 8888               protocol: TCP           imagePullPolicy: Always           securityContext:             capabilities:               drop:                 - ALL             runAsUser: 1000             runAsGroup: 3000             runAsNonRoot: true             readOnlyRootFilesystem: true       restartPolicy: Always       terminationGracePeriodSeconds: 30   strategy:     type: RollingUpdate     rollingUpdate:       maxUnavailable: 25%       maxSurge: 25%   revisionHistoryLimit: 10   progressDeadlineSeconds: 600 --- apiVersion: v1 kind: Service metadata:   name: gcp-tls-certificate-issuer   labels:     app: gcp-tls-certificate-issuer   annotations:     cloud.google.com/neg: '{"exposed_ports": {"8888":{"name": "gcp-tls-certificate-issuer"}}}'     controller.autoneg.dev/neg: '{"backend_services":{"8888":[{"name":"envcode-rabbit-https-backend-service","max_connections_per_endpoint":10000}]}}' spec:   ports:     - name: http       protocol: TCP       port: 8888       targetPort: 8888   selector:     app: gcp-tls-certificate-issuer   clusterIP: 10.10.12.130   clusterIPs:     - 10.10.12.130   type: ClusterIP

Обратите внимание на аннотацию в конфигурации Service. Именно через неё AutoNEG добавляет этот сервис в Google Load Balancer как backend.

Google Load Balancer

Следующая часть настраивается вне GKE и создаётся отдельно. Google Load Balancer — это не единый объект, а набор различных объектов, объединённых вместе. Ниже представлен код Terraform с комментариями:

resource "google_compute_managed_ssl_certificate" "rabbitmq" {   project = var.project   name    = "${var.environment_name}-google-managed-certificate-rabbitmq"    managed {     domains = ["rabitmq.example.com."]  # Replace with your domain   } }  # reserved IP address resource "google_compute_global_address" "default" {   project = var.project   name    = "tcp-proxy-xlb-ip" }  output "rabbitmq-ip" {   value = google_compute_global_address.default.address }  # forwarding rule for TCP Loadbalanser resource "google_compute_global_forwarding_rule" "default" {   project               = var.project   name                  = "${var.environment_name}-tcp-global-loadbalancer"   provider              = google   ip_protocol           = "TCP"   load_balancing_scheme = "EXTERNAL"   port_range            = "5671"   target                = google_compute_target_ssl_proxy.default.id   ip_address            = google_compute_global_address.default.id } # https://cloud.google.com/load-balancing/docs/ssl # When you use Google-managed SSL certificates with SSL Proxy Load Balancing, the frontend port for traffic must be 443 to enable the Google-managed SSL certificates to be provisioned and renewed.  # forwarding rule for HTTPS Loadbalanser resource "google_compute_global_forwarding_rule" "https" {   project               = var.project   name                  = "${var.environment_name}-https-global-loadbalancer"   provider              = google   ip_protocol           = "TCP"   load_balancing_scheme = "EXTERNAL"   port_range            = "443"   target                = google_compute_target_ssl_proxy.https.id   ip_address            = google_compute_global_address.default.id }  resource "google_compute_target_ssl_proxy" "default" {   project          = var.project   name             = "${var.environment_name}-global-loadbalancer-tcp-proxy"   backend_service  = google_compute_backend_service.default.id   ssl_certificates = [google_compute_managed_ssl_certificate.rabbitmq.id] }  resource "google_compute_target_ssl_proxy" "https" {   project          = var.project   name             = "${var.environment_name}-global-loadbalancer-https-proxy"   backend_service  = google_compute_backend_service.https.id   ssl_certificates = [google_compute_managed_ssl_certificate.rabbitmq.id] }  # backend service For RabbitMQ Autoneg resource "google_compute_backend_service" "default" {   project = var.project   name    = "${var.environment_name}-tcp-backend-service"   protocol              = "TCP"   port_name             = "tcp"   load_balancing_scheme = "EXTERNAL"   timeout_sec           = 10   health_checks         = [google_compute_health_check.default.id]   session_affinity      = "CLIENT_IP"    # We don't want TF to remove whatever was configured by AutoNEG   lifecycle {     ignore_changes = [backend]   } }  # backend service For HTTPS Autoneg resource "google_compute_backend_service" "https" {   project               = var.project                            #that's what you use in the service annotations   name                  = "${var.environment_name}-https-backend-service"     protocol              = "TCP"   port_name             = "tcp"   load_balancing_scheme = "EXTERNAL"   timeout_sec           = 10   health_checks         = [google_compute_health_check.https.id]    # We don't want TF to remove whatever was configured by AutoNEG   lifecycle {     ignore_changes = [backend]   } }  resource "google_compute_health_check" "default" {   project            = var.project   name               = "tcp-proxy-health-check"   description        = "Backend service for AutoNEG"   timeout_sec        = 1   check_interval_sec = 1    tcp_health_check {     port = "5672" #use container port   } }  resource "google_compute_health_check" "https" {   project            = var.project   name               = "https-proxy-health-check"   description        = "Backend service for AutoNEG"   timeout_sec        = 1   check_interval_sec = 1    tcp_health_check {     port = "8888" #use container port   } }

Как вы можете видеть, мы создали один IP-адрес и один SSL-сертификат, а затем использовали их в двух правилах перенаправления (forwarding rules). Это позволило нам использовать управляемый SSL-сертификат для TCP Load Balancer.

Не забудьте настроить DNS и указать IP-адрес на правильное имя хоста, чтобы всё заработало.

Совет: В GKE есть deployment l7-default-backend. Возможно, было бы достаточно создать сервис с аннотациями AutoNEG и направить его на pod-ы этого deployment. Попробуйте это и сообщите в комментариях, если это работает.


ссылка на оригинал статьи https://habr.com/ru/articles/863870/