如何在 HAProxy 负载平衡后面水平扩展 SSL 终止?

如何在 HAProxy 负载平衡后面水平扩展 SSL 终止?

我一直环顾四周,似乎没有人尝试像我一样扩展 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 终止卸载到运行 HAProxy 1.5 和 Tomcat 且仅在环回接口上监听的 Web 后端(AD),我可以保证从客户端到服务器的所有流量都是加密的,并且不可能从 Web 后端本地以外的任何东西进行嗅探。

此外,随着 SSL 需求的增加,我可以简单地在负载均衡器后面启动新的(廉价的)后端服务器。

最后,它消除了在面向外部的 LB 上运行证书的要求,并通过这样做增加了额外的安全性,因为受损的 LB 上不会有任何 pem 或证书。

我的情况似乎与这个非常相似:为什么没有水平可扩展软件负载均衡器平衡 SSL 的示例?但我没有使用基于文件的会话,并且如果可能的话,我想避免通过 IP 进行平衡,因为客户端可能来自 NAT 后面。

我尝试按照配置文档中的 HAProxy 说明使用带有 SSL ID 的 stick 表(http://cbonte.github.com/haproxy-dconv/configuration-1.5.html#4-stick%20store-response),但这似乎并没有将我的会话保持在一个后端服务器上(重新加载 A/admin?stats 页面,显示节点名称在所有后端服务器上反弹)。

显然,循环负载平衡有效,但粘性会话无效。

以下是我的 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,之后每次重新加载都显示 webB)。转到 显示http://LB:8888LB 的统计信息,并显示我的后端都处于健康状态。

当 SSL 在后端终止时,我需要进行哪些更改才能使会话坚持到一个后端?

编辑:问题:为什么不在后端服务器之间跳转并将会话存储在中央存储中(例如 memcached)?

回答:因为遗留应用程序非常脆弱,当会话跨服务器传输时会中断。只要用户停留在同一后端,应用程序就会按预期工作。这最终会改变(重写),但不会是短期内。

答案1

首先,这会给您的网络服务器增加不必要的复杂性。

其次,在 LB 上终止 SSL 连接意味着您可以在客户端使用 keepalive 连接,从而减少建立连接的复杂部分。此外,最有效的资源利用方式是对类似工作负载进行分组。许多人将静态内容和动态内容分开,而在 LB 上使用 SSL 意味着两者都可以通过同一连接来自不同的服务器。

第三,SSL 的扩展速度通常与 Web 应用所需的速度不同。我认为缺少示例是因为单个 LB 对或循环 DNS 对大多数人来说就足够了。在我看来,您可能高估了 SSL 工作量。

另外,我不确定你关于安全性的推理。除了 Web 服务器已经运行了更多可能存在漏洞的服务之外,如果 LB 中存在任何漏洞,那么你也会将它们引入到你的 Web 服务器上!

相关内容