Wildcard domains: Making exceptions with regular expressions
I am a big fan of wildcard subdomains; they make everything easier. And since Let’s Encrypt supports issuing wildcard certificates, it really is a no-brainer to just use them all the time.
But I ran into an issue with mailcow. Mailcow bundles everything you’d ever need to run a half-decent mailserver into docker images; one such image being ACME for Let’s Encrypt certificate generation and management. At the moment, its implementation does not support dns-01 validation and wanting to be as lazy as possible, I haven’t tried screwing with mailcow’s way of doing things very much by trying to insert my own self-managed certificates in. So, out of that laziness, I am stuck with http-01 validation.
Herein lies the issue: http-01 validation relies on a page at the domain being requested being available over HTTP containing the challenge response for Let’s Encrypt to validate domain ownership. Since I use wildcard domains and mailcow issues provides autodiscover and autoconfig support, this means a cert needs to be generated for autodiscover.davison.tech
and autoconfig.davison.tech
. Problem being, this is my nginx config for this site:
server {
listen 80;
listen [::]:80;
server_name davison.tech *.davison.tech;
return 301 https://$server_name$request_uri; # Enforce redirection to https
}
server {
include defaults/defaults-ssl.conf; # Contains defaults for SSL support (cipher suites, HSTS etc)
server_name davison.tech *.davison.tech;
location / {
[...]
And for my mailcow site:
server {
listen 80;
listen [::]:80;
server_name mail.warhaggis.com autodiscover.* autoconfig.*;
return 301 https://$server_name$request_uri; # Enforce redirection to https
}
server {
include defaults/defaults-ssl.conf;
server_name mail.warhaggis.com autodiscover.* autoconfig.*; # Contains defaults for SSL support (cipher suites, HSTS etc)
[...]
There’s two different uses of wildcards here: *.davison.tech
enables anything - even a keyboard mash - appended before .davison.tech
to redirect and work (couple with the wildcard certificate this also means valid HTTPS on any of those “sites”), but mailcow’s nginx config does the same thing for auto{discover,config}.*
meaning that any domain (pointing to this server) with an auto|discover
A/AAA record will direct the browser to the mailcow site. There is thus a conflict and ACME fails to get the certificate issued due to the specific domain names pointing to this blog.
Solution? Well it turns out, nginx can support regular expressions in the server_name context and we can now transform our wildcard into “anything, except …” and so this turns our nginx config into:
server {
listen 80;
listen [::]:80;
server_name davison.tech "~^(?=.*?\bdavison\.tech\b)((?!autodiscover|autoconfig).)*$";
return 301 https://$server_name$request_uri; # Enforce redirection to https
}
server {
include defaults/defaults-ssl.conf; # Contains defaults for SSL support (cipher suites, HSTS etc)
server_name davison.tech "~^(?=.*?\bdavison\.tech\b)((?!autodiscover|autoconfig).)*$";
location / {
[...]
Now, I’m no wizard at writing regular expressions, so I ran the following search and found that sweet perfect Stack Overflow result that contains exactly what you want - and just like any good answer, it breaks down exactly why this works.
I have no shame using Stack Overflow and I don’t think anyone should, but allow me to pontificate briefly at how important it is to read the damn answer and understand it. Because then, you get to pull this trick out of your hat in the future.
Should we really be using regular expressions in our server_name
contexts? Probably not. There’s probably a few unnecessary CPU cycles generated there. But whatever, computers are neat and for the most part, I don’t think we should be afraid to practice the unconventional.