Setting up dnsmasq - a lightweight DHCP, DNS, and TFTP server
The Fedora Server Edition recommends using the lightweight dnsmasq program to provide a server and a small to medium-sized local network with DHCP, DDNS and DNS caching services. Fedora Server has already preconfigured it as a NetworkManager plugin to ensure seamless integration of the components.
Introduction
By default, Fedora Server uses dnsmasq to provide local DNS and DHCP services for private or public subnets. It is preconfigured as a NetworkManager plug-in to ensure seamless integration of the components.
The DHCP component provides dynamic DNS for a DHCP-assigned IP address, offering a temporary DNS entry for the device’s hostname. It also supports static hostnames. Another common use case is to provide DHCP for a public subdomain, while an official public DNS server provides name resolution for the subdomain. Devices with such an address cannot, of course, be found via DNS. These addresses are primarily used for initial system installation, network-supported booting (PXE), and for dynamically assigning a specific IP address to machines identified by their MAC address. Another capability is providing a DNS caching service. However, since release 33, Fedora has used systemd-resolved as the DNS client, which includes versatile caching. Therefore, dnsmasq is no longer required for this purpose.
Dnsmasq can also provide a TFTP service that can be used to support PXE boot environments, most often used for remote system installation across a network.
Each dnsmasq component is optional. A system can use the DHCP component alone, the DNS component alone, the DHCP caching component alone, or any combination of these. Each component is configured separately.
The target is a small to medium-sized subnet. Typically, a server performs this task as an additional responsibility, alongside its main duties. It is practically impossible to determine the upper limit with any degree of accuracy. However, as a rule of thumb, dnsmasq can handle over 100 machines with ease. Significantly larger networks require better management and structuring capabilities. In this case, Kea, the ISC DHCP Server would be a more suitable choice.
For additional information, see the Fedora Magazine article Using the NetworkManager’s DNSMasq plugin (2019).
Prerequisites
All the necessary interfaces have been installed and fully configured. This includes assigning the correct firewall zones.
-
Check firewall zones
You should get something like
$ firewall-cmd --get-active-zones FedoraServer (default) interfaces: <IF01> ... <MY_ZONE> interfaces: <IF02> ...Fix the zone assignments if necessary
$ sudo firewall-cmd --permanent --zone=<zone_name> --change-interface=<interface_name> $ sudo firewall-cmd --reload -
Check auto forwarding
The system should automatically forward between the interfaces. Check the forwarding status and adjust it if necessary.
$ cat /proc/sys/net/ipv4/ip_forward $ cat /proc/sys/net/ipv6/conf/default/forwardingIn both cases a value of 1 indicates an active forwarding.
Otherwise, enable it immediately and configure it permanently.
$ echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward $ echo 1 | sudo tee /proc/sys/net/ipv6/conf/all/forwarding $ sudo tee /etc/sysctl.d/50-enable-forwarding.conf << 'EOF' # local customizations # # enable forwarding for dual stack net.ipv4.ip_forward=1 net.ipv6.conf.all.forwarding=1 EOF
Installation
The NetworkManager dnsmasq plugin, included by default, provides a basic configuration skeleton, but does not install the dnsmasq package. Thus, it avoids unnecessarily occupying space and introducing a superfluous, unused binary when dnsmasq is not needed on the server.
In case dnsmasq is not already installed
$ sudo 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. |
Basic configuration
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 static DNS names, /etc/dnsmasq.hosts.
|
NetworkManager reads all files in that directory, independently of the file extension. So you can’t temporarily deactivate a configuration by renaming it. |
The example here uses 2 interfaces, an external public interface enp1s0 (public.tld) and an internal private interface enp2s0 (internal.lan). You may add any number of additional interfaces by adding corresponding config files as in the examples here.
-
Activate the dnsmasq NetworkManager plugin
Create and edit the file at
/etc/NetworkManager/conf.d/00-use-dnsmasq.confusing your preferred editor running under sudo with root privileges. The content should be as follows:# /etc/NetworkManager/conf.d/00-use-dnsmasq.conf # This enables the dnsmasq plugin. [main] dns=dnsmasq -
Configuration of the name resolution (DNS) for the internal private network (internal.lan)
Create and edit the file at
/etc/NetworkManager/dnsmasq.d/01-DNS-<INTERNAL>.confusing your preferred editor running under sudo with root privileges. The content should be as follows:# /etc/NetworkManager/dnsmasq.d/01-DNS-<INTERNAL>.conf # This file sets up DNS for the private local net domain '<INTERNAL>.lan' local=/<INTERNAL>.lan/ # file where to find the list of IP - hostname mapping addn-hosts=/etc/dnsmasq-<INTERNAL>.hosts domain-needed bogus-priv # Automatically add <domain> to simple names in a hosts-file. expand-hosts # interfaces to listen on interface=lo interface=<ENPxyz> # in case of a bridge don't use the attached server virtual ethernet interface here! # Upstream public net DNS server (max.three) no-poll server=<uuu.vv.xx.yy> server=<www.vv.xx.zz> server=<2001:www:xxx:yyy::zz>Substitute values for <INTERNAL>, <ENPxyz> with a name for your internal private network, and the network interface connected to your internal private network.
Substitute the values for <uuu.vv.xx.yy>, <www.vv.xx.zz>, and <2001:www:xxx:yyy::zz> with the IP addresses of any upstream DNS servers.
Provide an empty host file
$ sudo touch /etc/dnsmasq-<INTERNAL>.hosts -
Configuration of the DHCP service for the internal private network (<INTERNAL>.lan)
Create and edit the file at
/etc/NetworkManager/dnsmasq.d/02-DHCP-<INTERNAL>.confusing your preferred editor running under sudo with root privileges. The content should be as follows:# etc/NetworkManager/dnsmasq.d/02-DHCP-<INTERNAL>.conf # This file sets up DHCP for the private local net domain '<INTERNAL>.lan' # The domain the DHCP part of dnsmasq is responsible for: domain=<INTERNAL>.lan,<uuu.vv.xx.y/24>,local # interfaces to listen on (redundant, same as for DNS) interface=<ENPxyz> # 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,<www.xxx.yy.zz> dhcp-option=6,<www.xx.yy.z> # Assign fixed IP addresses based on MAC address # dhcp-host=00:1a:64:ce:89:4a,NAME01,www.xx.yy.zz,infinite # dhcp-host=52:54:00:42:6a:43,NAME02,www.xx.yy.zz,infinite # Assign dynamically IP addresses to interface to listen on # Range for distributed addresses, tagged <int> for further references dhcp-range=tag:<ENPxyz>,<vvv.ww.xx.y,vvv.ww.xx.z>,24hSubstitute values for <INTERNAL>, <ENPxyz> with a name for your internal private network, and the network interface connected to your internal private network.
Substitute the value of <uuu.vv.xx.y/24> with the network address and mask for your internal private network.
The example shows the binding of the network interface and then sets the different DHCP responses for various client request options.
The subnet mask (dhcp-option=1) is set to <255.255.255.0>, the default router or gateway (dhcp-option=3) is set to <www.xxx.yy.zz>, and the DNS server (dhcp-option=6) is set to <www.xx.yy.z>. Substitute values appropriate to your network.
In this example, no permanent or fixed IP addresses are assigned, but examples are provided as commented lines, so that you can see how to add an entry to assign a particular IP address to a host based on it’s MAC address.
The example enables a DHCP range within the network and assigns hosts IP addresses from <vvv.ww.xx.y> to <vvv.ww.xx.z> with leases lasting for 24 hours. Substitute values as appropriate for your network.
-
Configuration of the DHCP service for the public network (<PUBLIC.TLD>)
Create and edit the file at
/etc/NetworkManager/dnsmasq.d/03-DHCP-<PUBLIC.TLD>.confusing your preferred editor running under sudo with root privileges. The content should be as follows:# etc/NetworkManager/dnsmasq.d/03-DHCP-<PUBLIC.TLD>.conf # This file sets up DHCP for the public '<PUBLIC.TLD>' domain interface # The domain the DHCP part of dnsmasq is responsible for: domain=<PUBLIC.TLD>,<uuu.vv.ww.xx/24> # the public interfaces to listen on interface=<ENPuvw> # 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.0> dhcp-option=tag:<ENPuvw>,option=router,<uuu.vv.ww.zz> # 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:<ENPuvw>,<uuu.vvv.w.x,uuu.vvv.w.y6,1hSubstitute values for <PUBLIC.TLD>, <ENPuvw> with the domain name for your public facing network, and the network interface connected to your public facing network.
Substitute the value of <uuu.vv.ww.xx/24> with the network address and mask for your public facing network.
Substitute the value <uuu.vv.ww.zz> with the IP address of the network router or default gateway.
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 public.tld.
-
Test the dnsmasq configuration
$ dnsmasq --test -
Adjusting the firewall
Allow ports for DHCP (UDP port 67) and DNS (TCP port 53) service on the public interface. If the system is running firewalld, you can configure these services as follows:
$ firewall-cmd --get-services $ sudo firewall-cmd --zone=<YOUR_ZONE> --permanent --add-service=dhcp $ sudo firewall-cmd --zone=<YOUR_ZONE> --permanent --add-service=dns $ sudo firewall-cmd --reload $ firewall-cmd --list-all --zone=<YOUR_ZONE> -
Restart NetworkManager to start dnsmasq
$ sudo systemctl restart NetworkManager $ ps -ef | grep dnsmasq dnsmasq 2114 2072 0 08:33 ? 00:00:00 /usr/sbin/dnsmasq --no-resolv ...NetworkManager should have started dnsmasq shown by the 'ps' command above.
-
Restart systemd-resolved
$ sudo systemctl restart systemd-resolved $ resolvectl statusThe systemd-resolved should recognize the dnsmasq nameserver attached to interfaces as configured.
-
Test the installation
-
Test DHCP in the public using a machine without IP address
$ ip a # no IPv4 address associated with interface $ sudo dhclient -4 -1 -v eth0 $ ip a # expect new IPv4 address associated with interface $ sudo dhclient -4 -1 -r -v eth0 # expected: no IPv4 again $ ip a # expect no IPv4 address associated with interface again -
Validate that a DNS lookup for the system works correctly on another server, by running any of the following:
$ dig app1 @10.10.10.1 $ nslookup app1 10.10.10.1 $ dhclient -v -d -s 10.10.10.1 enp6s0
-
Masquerading / NAT
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
$ sudo firewall-cmd --zone=FedoraServer --add-masquerade --permanent success $ sudo firewall-cmd --zone=trusted --add-masquerade --permanent success $ sudo firewall-cmd --reload $ sudo firewall-cmd --zone=FedoraServer --query-masquerade yes $ sudo 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 $ sudo firewall-cmd --direct --add-rule ipv4 nat POSTROUTING 0 -o enp1s0 -j MASQUERADE success $ sudo firewall-cmd --direct --add-rule ipv4 filter FORWARD 0 -i vbr2s0 -o enp2s0 -j ACCEPT success $ sudo 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 $ sudo firewall-cmd --permanent --new-policy trustedToExt success $ sudo firewall-cmd --permanent --policy trustedToExt --add-ingress-zone trusted success $ sudo firewall-cmd --permanent --policy trustedToExt --add-egress-zone FedoraServer success $ sudo firewall-cmd --permanent --policy trustedToExt --set-target ACCEPT success $ sudo firewall-cmd --reload successThis method is much clearer, improves maintainability and reduces sources of potential errors. The documentation of the upstream project provides more information.
-
Integrate libvirt’s virtual interface
In case libvirt and virtualization 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 another configuration file, for example /etc/NetworkManager/dnsmasq.d/30-DNS-libvirt.conf.
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.
# /etc/NetworkManager/dnsmasq.d/30-DNS-libvirt.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
Managing static DNS Entries
-
Edit the dnsmasq host file that you created at
/etc/dnsmasq-<INTERNAL>.hoststo define internal DNS mappings for hosts on the local network.The format is the same as /etc/hosts. See the hosts(5) man page for more information.
-
Restart NetworkManager to read the modified file.
$ sudo systemctl restart NetworkManager -
Test the modification
$ sudo nslookup {NAME} $ sudo nslookup {NAME}.example.lan
Configuring dnsmasq as a PXE service
Dnsmasq can be configured to act as a PXE service, by enabling the TFTP server and setting some dhcp-boot directives. In this example, the configuration is extended to provide a PXE service to enable network boot and to configure a Fedora installation over the network.
Systems that use legacy BIOS to perform a network boot behave differently to systems that use UEFI firmware to do the same. Since most modern systems use UEFI and systems booting using legacy BIOS can complicate setup, the configuration described in this example focuses only on UEFI support.
-
Install packages required to support PXE boot clients:
$ sudo dnf install grub2-tools grub2-efi-x64-modules -
Create the TFTP server directories and provide the network boot loader files:
$ sudo mkdir /var/lib/tftpboot/Use the
grub2-mknetdircommand to generate EFI binaries that cann be used for network boot.$ sudo grub2-mknetdir --net-directory=/var/lib/tftpboot --subdir=EFI Netboot directory for i386-pc created. Configure your DHCP server to point to /var/lib/tftpboot/EFI/i386-pc/core.0 Netboot directory for i386-efi created. Configure your DHCP server to point to /var/lib/tftpboot/EFI/i386-efi/core.efi Netboot directory for x86_64-efi created. Configure your DHCP server to point to /var/lib/tftpboot/EFI/x86_64-efi/core.efiUEFI systems that have Secure Boot enabled might reject a PXE boot if the EFI binaries aren’t signed. The most simple solution to this issue is to disable Secure Boot in the UEFI firmware when you need to perform network-based installation.
-
Download or copy the installation kernel and ram-disk image files to the TFTP server directory hierarchy:
You can either copy these files directly off an installation ISO, or you can download them directly from Fedora. In this example, we create a directory to host these files and then download each of the files from the Fedora pxeboot download URL:
$ sudo mkdir /var/lib/tftpboot/fedora44 $ sudo curl -L -o /var/lib/tftpboot/fedora44/initrd.img https://dl.fedoraproject.org/pub/fedora/linux/releases/44/Everything/x86_64/os/images/pxeboot/initrd.img $ sudo curl -L -o /var/lib/tftpboot/fedora44/vmlinuz https://dl.fedoraproject.org/pub/fedora/linux/releases/44/Everything/x86_64/os/images/pxeboot/vmlinuz -
Create the PXE Boot configuration files:
PXE Boot configuration files define the boot menu that loads once the system is connected to the TFTP server.
The
inst.repoURL in the file tells the Anaconda installer where to fetch packages from. The URL provided in the example is the official Fedora mirror, but if the system does not have external network access, you can equally create a mirror or host the install tree from an ISO locally, and point to that instead. Also, downloading from the Fedora mirror could take a long time and slow down network based install. Consider creating a mirror and then pointing to a URL on your local network as an alternative.Note that menu entry for the Fedora 44 Network Install points to the fedora44/initrd.img and fedora44/vmlinuz files that we downloaded in the previous step. If you wanted to host multiple install options you can download the kernel and ram-disk images for each distribution or release and create a menu entry for each release.
A menu entry is included to exit out of this menu and to use the next configured boot option, which usually includes the hard disk for the system. Providing this option in the menu, gives you a way to boot to disk easily. You can change the
set default=0line toset default=1to make the second menu entry the default value, if you prefer.Create and edit the file at
/var/lib/tftpboot/EFI/x86_64-efi/grub.cfgusing your preferred editor running under sudo to acquire root priveleges:set timeout=10 set default=0 menuentry "Fedora 44 Network Install" { linuxefi fedora44/vmlinuz \ inst.repo=https://dl.fedoraproject.org/pub/fedora/linux/releases/44/Everything/x86_64/os/ \ ip=dhcp \ inst.lang=en_US.UTF-8 \ inst.keymap=us \ quiet initrdefi fedora44/initrd.img } menuentry 'Exit to next boot device' { exit } -
Add a dnsmasq configuration entry to enable the TFTP service: Create and edit the file at
/etc/NetworkManager/dnsmasq.d/10-TFTP.confusing your preferred editor running under sudo to acquire root priveleges:# Enable TFTP server #enable-tftp[=<interface>[,<interface>]] enable-tftp # dnsmasq serves files from this directory tftp-root=/var/lib/tftpboot # Tell DHCP clients where to find the TFTP server and which file to load # Note that this configuration assumes that all clients are using UEFI firmware. # If you need to distinguish between client types, use the dhcp match directive to identify the client-arch in the DHCP request. See RFC 4578 2.1. # BIOS clients have a client-arch=0. # UEFI x86_64 clients have a client-arch=7|9. dhcp-match=set:efi-x86_64,option:client-arch,7 dhcp-match=set:efi-x86_64,option:client-arch,9 # dhcp-boot=<tag>,<filename>[,<server-name>,<server-ip>] dhcp-boot=tag:efi-x86_64,EFI/x86_64-efi/core.efiIn the configuration, DHCP matching is used to tag the request for the appropriate client architecture (UEFI). The DHCP boot instructions describe the filename to serve for the given tag. If you were handling BIOS clients and you had configured the syslinux binaries appropriately, you could create dhcp-match and dhcp-boot directives appropriate to that client architecture.
You can optionally edit the dhcp-boot directive to specify a server hostname or server IP where the TFTP service is located if not on the current dnsmasq host. Note that if you only want the TFTP service to be available on a particular network interface you can change the
enable-tftpoption toenable-tftp=<IFNAME>. -
Enable access to the TFTP service (UDP port 69) in your firewall:
If you’re running firewalld, you can do this by running the following commands:
$ sudo firewall-cmd --zone=FedoraServer --permanent --add-service=tftp $ sudo firewall-cmd --reload -
Test the dnsmasq configuration to make sure that it is still valid:
$ dnsmasq --test dnsmasq: syntax check OK. -
Restart NetworkManager to restart the dnsmasq plugin and enable the TFTP configuration:
$ sudo systemctl restart NetworkManager -
If the system is running SELinux, set the file context on the tftpboot directory:
$ sudo semanage fcontext -a -t tftpdir_t "/var/lib/tftpboot(/.*)?" $ sudo restorecon -R -v /var/lib/tftpbootIf semanage isn’t available, install the
policycoreutilspackage. -
Network boot a client system to check that it is assigned an IP address and that the network based installation starts:
While the client system is booting, you can monitor the NetworkManager logs to check that DHCP requests are recieved and that the service responds correctly:
$ journalctl -u NetworkManager -f | grep -E "dnsmasq|DHCP"The client system should present the Network Boot menu and if the timeout is reached, should begin loading the Anaconda installer automatically.
For network-based install to be properly effective, you should create a complete kickstart configuration file that can guide Anaconda through an unattended installation.
The Kickstart configuration file can be hosted on a local HTTP or NFS service and you can make it available to the installer by editing the PXE configuration file at /var/lib/tftpboot/EFI/x86_64-efi/grub.cfg and adding an entry for inst.ks= beneath the inst.repo line. For example, to point to a kickstart file hosted on an HTTP server with the IP address 10.0.100.1, run something like:
sudo sed -i '/inst.repo/a inst.ks=http://10.0.100.1/kickstart/my-server.ks.cfg \' /var/lib/tftpboot/EFI/x86_64-efi/grub.cfg
If you make any changes to /etc/dnsmasq.conf, restart the dnsmasq service. You do not need to restart the service if you change the content of boot loader configuration files.
Want to help? Learn how to contribute to Fedora Docs ›