I've been using Cloudflare for a while now and I'm quite happy with their free plan. However, one thing that I'm missing there is the fact that you can't create subdomains - at least I don't think there is one. Since I have a subscription to Azure, I would simply create new DNS zones there and point from Cloudflare to Azure for all of my subdomains - and it has been working quite well, actually. One thing that I'm not happy with though is the fact that it's quite a complex solution, especially for someone who's not really very well versed in cloud technologies. This also creates significant security risks if you allow access into your Azure account with improperly configured account for accessing
DNS Zones. I did follow some tutorials on how to do that, but I still don't feel very good about the whole deal - I'm not a cloud engineer after all.
So why did I even go through the hassle of getting things set up in Azure for my subdomains? Most of my subdomains have
A records for services that are not externally accessible in my internal DNS server, some of them actually have zero accessible services from outside, but I still like to use
ZeroSSL certificates with them so that my browsers are not complaining about self-signed certificates. Since probably all of my services are now behind
traefik, HTTP verification for my services is also not an option, at least I don't think it is - that would also mean that those services would have to have publicly resolvable DNS names, and I really, really don't want to do that for those that I only use internally.
So, how to resolve this conundrum? Well, here's where Dynamic Updates (RFC2136) comes in play. This system allows dynamic updates to DNS, which is really very handy with
_acme-challenge.servicename.sub.domain.xyz type of records. If you have a setup similar to mine, you will be happy to know that
traefik natively supports
RFC2136 as a certificate resolver provider and can create
TXT records for your services on the fly. What about safety, you may ask? Well, there are two ways of doing this - one way is to have a DNS server with
split horizon configured, which means that it will respond to queries differently based on the configuration - one set of replies when queries are coming from outside of your network, the other set of replies when queries are coming from an external network. This is a pretty good solution when you work in production environments and want to avoid complexities of multiple DNS server - however, we are talking about homelabs here, so why not make it a little bit simpler when it comes to configuration.
Also, I was recently hunting for a new DNS server - I've been using Windows DNS server for a while now, but I'm really not happy with it - not that it doesn't work, it's just that administering it is pretty cumbersome in my opinion and even though there is
WinAdmin that allows basic DNS server administration, it lacks any kind of advanced features in it. You can probably do stuff via
PowerShell, but I'm not a Windows guy and PS is really not my forte. Also, from what I know, it doesn't support
To my rescue - Technitium DNS Server! I'm really not sure how I've never heard about this one before, to be honest. It has absolutely everything I need - a
DHCP server (if it had
IPAM included in it, it would be the best open source product in DDI category - if not the only one that I know of) with a functional web interface that completely works for me. It supports advanced DNS protocols as well, so it was a no-brainer for me. I really can't say enough good words about this product.
So, how does this work for subdomains? First of all, let's take a look at the regular DNS query from a user on the Internet for an IP address of
srvc.sub.domain.xyz. Since this is an internally hosted service that will not be externally accessible, there will be no entry for it in the public DNS:
If you are running an internal DNS (as you should if you have multiple internal services), that looks something like this:
Why is it important to understand the difference between these queries and how they are working? Well, in most cases, you do not want to expose your internal DNS server to outside - mostly, if not exclusively, for security reasons. But if you run your DNS server internally only, how will
ZeroSSL confirm ownership of your (sub)domain? Well, this is why we need to have an external DNS server for our subdomain as well. This is where the simpler approach comes into play, but requires double the work when setting things initially.
Please keep in mind that DNS challenges that will come from
ZeroSSL will not actually look for specific
A records - they will look for
TXT records that your reverse proxy will dynamically create and remove once it's done. Hence, the query will be a little bit different in that case:
To start with this setup, you will need to have a publicly accessible server - any cloud provider will do - and install DNS server software on it. I really recommend going with Technitium, but you can choose whatever you want, as long as it supports
In my case, I'm using, as mentioned before,
traefik as my reverse proxy, so this will explain how to use its native support for
RFC2136 to dynamically update DNS entries in my external DNS server.
This is how the complete setup will work:
Before we start, a few warnings:
- patch your external DNS servers with all applicable security patches whenever they become available
- never ever create DNS entries for services/hosts that you don't want to have publicly accessible
- limit access to DNS server via through DNS requests only from Internet
- do not expose administrative access to the DNS server to the Internet
With this in mind, I would definitely recommend to limit access to the DNS server's administrative interface from your IP only or, better yet, set a VPN tunnel to it and access it from your own environment/homelab through the tunnel only.
In my environment, I actually have a server with Hetzner where all of my VMs are behind a firewall, so I created a VM within a
danger zone that has no access to the other zones, but is accessible from Internet via NAT rule on the firewall. So even if it gets compromised, it will not be able to get to any other zone hosted behind the firewall.
With that behind us, how do we configure things?
First thing to do, after we have our DNS server configured, is to create a
TSIG Key. To do that in Technitium DNS, choose
TSIG and click on
Give a name to the key and for algorithm choose
HMAC-SHA256 if it's not selected already. Leave the shared secret field blank as it will populate automatically once you save settings.
Next step is to go under
Zones and click on
Add Zone if you don't have one already:
Give it a name and set the type to
Primary Zone. I also like to use
Zone Serial option, but it is - optional.
Once you've selected your options, click on
Add and you will be taken to the zone configuration. Click on
Options and then click on
Under Zone Options, click on
Dynamic Updates (RFC2136) and select
Allow. You can select a different option if you want (or if you have a static IP address), but that is how I set it up.
Scroll down and under
Security Policy click on
Add to select the previously created
TSIG Key. Under the
Domain Name, I tried multiple different options, but the only one that actually worked for me was to allow wildcard for names under the subdomain. You may want to look into that further if you want to play it safe. Under
Allowed Record Types, I entered
TXT as I only want to allow
TXT records through
Finally, click on
Save and that should be it when it comes to allowing Dynamic Updates to your zone (subdomain). Now, as long as your reverse proxy can reach the external DNS server, it should be able to write
TXT records for
ACME challenges when requested to do so by
These are my settings in
traefik to enable
RFC2136 in my
certificate resolvers section.
- [email protected]
Additionally, you need to set environment variables:
These are pretty much self-explanatory variables - in my case, I named the
dns-challenge so I've entered that as a value for
RFC2136_TSIG_SECRET will be whatever Technitium DNS server generated for you,
RFC2136_TSIG_ALGORITHM should be set to
HMAC-SHA256 if you selected that as an option when creating the key and, finally,
RFC2136_NAMESERVER should be the IP address of your external DNS server that
traefik can reach.
traefik container and now, when creating new services, instruct them to use
certificate resolver (since that is the name of the resolver set in the configuration, you can actually name it whatever you want).
If you changed your existing
le certificate resolver, future services will use
RFC2136 as the provider for DNS challenges, and all existing services that already use
le will use
RFC2136 when renewing their certificates.
To test this out, create a new service under
traefik and observe in the Zone configuration on the DNS server creation of
TXT records while verifying ownership of the domain. Don't worry,
traefik will remove these records once the certificate has been issued.
And that's it, this is how you can set up your subdomain for DNS challenges by using
RFC2136 - this also works with domains, but you would then have to host your main domain on your DNS server as well and I prefer to have those in Cloudflare for a bunch of reasons (stability, DDoS protection, 'anonimity`, etc.).
One last time - please make sure that only services that you want to expose to the Internet have entries in your external DNS server!