如何在 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),我可以保證從客戶端到伺服器的所有流量都已加密,不可能從網路本地以外的任何內容中嗅探後端。
此外,隨著 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
我的 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)中?
*答:*因為遺留應用程序非常脆弱,並且在跨伺服器進行會話時會中斷。只要使用者停留在同一個後端,應用程序就會按預期工作。這最終會改變(重寫),但不會在短期內改變。
首先,這給您的網路伺服器增加了不必要的複雜性。
其次,在 LB 處終止 SSL 連接意味著您可以在客戶端使用 keepalive 進行連接,從而減少建立連接的複雜部分。此外,資源的最有效利用是將類似的工作負載分組。許多人將靜態和動態內容分開,LB 處的 SSL 意味著兩者可以通過同一連接來自不同的伺服器。
第三,SSL 通常以不同於您的 Web 應用程序所需的速率進行擴展。我認為缺少範例是由於單個 LB 對或循環 dns 對大多數人來說就足夠了。在我看來,您可能高估了 SSL 工作量。
另外,我不確定您對安全性的推理。除了網路伺服器已經在執行更多可能被利用的服務之外,如果 LB 中存在任何漏洞,那麼您也剛剛將它們引入您的網路伺服器!