KVM: Using dnsmasq for libvirt DNS resolution

On Linux host servers, libvirt uses a separate instance of dnsmasq for each virtual network.  So if you want full name and reverse lookup for KVM guests on the default 192.168.122.0 network, you can configure this particular dnsmasq instance using virsh.

The ability to customize the libvirt instances are limited, however, so if you need full dnsmasq capabilities or you need this DNS resolution to span multiple networks then you may want to allow these instances to go upstream to a global service instance of dnsmasq.

virsh for specific virtual network instance

Take a look at the dnsmasq instance used for the ‘default’ virtual network.  Notice how the binding is specific to the 192.168.122.1 network.

$ virsh net-list --all
 Name                 State      Autostart     Persistent
----------------------------------------------------------
 default              active     yes           yes

$ ps -ef | grep dnsmasq | grep default
libvirt+  2624     1  0 Oct16 ?        00:00:05 /usr/sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/default.conf

$ lsof -p 2624 | grep IPv4 | grep LISTEN
dnsmasq 2624 libvirt-dnsmasq    7u     IPv4              17534      0t0     TCP 192.168.122.1:domain (LISTEN)

If we wanted to add full DNS resolution to a host named ‘esxi1.lab.home’ with a static IP of 192.168.122.133, then we would first use virsh net-edit on the default network.

virsh net-edit default

And add the following content right before the <ip> element:

<dns>
<host ip='192.168.122.133'>
  <hostname>esxi1.home.lab</hostname>
</host>
</dns>

Then restart the default virtual network.

virsh net-destroy default && virsh net-start default

And test both a name and reverse lookup.

$ nslookup esxi1.home.lab 192.168.122.1
Server:		192.168.122.1
Address:	192.168.122.1#53
Name:	esxi1.home.lab
Address: 192.168.122.133

$ nslookup 192.168.122.133 192.168.122.1
Server:		192.168.122.1
Address:	192.168.122.1#53
133.122.168.192.in-addr.arpa	name = esxi1.home.lab.

Although the configuration files for the libvirt instances is kept in the “/var/lib/libvirt/dnsmasq” directory, changes you make to these files will be overwritten, so modifying them directly is not an option – instead you need to use virsh.

Host service instance of dnsmasq

The libvirt specific instances of dnsmasq use the DNS server the host machine has defined as upstream.  In other words, if the libvirt host machine’s “/etc/resolv.conf” points at Google’s DNS servers, that is where it will go for unrecognized names.

We can use this hook and have the libvirt host’s “/etc/resolv.conf” point to 127.0.0.1, and now all the libvirt specific dnsmasq instances will defer their upstream requests to our custom host instance of dnsmasq.

This exposes the full feature set of dnsmasq including wildcards and domains.  Installing dnsmasq on the host is as simple as using apt-get.

sudo apt-get install dnsmasq -y

Add the following lines to “/etc/dnsmasq.conf”, which will limit the binding to the local interface (lo) so it does not interfere with the libvirt bindings on other interfaces.

listen-address=127.0.0.1
interface=lo

# if you wanted additional bindings, append another 'interface' or 'listen-address' line
# I have seen interface throw 'unknown interface' errors
# if so, try listen-address instead
#interface=br0
#listen-address=<myNIC_IP>
bind-interfaces
server=<yourUpstreamDNSIPAddress>
log-queries

# does not go upstream to resolve addresses ending in 'home.lab'
local=/home.lab/

And restart the dnsmasq service

sudo systemctl restart dnsmasq
sudo systemctl status dnsmasq

Although you could add custom hostname mappings directly to dnsmasq.conf, dnsmasq also reads “/etc/hosts” as part of its lookup logic, so this is a nice way to easily add entries.  Go ahead and add the following entries to “/etc/hosts”.

192.168.122.133 esxi1.home.lab

Now edit “/etc/systemd/resolved.conf” (the newer replacement for what used to be “/etc/resolv.conf”) so it points at this dnsmasq instance for resolution.

# change 'DNS' to 127.0.0.1
sudo vi /etc/systemd/resolved.conf

