Назад | Перейти на главную страницу

Как горизонтально масштабировать завершение SSL за балансировкой нагрузки HAProxy?

Я смотрел вокруг, и, похоже, никто не пытается масштабировать завершение SSL, как я, и мне любопытно, почему мой подход кажется таким необычным.

Вот что я хочу сделать и поясню почему:

  10.0.1.1  10.0.1.2 - 10.0.1.5
-----+--------+----+----+----+
     |        |    |    |    |
  +--+--+   +-+-++-+-++-+-++-+-+
  | LB1 |   | A || B || C || D |
  +-----+   +---++---++---++---+
haproxy 1.5 haproxy 1.5 + tomcat
 tcp mode    http mode

Почему эта сумасшедшая установка Internet -> HAProxy (tcp mode) -> HAProxy (http mode) -> Tomcat? В двух словах: безопасность и масштабируемость

Разгрузив завершение SSL на веб-серверы (AD), которые запускают HAProxy 1.5 и Tomcat, прослушивающие только интерфейс обратной петли, я могу гарантировать, что весь трафик будет зашифрован от клиента к серверу без возможности прослушивания чего-либо, не локального в Интернете. бэкэнд.

Кроме того, по мере роста спроса на SSL я могу просто запускать новые (дешевые) серверы позади балансировщика нагрузки.

Наконец, он устраняет требование наличия сертификатов на внешнем LB и добавляет дополнительную безопасность, делая это, поскольку скомпрометированный LB не будет иметь на нем никаких pems или сертификатов.

Моя ситуация кажется очень похожей на эту: почему нет примеров горизонтально масштабируемых программных балансировщиков нагрузки, балансирующих ssl? но я не использую сеансы на основе файлов и, если возможно, я бы хотел избежать балансировки по IP, поскольку клиенты могут приходить из-за NAT.

