Сервис развивается: тестируем формат, собираем идеи, улучшаем сервис. Есть идеи?

Написать
Войти
Дайджесты новостей
Архитектура граничной маршрутизации трафика через HAProxy и Proxmox в Kubernetes

Двойной HAProxy на Proxmox: как безопасно публиковать сервисы Kubernetes из приватной сети

Пошаговая архитектура граничного шлюза для выделенного сервера с Proxmox: связка внешнего HAProxy на хосте с Ingress Controller кластера за NAT. Решение объединяет сквозной TLS passthrough со строгой фильтрацией SNI, генерацию конфигураций через Terraform и автоматический выпуск сертификатов ACME.

Двойной HAProxy на Proxmox: как безопасно публиковать сервисы Kubernetes из приватной сети

Развертывание собственного кластера Kubernetes на физическом выделенном сервере (bare metal) под управлением гипервизора Proxmox VE — популярное решение для снижения затрат на облачную инфраструктуру. Однако в отличие от управляемых облачных платформ (AWS, Google Cloud или Yandex Cloud), где для публикации приложений достаточно создать сервис типа LoadBalancer, на выделенном сервере инженеру обычно доступен всего один публичный IP-адрес, а все виртуальные машины кластера находятся в изолированной приватной сети за механизмом трансляции сетевых адресов (NAT).

Попытки пробросить входящий трафик через простые правила iptables или выставить порт API-сервера (6443) наружу создают прямые угрозы безопасности и не позволяют гибко управлять SSL/TLS-сертификатами для сотен сервисов. Надежным решением становится двухуровневая архитектура граничного шлюза (edge routing) на базе двух экземпляров балансировщика HAProxy: внешний экземпляр работает на самом хосте Proxmox, а внутренний — внутри кластера Kubernetes в роли Ingress Controller.

Топология двойного шлюза: разделение обязанностей

Главный принцип архитектуры — строгое разделение сетевой ответственности между хостом виртуализации и кластером:

  1. Внешний HAProxy на хосте Proxmox (Edge Router). Принимает входящие TCP/HTTP-пакеты из интернета на публичном IP. Его задача — фильтровать трафик по белому списку доменов, защищать control-plane кластера и перенаправлять пакеты на статические порты рабочих узлов без расшифровки пользовательского трафика (режим TLS Passthrough).
  2. Внутренний HAProxy Ingress Controller (Cluster Gateway). Развернут внутри Kubernetes как DaemonSet (по экземпляру на каждом рабочем узле). Он слушает статические порты NodePort (30080 для HTTP и 30443 для HTTPS), завершает (терминирует) шифрование TLS, читает правила Ingress и маршрутизирует запросы к подам приложений.

Сетевой маршрут пакета выглядит следующим образом: Клиент -> Публичный IP хоста Proxmox (HAProxy) -> Рабочие узлы K8s (NodePort 30080/30443) -> HAProxy Ingress Controller -> Сервисы и Поды приложений.

Защита API Kubernetes (kube-apiserver) организована локально: внешний HAProxy привязывает порт 6443 исключительно к интерфейсу 127.0.0.1, распределяя запросы по алгоритму leastconn между узлами control-plane. Прямой доступ к API из интернета полностью заблокирован: администраторы подключаются через SSH-туннелирование (port-forwarding) или доверенный VPN.

Механика фильтрации: ACME HTTP-01 и сквозной TLS по SNI

