Support for different TLS verify modes based on SNI

Hello,

We have an unorthodox use-case in which we want to enforce different TLS client auth (verify) rules depending on the incoming SNI server name. Basically, if the received SNI name is e.g. abc, we want to have client auth required (i.e. require client certificates during the TLS handshake, and abort the handshake if client does not provide them), whereas if the received SNI name is e.g. def, we want to have client auth optional (i.e. ask for client certificates during TLS handshake, but allow the handshake to conclude even if client does not provide them).

This is a rare setup, but we used to employ that in our applications using custom OpenSSL callbacks.

Interestingly, it looks possible to achieve this same configuration using HAProxy’s verify option in the crt-list definition, e.g.:

bind :8443 ssl crt-list /etc/haproxy/crt-list.txt ca-file /etc/haproxy/dummy-ca.crt no-ca-names crt-ignore-err all

where the /etc/haproxy/crt-list.txt is defined as e.g.:

/etc/haproxy/certs/example.pem [alpn h2,http/1.1] *.example.com example.com verify optional no-ca-names crt-ignore-err all
/etc/haproxy/certs/internal.pem  [alpn h2,http/1.1] *.internal.com internal.com verify required no-ca-names crt-ignore-err all

In this example, client certificate would be optional for *.example.com but mandatory for *.internal.com.

Unfortunately, it looks like this does not work. Per testing, it looks like the verify option is always obtained from the bind statement, and all verify defined in the crt-list certificates are always ignored. This means that I can only define a single client auth option for the whole server, regardless of received SNI names.

I wanted to make sure that I am not missing something obvious here and this is really not supported (despite verify being a possible option for the crt-list according to the documentation).

Thank you in advance!
Felipe

The syntax is:

<crtfile> [\[<sslbindconf> ...\]] [[!]<snifilter> ...]

You put some options in the correct part (alpn) and the rest of your options in the snifilter itself.

Your config would have to look like this to be syntactically correct:

example.pem [alpn h2,http/1.1 verify optional no-ca-names crt-ignore-err all] *.example.com example.com
internal.pem  [alpn h2,http/1.1 verify required no-ca-names crt-ignore-err all] *.internal.com internal.com
1 Like

@lukastribus Thank you once again! With your configuration, it is now working perfectly!

The only change I had to do was moving the crt-ignore-err outside the brackets, otherwise HAProxy would reject the conf:

example.pem [alpn h2,http/1.1 verify optional no-ca-names] *.example.com example.com crt-ignore-err all
internal.pem  [alpn h2,http/1.1 verify required no-ca-names] *.internal.com internal.com crt-ignore-err all

Thank you, I need to get my syntax right in the future :slight_smile:

Ok, but the syntax is still wrong and it still doesn’t do want you think.

Again, you cannot put options in the snifilter, that is not the syntax.

You first crt-list line will match:

  • the wildcard domain example.com
  • the exact domain example.com
  • the exact domain crt-ignore-err
  • the exact domain all

The second crt-list line will match:

crt-ignore-err is not supported in a crt-list, that is why you get an error if your try it. It’s already on the bind line, you don’t need it in the crt-list.

Haproxy config checker cannot protect you from putting keywords into pattern fields.

1 Like

Got it, makes sense. I ended up moving the crt-ignore-err to the bind line as it was the same for all domains anyway.

Thank you again!

1 Like