Setting up dnsmasq - a lightweight DHCP and DNS server
Fedora Server Edition recommends the lightweight dnsmasq program to provide DHCP, DDNS and DNS caching service for a server and a small to medium-sized local network. It works as a NetworkManager plugin to ensure a seamless interlocking of the components. It is the preconfigured default configuration and specifically supported.
A typical usage of dnsmasq is to provide a DHCP service for a private network. It is optionally supplemented by dynamic DNS, whereby a DHCP assigned IP address gets a temporary DNS entry with the hostname of the device. Additionally, it supports static hostnames, too. Another typical use case is to provide DHCP for a public subdomain, while an official public DNS server still provides the subdomain’s name resolution. Of course, devices with such an address cannot be found via DNS. They are primarily used for the initial system installation, for network-supported booting (PXE), or dynamically assigning machines, identified by their MAC address, a specific IP address. And sometimes dnsmasq is used as a caching DNS proxy without any DHCP or DDNS functionality. But since release 33, Fedora uses systemd-resolved as DNS client which includes a versatile caching. Thus, dnsmasq is no longer needed for this use case.
The dnsmasq DHCP and DNS is the default and recommend way to provide these services in Fedora Server Edition. Each of the components is optional. A system can use only the DHCP part without DNS, or only DNS without DHCP, or only DHCP caching, or any combination. Each component is configured separately. It is preconfigured as a NetworkManager plugin to ensure a seamless interlocking of the components.
The target is a small to middle-sized subnet. Usually, a server performs this task as a “side job” so to speak, and the main tasks involve other services.
A general determination of the upper limit is practically impossible. But as a rule of thumb, dnsmasq can easily handle 100 or more machines. Significantly larger networks primarily require better management and structuring capabilities. The ISC DHCP Server would then be a more suitable choice.
For additional information, see the Fedora Magazine article Using the NetworkManager’s DNSMasq plugin (2019).
The NetworkManager dnsmasq plugin included by default provides a basic configuration skeleton, but does not install the dnsmasq package. Thus, it avoids to uselessly occupy space and to introduce a superfluous and unused binary in case dnsmasq is not going to be in use on the particular server.
In case dnsmasq is not already installed
[…]# dnf install dnsmasq
Do not use systemctl directly on dnsmasq! It is used as a NetworkManager plugin, therefore NetworkManager starts and manages dnsmasq and adjusts
Calling systemctl directly would be ineffective and would rather start yet another dnsmasq instance, which leads to conflicts.
NetworkManager takes care of the dnsmasq plugin operation. Configuration files in the
/etc/NetworkManager/dnsmasq.d directory specify the custom configuration requirements, preferably one configuration file per task. The only exception in this example is the file containing the IP - hostname mapping of for static DNS names,
NetworkManager reads all files in that directory, independantly of the file extension. So you can’t temporarily deactivate a configuration by renameing it.
The example here uses 2 interfaces, an external public interface enp1s0 (example.com) and an internal private interface enp2s0 (example.lan). You may add any number of additional interfaces by adding corresponding config files as in the examples here.
Activate the dnsmasq NetworkManager plugin
[…]# vim /etc/NetworkManager/conf.d/00-use-dnsmasq.conf <i> # /etc/NetworkManager/conf.d/00-use-dnsmasq.conf # # This enabled the dnsmasq plugin. [main] dns=dnsmasq <esc><:wq>
Configuration of the name resolution (DNS) for the private network (example.lan)
[…]# vim /etc/NetworkManager/dnsmasq.d/01-DNS-example-lan.conf <i> # /etc/NetworkManager/dnsmasq.d/01-DNS-example-lan.conf # This file sets up DNS for the private local net domain example.lan local=/example.lan/ # file where to find the list of IP - hostname mapping addn-hosts=/etc/dnsmasq.hosts domain-needed bogus-priv # Automatically add <domain> to simple names in a hosts-file. Expand-hosts # interfaces to listen on interface=lo interface=enp2s0 # in case of a bridge don't use the attached server virtual ethernet interface # The below defines a Wildcard DNS Entry. #address=/.localnet/10.10.10.zzz # Upstream public net DNS server (max.three) no-poll server=134.102.xx.yy server=134.102.uu.vv server=2001:638:xxx:yyy::zz <esc><:wq>
Configuration of the DHCP service for the private network (example.lan)
[…]# vim /etc/NetworkManager/dnsmasq.d/02-DHCP-example-lan.conf # etc/NetworkManager/dnsmasq.d/02-DHCP-example-lan.conf # This file sets up DHCP for the private local net domain example.lan # The domain the DHCP part of dnsmasq is responsible for: domain=example.lan,10.10.10.0/24,local # interfaces to listen on interface=enp2s0 # general DHCP stuff (options, see RFC 2132) # 1: subnet masq # 3: default router # 6: DNS server # 12: hostname # 15: DNS domain (unneeded with option 'domain') # 28: broadcast address dhcp-authoritative dhcp-option=1,255.255.255.0 dhcp-option=3,10.10.10.10 dhcp-option=6,10.10.10.1 # Assign fixed IP addresses based on MAC address # dhcp-host=00:1a:64:ce:89:4a,NAME01,10.10.10.50,infinite # dhcp-host=52:54:00:42:6a:43,NAME02,10.10.10.51,infinite # Assign dynamically IP addresses to interface to listen on # Range for distributed addresses, tagged <int> for further references dhcp-range=tag:enp2s0,10.10.10.150,10.10.10.200,24h
Configuration of the DHCP service for the public network (example.com)
[…]# vim /etc/NetworkManager/dnsmasq.d/03-DHCP-example-com.conf # etc/NetworkManager/dnsmasq.d/03-DHCP-example-com.conf # This file sets up DNCP for the public example.com domain interface # The domain the DHCP part of dnsmasq is responsible for: domain=example.com,134.102.xx.yy/27 # interfaces to listen on interface=enp1s0 # general DHCP stuff (options, see RFC 2132) # 1: subnet masq # 3: default router # 6: DNS server # 12: hostname # 15: DNS domain (unneeded with option 'domain') # 28: broadcast address ##dhcp-authoritative ## we just send the bare minimum, e.g. no DNS server ##dhcp-option=1,255.255.255.224 dhcp-option=tag:enp1s0,option=router,220.127.116.11 # Assign fixed IP addresses based on MAC address # dhcp-host=00:1a:64:ce:89:4a,thootes,10.10.10.50,infinite # dhcp-host=52:54:00:42:6a:43,apollon,10.10.10.51,infinite # Assign dynamically IP addresses to interface to listen on # Range for distributed addresses, tagged <int> for further references dhcp-range=tag:enp1s0,18.104.22.168,22.214.171.124,1h
There is no DNS configuration for the external interface following, assuming that a official public DNS server is used to resolve all public facing interfaces of the domain example.com.
Adjusting the firewall
Allow ports for DHCP and DNS (53) service on the public interface.
[…]# firewall-cmd --get-services […]# firewall-cmd --zone=FedoraServer --permanent --add-service=dhcp […]# firewall-cmd --zone=FedoraServer --permanent --add-service=dns […]# firewall-cmd --reload […]# firewall-cmd --list-all
Restart NetworkManager to start dnsmasq
[…]# systemctl restart NetworkManager
NetworkManager adjusts now the nameserver entries in /etc/resolv. They are replaced by 127.0.0.1 and processed via dnsmasq.
Test the installation
The dnsmasp internal self test
[…]# dnsmasq --test
Test DHCP in the public using a machine without IP address
[…]# ip a # no IPv4 address associated with interface […]# dhclient -4 -1 -v eth0 […]# ip a # expect new IPv4 address associated with interface […]# dhclient -4 -1 -r -v eth0 # expected: no IPv4 again […]# ip a # expect no IPv4 address associated with interface again
Try on an other server
[…]# dig app1 @10.10.10.1 […]# nslookup app1 10.10.10.1 […]# dhclient -v -d -s 10.10.10.1 enp6s0
If machines in the private network need access to the public network, add masquerading / NAT to the firewall.
Enabling masquerading for the public zone and for the internal (trusted) trusted zone
[…]# firewall-cmd --zone=FedoraServer --add-masquerade --permanent success […]# firewall-cmd --zone=trusted --add-masquerade --permanent success […]# firewall-cmd --reload […]# firewall-cmd --zone=FedoraServer --query-masquerade yes […]# firewall-cmd --zone=trusted --query-masquerade yes
Allowing forwarding from the internal, private network to the external interface and further to the public network.
A commonly used way to accomplish this is to set 'rules' in the firewall configuration. Corresponding tutorials are very widespread. And those who are familiar with it may want to continue using it.
[…]# firewall-cmd --get-active-zones FedoraServer interfaces: enp1s0 trusted interfaces: vbr2s0 enp2s0 […]# firewall-cmd --direct --add-rule ipv4 nat POSTROUTING 0 -o enp1s0 -j MASQUERADE success […]# firewall-cmd --direct --add-rule ipv4 filter FORWARD 0 -i vbr2s0 -o enp2s0 -j ACCEPT success […]# firewall-cmd --direct --add-rule ipv4 filter FORWARD 0 -i enp1s0 -o vbr2s0 -m state --state RELATED,ESTABLISHED -j ACCEPT success
Fedora’s firewall daemon, however, offers with release 35 and beyond a more elegant option, so-called 'policies'. These abstract typical targets previously configured by rules.
[…]# firewall-cmd --get-active-zones FedoraServer interfaces: enp1s0 trusted interfaces: vbr2s0 enp2s0 […]# firewall-cmd --permanent --new-policy trustedToExt success […]# firewall-cmd --permanent --policy trustedToExt --add-ingress-zone trusted success […]# firewall-cmd --permanent --policy trustedToExt --add-egress-zone FedoraServer success […]# firewall-cmd --permanent --policy trustedToExt --set-target ACCEPT success […]# firewall-cmd --reload success
This method is much clearer, improves maintainability and reduces sources of potential errors. The documentation of the upstream project provides more information.
In case libvirt and virualization including a virtual network for the virtual machines, libvirt installs and configures its own dnsmasq instance. In most cases it is just convenient, instead of replacing the libvirt default network to integrate it in NetworkManagers dnsmasq plugin. Thus, two instances of dnsmasq operate along each other.
To make it work, just add onother configuration file. The example uses libvirt.lan as the libvirt virtual network domain name. Adjust as appropriate.
We just add the name resolution (DNS) for the libvirt virtual network (libvirt.lan), leaving the DHCP functionality untouched.
[…]# vim /etc/NetworkManager/dnsmasq.d/20-DNS-libvirt-lan.conf <i> # /etc/NetworkManager/dnsmasq.d/20-DNS-libvirt-lan.conf # This file directs dnsmasq to forward any request to resolve # names under the .libvirt.lan domain to 192.168.122.1, the # local libvirt DNS server default address. server=/libvirt.lan/192.168.122.1 <esc><:><w><q>