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 dnsmasq
.
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.
Ubuntu
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.d
for extra config files - Adds a config file with the mapping of
example.com
to172.17.0.1
- Tells
systemd-resolved
to send all requests forexample.com
to dnsmasq listening in127.0.0.1
MacOS
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:
- Set
10.128.0.1
as an address bound to thelo0
loopback interface - Install dnsmasq
- Tell dnsmasq to look in the
/opt/homebrew/etc/dnsmasq.d
directory for extra config files - Add a mapping from
example.com
to IP address10.128.0.1
- Set dnsmasq to run as a service
- Tell the MacOS name system to send all queries for
example.com
to dnsmasq running on127.0.0.1
Other options
There is another option which is good in a pinch for testing these sort of set ups.
The domain sslip.io
has been setup to reply with the IP address found in the hostname. e.g.
127.0.0.1.sslip.io
resolves to127.0.0.1
www.127-0-0-1.sslip.io
resolves to 127.0.0.1www.--1.sslip.io
resolves to::1
fe80--9a6e-9aca-57cc-eea3.sslip.io
resolves tofe80::9a6e:9aca:57cc:eea3
You can find more details about sslip.io
here and it includes a link to the source code you you can build and run your own if needed.