Ssl

具有 SNI 和不同 SSL 設置的 HAProxy

  • May 4, 2019

我的兩個站點都有 HAProxy,其中一個是公共的,一個是私有的。

www.mysite.com private.mysite.com

Atm,我正在使用這樣的 haproxy:

frontend mysite_https
 bind *.443 ssl crt /etc/mycert.pem ca-file /etc/myca.pem verify optional no-sslv3
 mode http
 acl domain_www     hdr_beg(host) -i www.
 acl domain_private hdr_beg(host) -i private.
 acl path_ghost     path_beg         /ghost/
 acl clientcert     ssl_c_used

 redirect location https://www.example.com if path_ghost !clientcert
 redirect location https://www.example.com if !domain_www !clientcert

 use_backend bknd_private if domain_private
 use_backend bknd_www     if domain_www

 default_backend bknd_www

這應該做的是要求客戶證書(可選)並繼續。如果域不是 www.example.com 且訪問者無法提供正確的證書或路徑為 /ghost/ 且訪問者無法提供正確的證書,則應重定向到https://www.example.com

到目前為止,這工作正常。但是,Mac 使用者使用 Safari 瀏覽我的網站時抱怨說,他們在瀏覽https://www.example.com/時不斷被要求提供證書,而例如 Firefox 僅在瀏覽https://private.example時詢問.com/https://www.example.com/ghost/

顯然這就是 Safari 的工作方式,所以我無法解決這個問題。我的想法是使用 SNI 來劃分不同的前端

frontend mysite_https
 bind *.443 ssl crt /etc/mycert.pem no-sslv3

frontend private_https
 bind *.443 ssl crt /etc/mycert.pem ca-file /etc/myca.pem verify optional no-sslv3

當然這不起作用,因為

一種。我不能讓兩個前端監聽埠 443,而只有一個公共 IP b。我還沒有找到一種方法來說“use_frontend if domain_www”或類似的東西。(僅 use_backend 或 use-server)

我也試過用三個 haproxy 伺服器來做

frontend haproxy-sni
bind *:443 ssl crt /etc/mycert.pem no-sslv3
mode tcp

tcp-request inspect-delay 5s
tcp-request content accept if { req.ssl_hello_type 1 }

acl domain_www ssl_fc_sni_end -i www.example.com

use-server server1 haproxy-private.lan  if !domain_www
use-server server2 haproxy-public.lan   if domain_www

這行得通,但是這裡的問題是 haproxy-private 請求客戶端證書,但請求沒有到達瀏覽器。不知何故,haproxy-sni 放棄了請求。

此外,我現在有三個不可取的 haproxy 伺服器(儘管如果我找不到更好的解決方案,這是一個可能的選擇)。

最好我想要這樣的東西(編造..不知道真正的選擇)

frontend mysite_https
 bind *.443 ssl crt /etc/mycert.pem no-sslv3
 mode http

 acl domain_www     hdr_beg(host) -i www.
 acl domain_private hdr_beg(host) -i private.
 acl path_ghost     path_beg         /ghost/

 ssl_options ca-file /etc/myca.pem verify optional if !www_domain          # made up!
 ssl_options ca-file /etc/myca.pem verify optional if !path_ghost          # made up!

 acl clientcert     ssl_c_used

 redirect location https://www.example.com if path_ghost !clientcert
 redirect location https://www.example.com if !domain_www !clientcert
 ...

我希望有人可以幫助我解決這個問題…

我找到了解決這個問題的方法,不需要額外的伺服器或服務。我不完全確定這是否不會產生新問題。對我來說,它現在似乎有效。

我這樣做的方式是為需要不同 ssl 設置的每個域創建一個前端。然後我將這些前端的綁定選項設置為高埠(公共無法訪問這些埠!)。

我創建了另一個在埠:443 上偵聽的前端,以根據 SNI 劃分流量,並將後端伺服器設置為 127.0.0.1:high-port。

這樣,我在 haproxy 中創建了一個循環

[incoming]->[haproxy:443]->[haproxy:7000]->[www.intern.lan]
[incoming]->[haproxy:443]->[haproxy:8000]->[private.intern.lan]

這是配置部分。

frontend frnd_snipt                                             # Frontend_SNI-PassThrough (snipt)
 bind *:443                                                    # Do not use bind *:8443 ssl crt etc....!
 option tcplog
 mode tcp 

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

 acl subdomain_is_www   req_ssl_sni -i www.example.com
 acl subdomain_is_www   req_ssl_sni -i example.com
 acl subdomain_is_private req_ssl_sni -i private.example.com

 use_backend bknd_snipt_private if subdomain_is_private
 use_backend bknd_snipt_www  if subdomain_is_www

backend bknd_snipt_www
 mode tcp                                              # tcp mode must match the frontend mode - already set as default in [global]
 server snipt-www 127.0.0.1:7000                       # run without "check", otherwise haproxy checks itself all the time!

backend bknd_snipt_private
 mode tcp     
 server snipt-private 127.0.0.1:8000                   # also, don't add "ssl" when in tcp mode. "ssl" is an http mode option (result in "NO-SRV" when set in tcp)

##### NORMAL HAPROXY PART #####
frontend www_example_com                                # this frontend can be in tcp or http mode...
 bind *:7000 ssl crt /etc/mycert.pem no-sslv3          # www. frontend with normal https
 mode http
 option httplog


frontend private_example_com
 bind *:8000 ssl crt /etc/mycert.pem ca-file /etc/myca.pem verify optional no-sslv3        # private. frontend with client certificate request.
 mode http
 option httplog
 ... # whatever you have in your frontend

如果有人對此有想法,或者知道為什麼這可能是個壞主意,請告訴我。它有效,但我想知道為什麼 use_frontend 不是一個選項。也許是因為無論出於何種原因,這是不應該做的事情。

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