is it possible to use conditional rules/routing based on which certificate was actually used in this connection?
Let’s say I have a bunch of certificates, each certificate with more than one subjectAltName (e.g. one certificate for example.org and www.example.org, and another one for example.com and examp.le). I don’t want to repeat all these alt names in haproxy config once more in the following style:
crt-store
load crt "example.com-key-cert.pem" ocsp-update on alias "example_com"
load crt "example.org-key-cert.pem" ocsp-update on alias "example_org"
frontend fe
bind *:443 ssl default_crt "@/example_com" crt "@/example_org"
use_backend example_org if { req.ssl_sni -i "example.org" }
use_backend example_org if { req.ssl_sni -i "www.example.org" }
use_backend example_com if { req.ssl_sni -i "example.com" }
use_backend example_com if { req.ssl_sni -i "examp.le" }
Is it possible to do this with only two use_backend statements which would query the certificate alias (@/...) from the crt-store section instead of every subjectAltName of all certificates?
In the above example, I used ssl_fc_sni instead of req.ssl_sni, which probably works only in mode tcp and not mode http.
ok, ssl_f_s_dn is close to what I am looking for, thanks. I did not know about CommonName being deprecated, though. Interesting.
the Host: header can work, but it still requires repeating all the host names in the configuration (or to use a map). Does the hdr(host) directive work even for HTTP/1.0 requests with full URL (i.e. GET https://examp.le/file.txt HTTP/1.0 without the Host: header)?
as for the Don’t Repeat Yourself principle – it seems that haproxy can use all certificates from a given directory using the bind ... crt that_directory/ directive. Is it possible to use all certificates from the crt-store section the same way? I tried things like crt "@/*", but this does not work, and I did not find something like that in the docs.
It works in HTTP mode too, but it is very wrong to use it, because it makes you think you are accessing the domain that is in the browser address bar, when this is not really the case. That is why you should always use the Host header instead. The documentation explains why: if you certificate matches more than one domain (via SAN or wildcards), you just get the domain name that was used during the TLS handshake, and not what the actual request is for.
The -m dom domain match should make sure that a request against www3.example.org matches a pattern of example.org. Isn’t that what you wanted?
If you just want one use_backend rule in your configuration, then you can use dynamic backend selection accessing whatever strings you like, rewriting them before matching, etc. You can even use LUA scripting.
For example to remove www:
use_backend %[hdr(host),lower,regsub('www.', '')]
If a client doesn’t send a Host header, anything based on the Host header will not work, but the same goes for SNI: if the client doesn’t send SNI, you cannot content switch based on it.
A HTTP/1.0 client likely doesn’t send SNI, so either way you cannot use any content switching rules.
Thanks for the configuration hints, I will definitely use this somewhere. But in this particular case, consider the example.com and examp.le pair from the first post, which are not in a domain-subdomain relation. Yes, I have certificates/hostname sets which I want to handle in the same way, but which do not have a similar domain name. This is why I wanted to do the configuration per certificate subjectAltName list.
So, it is at least possible to use bind for all the certificates in crt-store somehow?
I have a directory for certificates, but there are many more unrelated certificates (used by other Haproxy instances, for example).
My current solution is to generate this part of the Haproxy config (crt-store, bind directive, and crt/host/whatever to backend mapping) using ansible/jinja2 template. So I am OK now, I just wonder whether there is more simple or shorter solution.
The use-case is that I have some static certs, and then I have a directory of my customers certificates which I want to go to a specific backend.
It feels like Haproxy should know about the SANs in the parsed crt-store already, or know which crt-store was used for a request, and that I should be able to use a rule for routing.
Someone recommended me to use a list of domains like so:
acl host_dynamic_cert hdr(host) -f /usr/local/etc/haproxy/dynamic-certs/domain-list.txt
use_backend be_dynamic_customer if host_dynamic_cert
Which is also doable, but it could be one less moving part to worry about