Improving my DynDNS setup

Published on: 2024-01-29

I run quite a few services for friends and family. At some point, I will write a blog post about this, but today I want to write about a problem I have been having and how I solved it.

Within the last few months, I had spontaneous hiccups within my infrastructure with regards to DNS lookups. Every now and then, at different points in time, DNS lookups for certain subdomains where different services are hosted on, timed out or only resolved with considerable delay.

I tried to reproduce this, which didn't work well, as the problem only reared its head now and then. I setup a cronjob to be run every hour, and logged the output. That worked, and led to this:

dig a nxsubdomain.lpcvoid.com
; <<>> DiG 9.18.19-1~deb12u1-Debian <<>> a nxsubdomain.lpcvoid.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 54406
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; EDE: 22 (No Reachable Authority): (at delegation duckdns.org.)
; EDE: 23 (Network Error): (3.97.58.28:53 timed out for lpcvoid.duckdns.org A)
;; QUESTION SECTION:
;nxsubdomain.lpcvoid.com.		IN	A
;; Query time: 4160 msec
;; SERVER: 192.168.1.1#53(192.168.1.1) (UDP)
;; WHEN: Sat Jan 27 22:01:27 CET 2024
;; MSG SIZE  rcvd: 132

Turns out, DuckDNS, the dyndns provider I use, was the root of these timeouts. Every now and then, it would time out randomly. Now, this isn't any attack on them, DuckDNS is a one-player show offering a service for free to everybody. I am incredibly grateful for their continued work, and these sort of problems are normal once you become as widely used as they are.

But I cannot live with this, since I use domains everywhere, even within my LAN. I could of course configure my DNS server to cache more aggressively, or use internal LAN addresses when the request comes from within the LAN (compared to my WAN IP when requests arrive via the WAN). But this would also entail maintaining a "split horizon" DNS. I would like to avoid that, and use my WAN IP internally within my LAN. This is called reflection in the NAT world. I really feel like I am approaching the point in time where an IPv6 setup would reduce my LAN complexity by a considerable amount. Some day...

In any case, I needed an alternative. There's an elegant solution to this problem, which is technically superior to a dedicated dyndns provider.

I already use Cloudflare for my domains DNS setup. Like all other DNS management platforms, Cloudflare allows you to configure various DNS zones which point somewhere. In the past, I had a DuckDNS subdomain (lpcvoid.duckdns.org) point to my WAN IP, and CNAME'ed a wildcard zone rule to point to the DuckDNS domain for the actual address resolution. This worked, but is inefficient: You need to first lookup the domain you're interested in (let's say blabla.lpcvoid.com). This info is returned by Cloudflare, and is pretty fast. Then, your browser will lookup lpcvoid.duckdns.org (which blabla.lpcvoid.com points to via the CNAME entry, remember) and that will in turn result in my WAN IP. So you have two lookups in total, and one of those (the DuckDNS one in my case) is potentially slow.

Ideally, Cloudflare would always know my WAN IP, and update DNS A records whenever my WAN IP changes. This would completely remove the DynDNS provider from the equation, and result in very fast, single lookups which Cloudflare can respond to instantly.

Thankfully, OpenWRT, which runs on my router, maintains a script which can do exactly this automatically as part of the ddns-scripts-cloudflare package. For this to work, you need a Cloudflare API key (which you can generate here). Generate a token which can only change DNS zones - you should in general be quite restrictive with Cloudflare API tokens, since they are a pretty high value target for attackers. Afterwards, use the token and the username "Bearer" (yes, capital "B"), along with the record you wish to update on WAN IP change. So if your subdomain is foobar.example.com, and the record is thus called "foobar", you configure your "domain" in the script as "foobar@example.com" in order for Cloudflare to resolve it properly.

After doing this, you should have an A record which points to your WAN IP. Now, if you're lazy like me, and have dozens of subdomains, you obviously want to have a wildcard record. The trick is to have your wildcard CNAME record (*) point to the A record you just configured. This lookup, by the way, will be handled internally via Cloudflare's resolvers, and not result in two DNS queries.

In the end, you should have something like this:

dig a nxsubdomain.lpcvoid.com
; <<>> DiG 9.18.19-1~deb12u1-Debian <<>> a nxsubdomain.lpcvoid.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 49990
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;nxsubdomain.lpcvoid.com.	IN	A
;; ANSWER SECTION:
nxsubdomain.lpcvoid.com. 600	IN	CNAME	homeserver.lpcvoid.com.
homeserver.lpcvoid.com.	600	IN	A	79.235.213.105
;; Query time: 20 msec
;; SERVER: 192.168.1.1#53(192.168.1.1) (UDP)
;; WHEN: Mon Jan 29 14:55:48 CET 2024
;; MSG SIZE  rcvd: 93

Beautiful, problem solved. DNS lookups are now snappy.

In my next post I guess I will write about my complete setup.

Site generated on 2024-01-29 14:42:43. Changelog on Github.
Subscribe with RSS: https://lpcvoid.com/rss