Routing wildcard domains to WSL2 with Technitium DNS

John Rix
5 min readFeb 17, 2024
DALL-E 3’s interpretation of the article title. Aside from that left leg, it’s surprisingly… not mangled?

Long before Microsoft’s remarkable embrace of Linux in the form of WSL, VirtualBox was a foundational tool in my web dev workflow. Like countless others, my sites are hosted on Ubuntu servers in production, so it makes perfect sense to be developing in that environment.

VirtualBox served me well over the years, but like any tool, it has its foibles and annoyances so when Microsoft announced WSL (and then made it work better with the release of WSL2), I was very interested. Inertia and lack of time to learn it properly and migrate my toolchain over has meant I never got there though… until 2024.

The more recent introduction of Windows Terminal also added some additional appeal that also helped me over the line. Like VirtualBox, I have been using PuTTY+SuperPuTTY longer than I remember. Although SuperPuTTY has its bugs, I was not specifically looking to drop these from my toolkit — PuTTY is about the most useful dev/IT tool on the planet — but Windows Terminal works very well for me now and integrates nicely with WSL.

Now where was I again?

Anyway, all of that is just fluff to couch what this article is actually intended to be about.

One of my key requirements for my dev environment is the ability to use wildcard DNS records to route arbitrary subdomains to my development web server. Consequently, the windows hosts file just doesn’t cut the mustard. Previously with VirtualBox, I was using BIND on the Ubuntu dev server for this purpose, where it hosted my dev DNS zone and forwarded all other lookup requests to external DNS resolvers. I configured Windows networking to use the dev server as my primary DNS resolver accordingly.

This was not quite so practical with WSL though (at least initially while I was still figuring out its networking and more recent introduction of systemd support), so I ended up opting for Technitium DNS on Windows instead.

Installing Technitium was straightforward and I followed my nose to the Zones tab in Settings, where I created my test domain and wildcard ‘A’ record accordingly. Now we get to the meat of the matter though since I was not able to use 127.0.0.1 as the IP address for the ‘A’ record — this won’t route traffic into the WSL instance. Instead, I needed to use the IP address assigned by WSL for internal communications between Windows and the Ubuntu guest. One ‘ip address’ command later and I was away in a hack as they say here in Ireland (look it up)…

So what’s the problem?

That is… until I next rebooted, and discovered that said IP address is dynamically assigned and changes after a reboot. Well that sucks.

For a couple of reboots, I just put up with it and manually updated my DNS config each time, but I knew from the outset I wasn’t going to be able to put up with that for long. I consulted with the Goog about statically assigning IP addresses for the internal Windows/WSL communications and got various SO and similar results on the topic, but none with an obvious silver bullet.

In the end, I settled on using Technitium’s REST API to dynamically update the DNS Zone when WSL comes online. The bash script below does just that (I named it updateDNSZone.sh). Replace or parameterise the credentials and domain name below, then place it somewhere suitable within your WSL distro (eg. your home directory or wherever you like to keep your utility scripts) and add to the end of your .bashrc or other favoured login script within your Linux environment.

#!/bin/bash
#
# This script is intended to be run from within a WSL instance upon start-up.
# It will update a DNS A record in the Technitium DNS Service running on the Windows host
# to point to the newly assigned ephemeral IP address of the WSL instance.
# This enables routing of web traffic for the indicated domain to the WSL instance
# without the need of a statically defined IP address on either side.
#
# The domain name in question can be a wildcard domain if desired.
#
# For Technitium API details, see https://github.com/TechnitiumSoftware/DnsServer/blob/master/APIDOCS.md
#
# Note that this script currently uses a username and password, though it is also possible
# to generate a static API access token using the createToken API request
#

TNMPORT=5380
TNMUSER=admin
TNMPASS=mypassword
DOMAIN=*.mydomain.dev

# Get the IP address of the Windows host so that we can reach the Technitium server
windowshost=$(ip route | grep default | awk '{ print $3 }')
apibaseurl="http://$windowshost:$TNMPORT/api"

# Log in to generate a temporary access token
loginurl="$apibaseurl/user/login?user=$TNMUSER&pass=$TNMPASS"
logintoken=$(curl -s "$loginurl" | jq -r '.token')

# Get the existing DNS A record to find the old IP address
getrecordsurl="$apibaseurl/zones/records/get?token=$logintoken&domain=$DOMAIN"
oldip=$(curl -s "$getrecordsurl" | jq -r '.response.records[0].rData.ipAddress')

# Determine the new IP address from the local sytem
newip=$(ip addr show $(ip route | awk '/default/ { print $5 }') | grep "inet" | head -n 1 | awk '/inet/ {print $2}' | cut -d'/' -f1)

# Update the DNS A record
echo "Remapping $oldip -> $newip"
updateurl="$apibaseurl/zones/records/update?token=$logintoken&domain=$DOMAIN&type=A&value=$oldip&newValue=$newip"
curl -s "$updateurl" > /dev/null

Note that the above is dependent upon the ‘jq’ command for JSON parsing, which you may need to install via:

sudo apt install jq

But wait — there’s more!

Aside from the dynamic IP addresses being assigned, the other problem I encountered between reboots was that WSL2 currently fails to start after a reboot when Technitium DNS is installed — see here. So now we have a chicken/egg problem — we can’t start WSL while Technitium is running, but we can’t update Technitium on starting WSL if Technitium has to be shutdown for the purpose. ^%”^&%^$(!

I spent some time working out a solution to this involving a Windows batch script that needed to be Run as Administrator after a reboot, but then subsequently discovered a simple solution — in the General tab of the Technitium Settings, replace 0.0.0.0:53 with 127.0.0.1:53 in the DNS Server Local Endpoints field.

That’s all folks.

--

--