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
I ended in a very frustrating trial & error loop and don’t get further. So I really appriciate your answers
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?
happy to share that with you! So my full config is:
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
# !!! UNTIL HERE IS AUTOCONFIGURED BY THE SERVICE USED
# !!! FROM HERE I EXTEND THE AUTO-CONFIGURATION
# Add global settings
global
tune.ssl.default-dh-param 2048
# Pass SSL requests for *.ouun.io to server ouun-certs
frontend wildcard_tcp
mode tcp
bind :443,:::443
option tcplog
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
acl is_wilddomain req.ssl_sni wilddomain.com
acl is_wilddomain req.ssl_sni -m end .wilddomain.com
# Offload wilddomain.com TCP requests
use_backend wildcard_server_tcp if is_wilddomain
# Route to default config otherwise
default_backend webservers
backend wildcard_server_tcp
mode tcp
server www1 10.0.0.11:443
You are binding twice to port 443, you cannot do that. Your OS will load-balance between the two frontend which is absolutely NOT what you want.
You need a single frontend on port 443, in TCP mode, which is looking at the SNI value for routing decision making. For domains that you want to terminate locally, you need to define a backend that reconnects to a SSL terminating frontend with your configuration.
So for example:
move the frontend load-balancer from port *:443 to 127.0.0.1:1443
create a backend in tcp mode to reconnect to 127.0.0.1:1443
use that backend as a default_backend in wildcard_tcp, as opposed to webservers
Also see this example (slightly different use-case):
Thank you @lukastribus, I will check that out in more detail. But the first issue I face is, that I am using a service for HAProxy where I am not able to modify the main part of the config but can only extend it. Please see the following comment in my config above:
# !!! UNTIL HERE IS AUTOCONFIGURED BY THE SERVICE USED
# !!! FROM HERE I EXTEND THE AUTO-CONFIGURATION
So I already did a research but was not able to figure out, whether it is possible to “overwrite” parts of the non-editable config in my extended config. No idea how to follow your recommendation otherwise to