Skip to content

DNSMasq

dnsmasq is a caching dns and dhcp server that allows a single source of truth for both. This replaces services such as bind and isc-dhcp. One of the nice things is that once you give out an address to a host the host resolves to that address. The other nice thing is that it is file based

It was my hope that I could configure the dnsmasq provided by opnsense with files that I could control but it insisted on requiring all settings be implimented via its gui and overwriting any manual files. So we are running dnsmasq on a separate server.

At some point I will get this to work on a freebsd jail running on the router.

sample dnsmasq.conf

#------------------------------------------------------------ dnsmasq.conf for figaro
# symlink as appropriate (linux=/etc/dnsmasq.conf,bsd=/usr/local/etc/dnsmasq.conf)
#
#
# Global stuff. 
#
#interface=igb0          # Only needed for freebsd
bind-interfaces          # Really bind only the configured interfaces
no-resolv                # Don't read /etc/resolv.conf for upstream DNS
no-poll
domain-needed
bogus-priv

#
# Logging
#

log-dhcp
log-queries
log-facility=/var/log/dnsmasq.log

#
# upstream dns
#
server=192.168.128.2
server=205.171.3.25
server=8.8.8.8
all-servers
cache-size=5000

#
# local dns configuration
#
auth-server=merlot,192.168.129.2
local=/merlot/
auth-zone=merlot,192.168.128.0/17
expand-hosts
#no-hosts

# 
# Dhcp configuration.
#
dhcp-ignore-clid
domain=merlot
dhcp-range=192.168.128.100,192.168.129.254
dhcp-option=option:router,192.168.128.1
#dhcp-option=option:ntp-server,192.168.0.4,10.10.0.5
#dhcp-host=D4:BE:D9:EC:EE:CE,192.168.31.49
#dhcp-option=vendor:PXEClient,1,192.168.31.2
dhcp-option=option:domain-search,merlot,merlot.suspectdevices.com,suspectdevices.com,digithink.com,fromhell.com,local
dhcp-option=option:dns-server,192.168.129.2,205.171.3.25,8.8.8.8
dhcp-authoritative
dhcp-hostsfile=/usr/local/merlot/dnsmasq/reservations.list

#
# cname 
#
conf-file=/usr/local/merlot/dnsmasq/cnames.list

Generating the necessary files.

one file to rule them all

We start with a csv file with all of the information we need.

domain,address,mac,name,notes,alias,,,
merlot,192.168.128.1,00:0e:b6:c0:1a:8b,misskitty,firewall,,,
...

as little pile of python

#!/usr/bin/env python3
#------------------------------------------------------ creatednsmasqconfigs.py
# Given a csv file in the following format
# domain,address,mac,name,notes,alias,,,
#
# Generate the files used by dnsmasq (hosts,ether,reservations,cnames)
#
import sys
import csv

with open("/etc/hosts", "w") as h, \
     open("/etc/ethers", "w") as e, \
     open("/usr/local/merlot/dnsmasq/reservations.list", "w") as r, \
     open("/usr/local/merlot/dnsmasq/cnames.list", "w") as c:

    # should print comment header lines to each file here.
    print("127.0.0.1 localhost localhost.merlot localhost.merlot.suspectdevices.com",file=h)
    reader = csv.DictReader(open('/usr/local/merlot/data/merlot.csv'))
    for row in reader:
        if ('#' not in row['domain']): # skip commentted out lines (kassumes domain is first field)
            print(row['address']+' '+row['name']+' '+row['name']+'.'+row['domain'],file=h)
            print(row['mac']+' '+row['name']+'.'+row['domain'],file=e)
            print(row['mac']+','+row['address']+','+row['name']+',infinite',file=r)
            if (row['alias']):
                print('cname='+row['alias']+','+row['alias']+'.'+row['domain']+\
                    ','+row['name']+'.'+row['domain'],file=c)

files generated

  • /etc/hosts
    127.0.0.1 localhost localhost.merlot localhost.merlot.suspectdevices.com
    192.168.128.1 misskitty misskitty.merlot
    
  • /etc/hosts
    00:0e:b6:c0:1a:8b misskitty.merlot
    
  • reservations
    00:0e:b6:c0:1a:8b,192.168.128.1,misskitty,infinite
    
  • cnames
    cname=firewall,firewall.merlot,misskitty.merlot
    

references

  • https://oneuptime.com/blog/post/2026-01-15-setup-dnsmasq-local-dns-ubuntu/view