我是 HAProxy 的新手,大部分功能都按预期运行。当前设置是:如果我将新站点添加到平衡服务器(LB 后面)之一,则证书由负载均衡器颁发和提供。因此,SSL 终止与常规 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 日志中产生“load-balancer/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?那么只用于颁发、更新和提供证书?
非常感谢!
答案1
总结:可以通过配置一个 TCP 代理监听所有请求,然后使用SNI 扩展可以执行以下操作之一:1)调用 TCP 后端,将 ssl-offload 留给服务器,或者 2)调用执行 ssl-offload 的 HAProxy 的 HTTP 前端。
bind
HAProxy 可以配置为在执行 TLS 握手时对同一 IP/端口中的不同域使用不同的证书,因此在同一行中。可以使用crt 列表绑定行中的关键字。
但是,这种配置没有将 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