Я пробовал следовать инструкциям HAProxy в документе конфигурации для использования таблицы стик с идентификатором SSL (http://cbonte.github.com/haproxy-dconv/configuration-1.5.html#4-stick%20store-response), но это не похоже на то, чтобы сохранить мой сеанс на одном внутреннем сервере (перезагрузка страницы статистики A / admin?, которая показывает, что имя узла перескакивает через все мои внутренние серверы).

Очевидно, что циклическая балансировка нагрузки работает, а липкие сеансы - нет.

Вот пример моей конфигурации LB:

global
    log 127.0.0.1 local0 notice
    maxconn 200
    daemon
    user appserver
    group appserver
    stats socket /tmp/haproxy

defaults
    log     global
    mode    tcp
    timeout client  5000ms
    timeout connect 50000ms
    timeout server  50000ms

    option contstats

frontend frontend_http
    log global
    bind *:80
    default_backend backend_http_servers

frontend frontend_ssl
    log global
    bind *:443
    default_backend backend_servers

listen stats :8888
    mode http
    stats enable
    stats hide-version
    stats uri /

#################################################################################################
## NOTE: Anything below this section header will be generated by the bootstrapr process and may be 
##       re-generated at any time losing manual changes
#################################################################################################
##          BACKENDS
#################################################################################################
backend backend_http_servers
    mode tcp

    #option httpchk

    server webA:8081 webA:8081 check port 8081
    server webB:8081 webB:8081 check port 8081
    # This configuration is for HTTPS affinity from frontdoor to backend

    # Learn SSL session ID from both request and response and create affinity
    backend backend_servers
    mode tcp

    balance roundrobin
    option ssl-hello-chk
    #option httpchk

    # maximum SSL session ID length is 32 bytes
    stick-table type binary len 32 size 30k expire 30m

    acl clienthello req_ssl_hello_type 1
    acl serverhello rep_ssl_hello_type 2

    # use tcp content accepts to detects ssl client and server hello
    tcp-request inspect-delay 5s
    tcp-request content accept if clienthello

    # no timeout on response inspect delay by default
    tcp-response content accept if serverhello

    # SSL session ID (SSLID) may be present on a client or server hello
    # Its length is coded on 1 byte at offset 43 and its value starts
    # at offset 44
    # Match and learn on request if client hello
    stick on payload_lv(43,1) if clienthello

    # Learn on response if server hello
    stick store-response payload_lv(43,1) if serverhello

############################################
# HTTPS BACKENDS
############################################
    server webA:8443 webA:8443 check port 8443
    server webB:8443 webB:8443 check port 8443

Пример моей конфигурации бэкэнда для webA выглядит так:

global
    log 127.0.0.1 local0 info
    maxconn 200
    daemon

defaults
    log     global
    mode    http
    option  dontlognull
    option  forwardfor
    option  httplog
    option  httpchk # checks server using HTTP OPTIONS on / and marks down if not 2xx/3xx status
    retries 3
    option redispatch
    maxconn         200
    timeout client  5000
    timeout connect 50000
    timeout server  50000

frontend frontend_http
    log global

    # only allow connections if the backend server is alive
    monitor fail if { nbsrv(backend_application) eq 0 }

    reqadd X-Forwarded-Proto:\ http    # necessary for tomcat RemoteIPValve to report the correct client IP and port
    reqadd X-Forwarded-Protocol:\ http # necessary because who knows what's actually correct?
    reqadd X-Forwarded-Port:\ 80       # also here for safety
    bind *:8081
    default_backend backend_application

frontend frontend_ssl
    log global

    # only allow connections if the backend server is alive
    monitor fail if { nbsrv(backend_application) eq 0 }

    reqadd X-Forwarded-Proto:\ https    # necessary for tomcat RemoteIPValve to report the correct client IP and port
    reqadd X-Forwarded-Protocol:\ https # necessary because who knows what's actually correct?
    reqadd X-Forwarded-Port:\ 443       # also here for safety
    reqadd X-Forwarded-SSL:\ on         # also here for safety
    bind *:8443 ssl crt /path/to/default.pem crt /path/to/additional/certs crt /path/to/common/certs
    default_backend backend_application
 #################################################################################################
#           Backends
#################################################################################################
backend backend_haproxy
    stats enable
    stats show-node
    stats uri    /haproxy
    acl acl_haproxy url_beg /haproxy
    redirect location /haproxy if !acl_haproxy

backend backend_application
    stats enable
    stats show-node
    stats uri  /haproxy
    option httpclose
    option forwardfor
    acl acl_haproxy url_beg /haproxy
    server 127.0.0.1:8080 127.0.0.1:8080 check port 8080

В этой конфигурации соединение SSL (или не SSL) маршрутизируется через LB к одному из бэкэндов в циклическом режиме. Однако, когда я перезагружаю страницу (делаю новый запрос), становится ясно, что я перехожу на другой бэкэнд независимо от SSL или нет.

Я проверяю это, перейдя в https://LB/haproxy который является URL-адресом страницы статистики серверной части с именем узла (показывает webA в первый раз, webB после перезагрузки и так далее при каждой последующей перезагрузке). Собирается http://LB:8888 показывает статистику для LB и показывает, что мои бэкенды все исправны.

Что мне нужно изменить, чтобы сеансы придерживались одного бэкэнда, когда SSL прерывается на бэкэнде?

Изменить: вопрос: почему бы не перебросить бэкэнд-серверы и не сохранить сеанс в центральном хранилище (например, memcached)?

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

Во-первых, это добавляет ненужной сложности вашим веб-серверам.

Во-вторых, завершение SSL-соединения на LB означает, что вы можете использовать keepalive на стороне клиента для соединения, уменьшая сложную часть установления соединения. Также наиболее эффективное использование ресурсов - это группировка рабочих нагрузок. Многие люди разделяют статический и динамический контент, SSL в LB означает, что оба могут поступать с разных серверов через одно и то же соединение.

В-третьих, SSL обычно масштабируется с другой скоростью, чем требует ваше веб-приложение. Я думаю, что отсутствие примеров связано с тем, что большинству людей достаточно одной пары LB или Round robin dns. Мне кажется, что вы переоцениваете нагрузку на SSL.

Также я не уверен в ваших рассуждениях относительно безопасности. В дополнение к тому факту, что на веб-сервере уже запущено гораздо больше сервисов с возможными эксплойтами, если в LB есть какие-либо уязвимости, то вы также только что добавили их на свои веб-серверы!