Old Internet Proverb:
It’s not DNS
It can’t be DNS
It was DNS
A recent project uses hostname based routing and generates hostnames on a supplied domain.
In production this is not too tricky (assuming you know how to configure your chosen DNS server), you just set up a wild card DNS entry to point to the host running the ingress reverse proxy.
For development things are a little trickier, especially if you are a bit of a road warrior/digital nomad wandering round place to place connecting your laptop to lots of different WiFi networks.
For a single hostname you can usually just stick an entry in your OS’s
/etc/hosts file that will map the name to a given IP address, but you can’t do that with a whole domain. Also the project I’m working on is container based so we also have to be careful not to use
127.0.0.1 as the address for the ingress host, because that will resolve to the container’s local IP stack, rather than the loop back interface of the host machine.
The solution is to setup a DNS server that will respond with a single IP address for any hosts in the given domain. Luckily there is a package that can do this for use called
You can use the
address=/domain.name/ip.add.re.s configuration option to map as many domains as you would like to single IP addresses (as well as adding individual hostnames as normal).
For the example setups listed I’m going to use the domain
example.com but it will work for anything.
On Linux I’m going to use the IP address
172.17.0.1 because this is the default address bound to the
docker0 bridge interface. This is one of the addresses that ports mapped to containers get forwarded from, so will work for accessing the ingress proxy.
Modern versions of Ubuntu use something called
systemd-resolved which manages which DNS server to use (depending on how many interfaces you have connected to how many different networks), it also runs a caching system to try and reduce the number of lookups get done.
While this is very useful it does make doing what we want to do here a little trickier, but after a lot of messing around and testing the following works on Ubuntu 22.04 and I’m pretty sure will work on 20.04 as well.
sudo apt-get install dnsmasq sudo echo "bind-interfaces" >> /etc/dnsmasq.conf sudo echo "no-resolv" >> /etc/dnsmasq.conf sudo echo "conf-dir=/etc/dnsmasq.d" >> /etc/dnsmasq.conf sudo echo "address=/example.com/172.17.0.1" > /etc/dnsmasq.d/02-flowforge.conf sudo service dnsmasq restart sudo echo "DNS=127.0.0.1" >> /etc/systemd/resolved.conf sudo echo "DOMAINS=~example.com" >> /etc/systemd/resolved.conf sudo service systemd-resolved restart
This does the following steps:
- Installs dnsmasq
- Configures dnsmasq to not try and use the system DNS information to forward requests
- To look in
/etc/dnsmasq.dfor extra config files
- Adds a config file with the mapping of
systemd-resolvedto send all requests for
example.comto dnsmasq listening in
MacOS can work basically the same way as I’ve just described for Ubuntu. The only differences are that dnsmasq gets installed via HomeBrew, and we need to assign a phantom IP address to the loopback adapter because Docker on MacOS doesn’t have the same
docker0 bridge interface we can use.
sudo ifconfig lo0 alias 10.128.0.1 brew install dnsmasq echo "conf-dir=/opt/homebrew/etc/dnsmasq.d" >> /opt/homebrew/etc/dnsmasq.conf echo "address=/example.com/10.128.0.1" > /opt/homebrew/etc/dnsmasq.d/ff.conf sudo brew services start dnsmasq dscacheutil -flushcache sudo mkdir -p /etc/resolver sudo tee /etc/resolver/example.com > /dev/null <<EOF nameserver 127.0.0.1 domain example.com search_order 1 EOF
These commands do the following:
10.128.0.1as an address bound to the
- Install dnsmasq
- Tell dnsmasq to look in the
/opt/homebrew/etc/dnsmasq.ddirectory for extra config files
- Add a mapping from
example.comto IP address
- Set dnsmasq to run as a service
- Tell the MacOS name system to send all queries for
example.comto dnsmasq running on