Почему для правильной маршрутизации HAProxy на основе SNI требуется инструкция интерфейса «tcp-request content accept»?

Почему для правильной маршрутизации HAProxy на основе SNI требуется инструкция интерфейса «tcp-request content accept»?

Недавно я попытался настроить маршрутизацию на основе SNI на HAProxy для mongodb+srvподключения по протоколу mongodb.

Я заставил это работать, но это не работало, пока я не поставил

tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }

в моей конфигурации frontend он начал работать правильно

Без них (илитолько с однимиз них) я продолжал получать спорадические сбросы соединения для примерно 70% моих запросов. Обратите внимание, что иногда соединение все же удавалось установить.

В соответствии сhttp://cbonte.github.io/haproxy-dconv/2.0/configuration.html#4-tcp-request%20content

Правила на основе содержимого оцениваются в точном порядке их объявления. Если ни одно правило не соответствует или его нет, то действие по умолчанию — принять содержимое. Нет определенного ограничения на количество правил, которые могут быть вставлены.

Так что это должно было работать по умолчанию.

Либо я упускаю какую-то фундаментальную концепцию TCP, либо какую-то конкретную деталь соединения Mongo. Но я заметил, что почти у всех есть одно и то же правило, определенное при переходе на маршрутизацию на основе SNI, так что это должно иметь смысл для большинства людей.

Рабочая конфигурация HAProxy:

    frontend fe_mongo-nonprod
      bind :27017
      tcp-request inspect-delay 5s
      tcp-request content accept if { req_ssl_hello_type 1 }

      acl mongoAtlas-01 req_ssl_sni -i host1.mongodb.net
      acl mongoAtlas-02 req_ssl_sni -i host2.mongodb.net
      acl mongoAtlas-03 req_ssl_sni -i host3.mongodb.net
      use_backend be_mongo-nonprod if mongoAtlas-01 || mongoAtlas-02 || mongoAtlas-03

    backend be_mongo-nonprod
      mode tcp

      acl mongoAtlas-01 req_ssl_sni -i host1.mongodb.net
      acl mongoAtlas-02 req_ssl_sni -i host2.mongodb.net
      acl mongoAtlas-03 req_ssl_sni -i host3.mongodb.net

      use-server server-01 if mongoAtlas-01
      use-server server-02 if mongoAtlas-02
      use-server server-03 if mongoAtlas-03

      server server-01 host1.mongodb.net:27017
      server server-02 host2.mongodb.net:27017
      server server-03 host3.mongodb.net:27017

Также стоит отметить, что HAProxy развернут в Kubernetes (управляемый Google кластер в GCP), имеет istio-sidecar и доступен через службу Internal LoadBalancer k8s.

Как указано выше, конфигурация работает отлично. Мне интересно, почему tcp-request content acceptэто абсолютно необходимо и почему соединение не [всегда] принимается по умолчанию здесь.

решение1

Я попытаюсь ответить на свой собственный вопрос, немного подумав.

Итак, в документации HAProxy также есть следующее упоминание: http://cbonte.github.io/haproxy-dconv/2.0/configuration.html

Если необходимо переключение контента, рекомендуется сначала дождаться полного приветствия клиента (тип 1), как в примере ниже.

Теперь, поскольку данные SNI также содержатся в разделе Client Hello запроса (https://www.rfc-editor.org/rfc/rfc6066#page-6) Мое предположение следующее:

В случае, если Client Hello распределен по нескольким TCP-пакетам и нет tcp-request content accept is configured, при получении первого пакета HAProxy пытается маршрутизировать его, и происходит одно из двух:

  1. Если настроен бэкэнд по умолчанию, пакет пересылается на этот бэкэнд.
  2. Если бэкэнд по умолчанию не настроен (в моем случае), пакет отбрасывается и соединение сбрасывается.

Также обратите внимание, что даже в первом описанном случае сервер HAProxy, на который указывает в разделе backend, может также требовать SNI. Поэтому пересылка неполного запроса без указания SNI также приведет к сбросу соединения.

Связанный контент