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

Bri*_*onS 6 ssl scaling load-balancing 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
Run Code Online (Sandbox Code Playgroud)

为什么这个疯狂的设置Internet -> HAProxy (tcp mode) -> HAProxy (http mode) -> Tomcat?两个词:安全性和可扩展性

通过将 SSL 终端卸载到运行 HAProxy 1.5 的 Web 后端 (AD) 和仅在环回接口上侦听的 Tomcat,我可以保证从客户端到服务器的所有流量都经过加密,不可能从任何非本地网络嗅探到网络后端。

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

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

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

我已经尝试按照配置文档中的 HAProxy 说明使用带有 SSL ID 的棒表(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
Run Code Online (Sandbox Code Playgroud)

我的 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
Run Code Online (Sandbox Code Playgroud)

在此配置中,SSL(或非 SSL)连接以循环方式通过 LB 路由到后端之一。但是,当我重新加载页面(发出新请求)时,很明显我会移动到另一个后端,无论是否使用 SSL。

我通过转到https://LB/haproxy具有节点名称的后端统计页面的 URL来测试这一点(第一次显示 webA,重新加载后显示 webB,随后每次重新加载时显示)。将http://LB:8888显示 LB 的统计数据并显示我的后端都健康。

当 SSL 在后端终止时,我需要更改什么才能使会话坚持一个后端?

编辑:问题:为什么不跨后端服务器反弹并将会话存储在中央存储(如 memcached)中?

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

Jam*_*yan 4

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

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

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

我也不确定你关于安全的推理。除了网络服务器已经运行了更多可能被利用的服务这一事实之外,如果负载均衡器中存在任何漏洞,那么您也将它们引入了您的网络服务器!