# restart systemd resolv and then check status
sudo systemctl restart systemd-resolved 

# global DNS should now be '127.0.0.1' pointing to local dnsmasq resolvectl dns

Validate by doing a name and reverse lookup on this local dnsmasq instance.

$ nslookup esxi1.home.lab 127.0.0.1
Server: 127.0.0.1
Address: 127.0.0.1#53

Name: esxi1.home.lab
Address: 192.168.122.133

$ nslookup 192.168.122.133 127.0.0.1
Server:		127.0.0.1
Address:	127.0.0.1#53
133.122.168.192.in-addr.arpa	name = esxi1.home.lab.

Validate upstream of virtual network specific instances

If you want to prove to yourself that the virtual network specific instances will use the host service instance for upstream, then do a ‘virsh net-edit default’ and remove the <dns> section and restart the default network.

virsh net-destroy default && virsh net-start default

Then do a lookup on the 192.168.122 specific instance, and notice it still resolves.  This is because it is going to the dnsmasq host instance.

nslookup esxi1.home.lab 192.168.122.1

And if you tail “/var/log/syslog”, you will see the query come in.

Oct 21 23:50:44 myhost dnsmasq[8446]: query[A] esxi1.home.lab from 127.0.0.1
Oct 21 23:50:44 myhost dnsmasq[8446]: /etc/hosts esxi1.home.lab is 192.168.122.133

 

 

REFERENCES

https://www.cyberciti.biz/faq/linux-kvm-libvirt-dnsmasq-dhcp-static-ip-address-configuration-for-guest-os/ (set static IP for virsh)

https://serverfault.com/questions/101292/libvirt-change-dhcp-setup-without-restarting

https://www.redhat.com/archives/libvirt-users/2015-February/msg00089.html (example <dns> section)

https://wiki.libvirt.org/page/Networking#virsh_net-update (only safe changes to dnsmaq are through virsh net-update)

https://serverfault.com/questions/840163/custom-dnsmasq-or-custom-options-with-libvrt (advanced dnsmasq options not available with libvirt)

https://serverfault.com/questions/727998/dnsmasq-on-virbr0-libvirt-and-network-manager (new dnsmasq instance for each virtual interface bridge)

https://serverfault.com/questions/101982/get-list-of-dhcp-clients-with-kvmlibvirt (domifaddr and net-dhcp-leases in newer releases of virsh)

http://www.think-foundry.com/build-cloud-foundry-vsphere-bosh/ (vmware and cloudfoundry with vlan)

https://wiki.libvirt.org/page/Networking#virsh_net-update (list of items that can be updated with virsh net-update)

https://qiita.com/bmj0114/items/9c24d863bcab1a634503 (dnsmasq wildcards and forwarding to upstream)

https://www.redhat.com/archives/libvirt-users/2014-April/msg00028.html (use bind-interfaces for host system instance so it does not conflict with instances from libvirt)

https://wiki.archlinux.org/index.php/dnsmasq (dnsmasq upstream, custom domain, expand-hosts, test)

https://serverfault.com/questions/799200/bind-dnsmasq-dns-to-just-localhost-127-0-0-1 (bind just to localhost to avoid conflict)

https://docs.openstack.org/ocata/networking-guide/misc-libvirt.html (default iptables rules that libvirt creates for default network)

https://textik.com/ (ascii art creator for image)

NOTES

Example of using dns-txt, dns-srv, dns-host records [1].

<domain name="example.com"/>
<dns>
  <txt name="example" value="example value" />
  <srv service='name' protocol='tcp' domain='test-domain-name' target='.' port='1024' priority='10' weight='10'/>
  <host ip='192.168.122.2'>
    <hostname>myhost</hostname>
    <hostname>myhostalias</hostname>
  </host>
</dns>

direct mapping put into /etc/dnsmasq.conf

local=/home.lab/
address=/esxi1.home.lab/192.168.122.133

if dnsmasq systemd service was started and stopped too frequently, you have to unmask

sudo systemctl unmask dnsmasq.service && sudo systemctl enable dnsmasq.service