Load-Balancing

HAProxy:SSL 終止,特定域萬用字元 SSL 證書請求例外

  • October 9, 2020

我是 HAProxy 的新手,大部分元件都按預期工作。目前設置是:如果我將新站點添加到其中一個平衡(在 LB 之後)伺服器,則證書由負載均衡器頒發和提供。所以 SSL Termination 可以正常使用正常 Let’s Encrypt 證書,但是我正在使用的服務在此設置中存在限制:

如果我將新站點添加到平衡伺服器並希望使用萬用字元*.wilddomain.com證書,則它不是由負載平衡器頒發,而是由平衡伺服器 (10.0.0.10) 頒發。由於 LE 驗證是通過 DNS 完成的,因此萬用字元證書現在有效並且可以在平衡伺服器上使用。

因此,現在我有一個負載均衡器,其中包含幾個正確使用的“正常”LE 證書,以及一個保存萬用字元證書的伺服器。

我的問題是:如何將 HAProxy 設置為僅針對特定域 (wilddomain.com) 傳遞萬用字元證書,同時通過 SSL 終止直接從 LB 提供所有其他證書。

我目前的配置是這樣的:

global
   log /dev/log        local0
   log /dev/log        local1 notice
   chroot /var/lib/haproxy
   stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
   stats timeout 30s
   user haproxy
   group haproxy
   daemon

   # Default SSL material locations
   ca-base /etc/ssl/certs
   crt-base /etc/ssl/private

   # Default ciphers to use on SSL-enabled listening sockets.
   # For more information, see ciphers(1SSL). This list is from:
   #  https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
   # An alternative list with additional directives can be obtained from
   #  https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=haproxy
   ssl-default-bind-ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE->
   ssl-default-bind-options no-sslv3

defaults
   log global
   mode        http
   option      httplog
   option      dontlognull
   timeout connect 5000
   timeout client  50000
   timeout server  50000
   errorfile 400 /etc/haproxy/errors/400.http
   errorfile 403 /etc/haproxy/errors/403.http
   errorfile 408 /etc/haproxy/errors/408.http
   errorfile 500 /etc/haproxy/errors/500.http
   errorfile 502 /etc/haproxy/errors/502.http
   errorfile 503 /etc/haproxy/errors/503.http
   errorfile 504 /etc/haproxy/errors/504.http

# Default Let's Encrypt backend server used for renewals and requesting certificates
backend letsencrypt-backend
   server letsencrypt 127.0.0.1:8888

# Load balancer settings
frontend load-balancer
   bind *:80

   bind *:443 ssl crt /etc/ssl/domain1.com/domain1.com.pem crt /etc/ssl/domain2.com/domain1.com.pem

   redirect scheme https code 301 if !{ ssl_fc }

   # See if its an letsencrypt request
   acl letsencrypt-acl path_beg /.well-known/acme-challenge/
   use_backend letsencrypt-backend if letsencrypt-acl

   mode http
   default_backend webservers

# Backend webservers (the attached servers to the load balancer)
backend webservers
   balance roundrobin
   option forwardfor
   cookie SRVNAME insert
   http-request set-header X-Forwarded-Port %[dst_port]
   http-request add-header X-Forwarded-Proto https if { ssl_fc }

   # Server www1
   server www1 10.0.0.10:80 weight 1 check
   # Server www2
   server www2 10.0.0.11:80 weight 1 check

編輯我

通過將以下內容添加到上述配置中,我更進一步,但這會在 HAProxy 日誌中產生“負載平衡器/2:SSL 握手失敗”。

frontend wildcard_tcp
   bind *:443
   option tcplog
   mode tcp 

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

   acl is_wilddomain req_ssl_sni -m end wilddomain.com

   use_backend wildcard_server_tcp  if is_wilddomain

backend wildcard_server_tcp
   mode tcp 
   server ssl-wildcard-server 10.0.0.10:443

這是一個合適且正確的解決方案嗎?還是有更好/性能更好的?是否有可能擁有一個僅負責 ssl-offload 的非常基本的後端伺服器?那麼僅用於頒發、更新和提供證書嗎?

非常感謝!

tl;dr 這可以通過配置 TCP 代理偵聽所有請求並使用SNI 擴展來完成:1) 呼叫 TCP 後端,將 ssl-offload 留給伺服器,或 2) 呼叫 HAProxy 的 HTTP 前端來執行 ssl -解除安裝。


HAProxy 可以配置為在同一 IP/埠中的不同域使用不同的證書,因此在bind執行 TLS 握手時在同一行中。可以使用綁定行中的crt-list關鍵字微調此配置。

但是,此類配置沒有將 ssl-offload 傳遞到後端伺服器的選項。HAProxy 前端應配置為執行 ssl-offload,或者應配置為mode tcp並將 ssl-offload 留給後端。

為了在同一 IP/埠中實現混合本地和遠端 ssl-offload,對於不同的域,應在 HAProxy 配置中添加另一個代理:

                                            +-----------------+
                                            |                 |
           +------+     (TCP request)       | wildcard server |
  O        |      | === *.wildcard.com ===> |                 |
 -|-   ==> | mode |                         +-----------------+
 / \       | tcp  | (local socket)  +-------------+
           |      | === others ===> |             |
           +------+                 | https front |
                                    | ssl-offload |
                                    |             |
                                    +-------------+
                                           |
                                           | (plain http request)
                                           |
                                           v
                                    +---------------+
                                    |               |
                                    | other servers |
                                    |               |
                                    +---------------+

以下片段具有前端 TCP 代理和本地 ssl-offload 前端。請注意,這將消耗兩倍的連接數,相應地調整全域 maxconn。

defaults
 timeout server 1s
 timeout client 1s
 timeout connect 1s
listen public
 mode tcp
 bind :443,:::443
 tcp-request inspect-delay 5s
 tcp-request content accept if { req.ssl_hello_type 1 }
 acl wildcard req.ssl_sni wildcard.local
 acl wildcard req.ssl_sni -m end .wildcard.local
 use_backend passthrough if wildcard
 server local_offload unix@/var/run/local.sock send-proxy-v2
backend passthrough
 mode tcp
 server ssl 10.0.0.10:443
listen local_offload
 mode http
 bind unix@/var/run/local.sock ssl crt /var/haproxy/crt.pem accept-proxy
 server plain0 10.0.0.10:80
 server plain1 10.0.0.11:80

引用自:https://serverfault.com/questions/1036927