Can't connect to backend server if they are using SSL with wildcard Subject Alternative Names

For some reason I get “503 Service Unavailable” when trying to reach a backend server over 443/ssl where the target server uses wildcard SSL in their Subject Alternative Names. Here’s the full config you can test out to verify. It works when trying to reach backend without SSL or with SSL that doesn’t use wildcards. See comments in backend sections. How can I make it work with backend sites that uses wildcard SSL?

global
    log /dev/log    local0
    log /dev/log    local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

defaults
    log     global
    mode    http
    option  httplog
    option  dontlognull
    timeout connect 5000
    timeout client  50000
    timeout server  50000

frontend http-in
  bind *:80
  bind *:443 ssl crt /root/cert.pem

  default_backend app6

# this works when backend server on port 80 but fails on 443
# https://10.10.1.111/data/validatorstats.json
# cert uses wildcard
backend app1
  http-request set-header Host defier.net
  server server1 defier.net:443 ssl verify none
#  server server1 defier.net:80 check

# this works: https://10.10.1.111/document/txt/example.txt
# cert is not using wildcard
backend app2
  http-request set-header Host example-files.online-convert.com
  server server2 example-files.online-convert.com:443 ssl verify none

# this fails: https://10.10.1.111/
# cert uses wildcard
backend app3
  http-request set-header Host osmosisarchive-rpc.quickapi.com
  server server3 osmosisarchive-rpc.quickapi.com:443 ssl verify none

# this fails: https://10.10.1.111/
# cert uses wildcard
backend app4
  http-request set-header Host osmosis-rpc.polkachu.com
  server server4 osmosis-rpc.polkachu.com:443 ssl verify none

# this fails: https://10.10.1.111/sample.txt
# cert uses wildcard
backend app5
  http-request set-header Host txt2html.sourceforge.net
  server server5 txt2html.sourceforge.net:443 ssl verify none

# this works: https://10.10.1.111/
# cert is not using wildcard
backend app6
  http-request set-header Host thetestdata.com
  server server6 thetestdata.com:443 ssl verify none

You almost certainly need to send SNI to some of those servers, so they actually respond.

You do this by adding sni str(example.org) :

backend app5
 http-request set-header Host txt2html.sourceforge.net
 server server5 txt2html.sourceforge.net:443 ssl sni str(txt2html.sourceforge.net) verify none

Wow that worked! Thank you! I’m not familiar with sni but will read up some more on it and see why it’s needed by some servers. I’m guessing it’s a security feature

It’s basically sending the hostname your are trying to connect in the first SSL message (client_hello), so that the server knows which certificate to send you, when it has multiple.

@lukastribus That makes sense. Thank you! Would you happen to know how to enable health checks with SSL? I tried different combinations but can’t get it to work. with “verify none” my page works but not checking health. But if I do “verify required” then it says I’m missing the cert. But I already specified the pem file in the frontend. Any suggestions?

backend app1
  balance roundrobin
  http-check expect status 200
  option ssl-hello-chk
  http-request set-header Host defier.net
  server server1 defier.net:443 ssl verify none sni str(defier.net) verifyhost defier.net check-sni defier.net

Don’t use option ssl-hello-chk, that’s an old options that just mimics are SSLv3 client hello, this is not gonna work.

Just add the check keyword also with specifying the sni with check-sni

Now if you want to verify the server certificate (verify required), than you need to specify not the certificate but the certificate authority root file.

As per verify:

The certificate provided by the server is verified using CAs from ‘ca-file’ and optional CRLs from ‘crl-file’ after having checked that the names provided in the certificate’s subject and subjectAlternateNames attributes match either the name passed using the “sni” directive, or if not provided, the static host name passed using the “verifyhost” directive.

So you need to configure the ca root files, for example by refering to the system-ca as per ca-file

@lukastribus
I don’t want to verify the cert since I trust it. So I’ll keep “verify none”.

I tried adding check keyword with check-sni but it doesn’t seem to do anything. I added “check port 443” and that seems to verify port 443 is open which is good. But one last thing I can’t figure out is how to get it to check for status 200 for health check. I have “http-check expect status 200” but it seems ignored. But if I add “option httpchk” then it always fails. So not sure what to do from here to get it to verify 200 response over SSL backend site. I feel like I’m really close and it’s something simple but I haven’t found one that works. See below for what I have so far. The port 443 check works but not the “http-check expect status 200” doesn’t work.

  balance roundrobin
  http-check expect status 200
  http-request set-header Host defier.net
  server defier.net defier.net:443 ssl verify none sni str(defier.net) check port 443 check-sni defier.net

You need need “port 443” when the configuration already points to 443.

I think you are only missing a option httpchk in the backend.