Особое внимание уделено обработке портов 80 и 443 на внешнем балансировщике:

  • Порт 80 (HTTP). Внешний HAProxy удаляет небезопасный заголовок Proxy (защита от уязвимости httpoxy), добавляет заголовки X-Real-IP и X-Forwarded-Proto, после чего сверяет заголовок Host со списком разрешенных доменов в /etc/haproxy/allow_domains.txt. Запросы к служебному пути /.well-known/acme-challenge/ беспрепятственно передаются в кластер для автоматического подтверждения прав на домен центрами сертификации (Let's Encrypt). Все остальные HTTP-запросы возвращают постоянный редирект на защищенный протокол HTTPS (HTTP 301).
  • Порт 443 (HTTPS). Внешний HAProxy работает в чистом режиме TCP-проксирования. Директива tcp-request inspect-delay 5s дает балансировщику время проинспектировать начальный пакет рукопожатия TLS (ClientHello) и извлечь имя запрашиваемого сервера через параметр SNI (req_ssl_sni). Если домен совпадает с белым списком или wildcard-шаблоном (.example.com), зашифрованный TCP-поток направляется на NodePort 30443 рабочих узлов. Любые соединения без SNI или с чужими именами немедленно сбрасываются.

Такой подход защищает внутренний кластер от сканеров уязвимостей и не требует хранения приватных SSL-ключей на хосте Proxmox.

Автоматизация периметра: шаблонизация в Terraform и валидация в Ansible

Чтобы исключить ошибки ручной правки конфигураций при добавлении или пересоздании виртуальных машин, генерация haproxy.cfg полностью автоматизирована:

  • Terraform. С помощью встроенной функции templatefile и ресурса local_file генерирует актуальный конфигурационный файл на основе IP-адресов созданных виртуальных машин из пулов kube_api_backend, worker_http_backend и worker_https_backend.
  • Ansible. Реализует безопасный пайплайн доставки: файл загружается на хост под временным именем haproxy.cfg.tmp, после чего выполняется валидация синтаксиса официальной утилитой: haproxy -c -f /etc/haproxy/haproxy.cfg.tmp Только при успешном коде возврата временный файл заменяет рабочий, и сервис перезагружается без разрыва существующих соединений (graceful reload).

Пошаговое руководство: развертывание Ingress Controller и cert-manager

После подготовки внешнего шлюза внутри кластера Kubernetes разворачиваются компоненты приема трафика и автоматического выпуска сертификатов.

1. Установка HAProxy Ingress Controller

По официальной инструкции из репозитория Helm-чартов HAProxy добавьте репозиторий и обновите индексы:

helm repo add haproxytech https://haproxytech.github.io/helm-charts
helm repo update

Создайте файл параметров values.yaml, настроив режим DaemonSet и указав статические порты:

controller:
  kind: DaemonSet
  service:
    type: NodePort
    nodePorts:
      http: 30080
      https: 30443
    externalTrafficPolicy: Local
  config:
    ssl-redirect: "false"

Параметр ssl-redirect: "false" критически важен: он предотвращает циклическую переадресацию запросов ACME HTTP-01, поскольку первичный редирект уже выполнен внешним HAProxy.

Выполните установку в выделенный namespace:

helm install haproxy-ingress haproxytech/kubernetes-ingress \
  --namespace haproxy-controller \
  --create-namespace \
  -f values.yaml

Проверьте статус подов и сервиса:

kubectl get pods -n haproxy-controller -o wide
kubectl get svc -n haproxy-controller
2. Установка cert-manager и выпуск сертификатов Let's Encrypt

Установите контроллер сертификатов по документации cert-manager:

helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --set crds.enabled=true

Настройте манифест ClusterIssuer для проверки по протоколу HTTP-01 согласно руководству cert-manager по HTTP-01. Рекомендуется всегда начинать со staging-сервера Let's Encrypt во избежание блокировки по лимитам запросов:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    email: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-staging-key
    solvers:
    - http01:
        ingress:
          class: haproxy

Примените манифест командой kubectl apply -f cluster-issuer-staging.yaml и создайте Ingress-ресурс с аннотацией cert-manager.io/cluster-issuer: letsencrypt-staging.

Послойная проверка и ограничения архитектуры

Контроль работоспособности схемы выполняется послойно:

  1. Проверка DNS. Убедитесь, что А-запись домена указывает строго на публичный IP хоста Proxmox.
  2. Проверка фильтрации портов. Запрос curl -I http://unauthorized.domain должен сбрасываться или отклоняться внешним HAProxy без передачи в кластер.
  3. Проверка статуса сертификата. Выполните команду kubectl get certificate,challenge,order -A. После успешного перехода заказа в статус Ready переключите ClusterIssuer на production-сервер Let's Encrypt.

Ограничения безопасности: Режим TLS Passthrough не позволяет внешнему HAProxy анализировать содержимое зашифрованных HTTPS-пакетов, поэтому базовый балансировщик не заменяет полноценный WAF (Web Application Firewall). При необходимости защиты от объемных распределенных атак (DDoS) перед хостом Proxmox рекомендуется использовать внешние облачные сервисы фильтрации трафика.