I am new to HAProxy and got most parts working as expected. The current setup is: If I add a new site to one of the balanced (behind the LB) servers, the certificate is issued and served by the Load Balancer.
So SSL Termination is working fine with regular Let's Encrypt certificates, but I have a limitation in this setup by the service I am using:
If I add a new site to a balanced server and want to use a wildcard *.wilddomain.com
certificate, it is not issued by the Load Balancer, but by the balanced server (10.0.0.10). As LE validation is done over DNS, the wildcard certificate is valid and available on the balanced server now.
So now I have a Load Balancer with several "regular" LE certs which are used corretly, and a server behind which holds the wildcard certificate.
My question is: How can I set up HAProxy to passthrough to the wildcard certificate only for a specific domain (wilddomain.com) while serving all other certificates directly from the LB with SSL Termination.
My current config is this:
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
EDIT I
I came a bit further by adding the following to the above config, but this produces "load-balancer/2: SSL handshake failure" in the HAProxy logs.
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
Is this a suitable and correct solution? Or is there a better / more performant one?
Would it be even possible to have a very basic backend server that is only responsible for the ssl-offload? So only for issuing, renewing and serving the certificates?
Thanks so much!
Best Answer
tl;dr this can be done configuring a TCP proxy listening all requests and using the SNI extension to either: 1) call a TCP backend which leaves the ssl-offload to the server, or 2) call a HAProxy's HTTP frontend that does the ssl-offload.
HAProxy can be configured to use distinct certificates for distinct domains in the same IP/port, hence in the same
bind
line, when performing a TLS handshake. This configuration can be fine tuned using the crt-list keyword in the bind line.Such configuration however doesn't have an option to passthrough the ssl-offload to a backend server. A HAProxy frontend should be configured to either perform the ssl-offload, or should be configured as
mode tcp
and leave the ssl-offload to the backend.In order to achieve mixed local and remote ssl-offload in the same IP/port, for distinct domains, another proxy should be added to the HAProxy configuration:
The following snippet has the fronting TCP proxy and a local ssl-offload frontend. Note that this will consume twice the number of connections, tune global maxconn accordingly.