HAProxy:特定域的 SSL 终止,但通配符 SSL 证书请求除外

HAProxy:特定域的 SSL 终止,但通配符 SSL 证书请求除外

我是 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 前端。


bindHAProxy 可以配置为在执行 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

相关内容