Using dnsmasq to run a local TLD

If you work on a lot of different projects you likely have a lot of different entries in your hosts file to point fake domains to localhost or one or more Vagrant boxes. This can be a bit of a pain to manage. If you’re running Linux, BSD or Mac OS then this article offers an option that might ease this problem a bit: dnsmasq.

In this article I will describe how to setup dnsmasq to run your own local (fake) top level domain for which all domains will route to localhost (or another IP address of choice, to point to a Vagrant box for example). While writing this article I used Ubuntu 20.04, if you are running another operating system that can run dnsmasq you may have to do things slightly differently… in that case: be ready to consult some manuals.

Dnsmasq describes itself as a small caching DNS proxy and DHCP/TFTP server. In this article I am focussing only on using its DNS functionality to fake a top level domain for development purposes, its other functions will be disabled.

Installation

To use dnsmasq for the purpose described in this article you will need to install resolvconf alongside it (at least on Ubuntu).

➜  sudo apt install -y resolvconf dnsmasq

After installation dnsmasq will try to start but fail. This happens because port 53 (which dnsmasq wants to use) is already taken by systemd-resolved:

Job for dnsmasq.service failed because the control process exited with error code.
See "systemctl status dnsmasq.service" and "journalctl -xe" for details.
invoke-rc.d: initscript dnsmasq, action "start" failed.
● dnsmasq.service - dnsmasq - A lightweight DHCP and caching DNS server
     Loaded: loaded (/lib/systemd/system/dnsmasq.service; enabled; vendor preset: enabled)
     Active: failed (Result: exit-code) since Thu 2020-09-24 22:45:18 CEST; 3ms ago
    Process: 5668 ExecStartPre=/usr/sbin/dnsmasq --test (code=exited, status=0/SUCCESS)
    Process: 5669 ExecStart=/etc/init.d/dnsmasq systemd-exec (code=exited, status=2)

Sep 24 22:45:18 nomac systemd[1]: Starting dnsmasq - A lightweight DHCP and caching DNS server...
Sep 24 22:45:18 nomac dnsmasq[5668]: dnsmasq: syntax check OK.
Sep 24 22:45:18 nomac dnsmasq[5669]: dnsmasq: failed to create listening socket for port 53: Address already in use
Sep 24 22:45:18 nomac dnsmasq[5669]: failed to create listening socket for port 53: Address already in use
Sep 24 22:45:18 nomac systemd[1]: dnsmasq.service: Control process exited, code=exited, status=2/INVALIDARGUMENT
Sep 24 22:45:18 nomac dnsmasq[5669]: FAILED to start up
Sep 24 22:45:18 nomac systemd[1]: dnsmasq.service: Failed with result 'exit-code'.
Sep 24 22:45:18 nomac systemd[1]: Failed to start dnsmasq - A lightweight DHCP and caching DNS server.

To be able to use dnsmasq the configuration file, /etc/dnsmasq.conf, needs to be modified. A good starting point is to overwrite it (you can of course make a backup of the original in case you want to rollback to it or explore the other available settings) with the following :

# The following two options make you a better netizen, since they
# tell dnsmasq to filter out queries which the public DNS cannot
# answer, and which load the servers (especially the root servers)
# unnecessarily. If you have a dial-on-demand link they also stop
# these requests from bringing up the link unnecessarily.
domain-needed
bogus-priv

# If you want dnsmasq to listen for DHCP and DNS requests only on
# specified interfaces (and the loopback) give the name of the
# interface (eg eth0) here.
# Repeat the line for more than one interface.
interface=lo
# If you want dnsmasq to provide only DNS service on an interface,
# configure it as shown above, and then use the following line to
# disable DHCP and TFTP on it.
no-dhcp-interface=lo
# On systems which support it, dnsmasq binds the wildcard address,
# even when it is listening on only some interfaces. It then discards
# requests that it shouldn't reply to. This has the advantage of
# working even when interfaces come and go and change address. If you
# want dnsmasq to really bind only the interfaces it is listening on,
# uncomment this option. About the only time you may need this is when
# running another nameserver on the same machine.
bind-interfaces

With the settings above dnsmasq only provides DNS (disabling its other features) on the loopback interface (localhost) and will co-exist with systemd-resolved. After having updated the dnsmasq configuration you can finally start it with the command sudo systemctl start dnsmasq. If you then perform DNS queries they will go through dnsmasq which will in turn use the DNS servers you would use normally.

Configuration

Now that you have dnsmasq up and running you can add your own exceptions in the configuration file. Let’s configure dnsmasq to route everything of the fictive .test TLD to localhost by adding the following lines to the end of /etc/dnsmasq.conf

# Domain mappings overriding regular DNS
address=/test/127.0.0.1

To enable these changes you will need to restart dnsmasq using the command sudo systemctl restart dnsmasq. After doing you can test this out by pinging hostnames ending in .test or by using the dig command:

cor@nomac in ~ at 22:48:11 CEST 
➜  ping hello.test
PING hello.test (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.037 ms
^C
--- hello.test ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.037/0.037/0.037/0.000 ms
cor@nomac in ~ at 22:48:16 CEST 
➜  dig www.example.test +short
127.0.0.1

If you are getting unexpected results you should take a look in your system’s hosts file, entries there take precedence over DNS servers (which includes dnsmasq).

If desired you can add overrides above the TLD by adding another line below the just added address line:

address=/another.test/127.0.1.1

After restarting dnsmasq again you can see this effect by running dig against different hostnames:

cor@nomac in ~ at 22:49:18 CEST 
➜  dig hello.test +short
127.0.0.1
cor@nomac in ~ at 22:49:33 CEST 
➜  dig another.test +short
127.0.1.1
cor@nomac in ~ at 22:49:39 CEST 
➜  dig and.another.test +short
127.0.1.1

As you can see above anything going in front of a mapped domain will point to that IP address, this allows you to use subdomains without having to map them all like you would have had to do in the system’s hosts file.

Just like in the hosts file nothing is stopping you from remapping existing hostnames, domains and even TLDs here (so be careful to not unintentionally make parts of the internet unavailable to yourself!). Also, as said at the start of this article: you can map to other IP addresses than localhost to for example point your fake TLD or domain to a Vagrant box that you use to develop multiple projects (such as Laravel Homestead).

In conclusion

And with that we have come to the end of this short article on using dnsmasq. If you want to learn more about dnsmasq and all that it can do you can check out its manual page here.

If you have feedback on this article or have questions based on its contents then feel free to reach out to me on Twitter or through e-mail.

2 Replies to “Using dnsmasq to run a local TLD”

Comments are closed.