NetworkEnjin – Part 2: DNS and DHCP with DNSMasq
Installing DNSMasq
To install dnsmasq:
apt install dnsmasq
You will see an error along the lines of “failed to create listening socket for port 53: Address already in use” that’s because systemd-resolved.service is running. We’ll disable that later once dnsmasq is configured.
Rename the default dnsmasq config file in case you would like to reference it later:
mv /etc/dnsmasq.conf /etc/dnsmasq.default.conf
Create a new /etc/dnsmasq.conf and simply fill it with:
conf-dir=/etc/dnsmasq.d/,*.conf
In case /etc/dnsmasq.d doesn’t exist run
mkdir -p /etc/dnsmasq.d
Configuring DNS
Create /etc/dnsmasq.d/dns.conf and fill it with:
no-resolv
server={:DNSServerIP:}
This will delete tell dnsmasq to ignore what the server has it’s dns directed to whatever you set {:DNSServerIP:} to be. You can specify multiple DNS servers by having multiple server lines.
Configuring DHCP
Create /etc/dnsmasq.d/dhcp.conf and fill it with:
dhcp-authoritative
dhcp-leasefile=/opt/dnsmasq/dnsmasq.leases
no-dhcp-interface={:WANInterface:}
dhcp-option=option:router,{:LANIP:}
dhcp-option=option:dns-server,{:LANIP:}
dhcp-range={:DHCPStartIP:},{:DHCPEndIP:},8h
To configure IP reservations add this line for each reservation you would like to make
dhcp-host={:MACAddress:}, {:IPReservation:}
To resolve hostnames to a domain add this
expand-hosts
domain={:LocalDomain:}
You’ll need to create the following folder for the dhcp lease file: /opt/dnsmasq
mkdir -p /opt/dnsmasq
Disabling systemd’s resolved
Once you are happy with your config disable systemd’s resolved and start dnsmasq
systemctl stop systemd-resolved.service
systemctl disable systemd-resolved.service
systemctl start dnsmasq.service
Optional: Reduce snooping with DNS over HTTPS
Install the tools to build our HTTPS to DNS proxy
apt install git build-essential cmake libc-ares-dev libcurl4-openssl-dev libev-dev
For our HTTPS to DNS proxy we are going to be using the following GitHub project: https://github.com/aarond10/https_dns_proxy
From wherever you feel appropriate clone the project, compile it and copy it to /usr/bin
git clone https://github.com/aarond10/https_dns_proxy.git
cd https_dns_proxy
cmake .
make
sudo cp ./https_dns_proxy /usr/bin
Create a systemd service to run the proxy with
systemctl edit --full --force https_dns_proxy.service
Make the contents of that service
[Unit]
Description=HTTPS to DNS Proxy
After=network.target
StartLimitIntervalSec=0
[Service]
Type=simple
Restart=always
RestartSec=3
User=nobody
Group=nogroup
ExecStart=/usr/bin/https_dns_proxy -a 127.0.0.1 -p 5053 -4 -b 8.8.8.8,8.8.4.4 -r "https://dns.google/dns-query"
[Install]
WantedBy=multi-user.target
The ExecStart line is where the magic happens. It starts https_dns_proxy listening on localhost port 5053 and looks at Google’s DoH servers.
Yes I do see irony of reducing snooping by setting my DNS to use Google’s servers. That’s a personal choice I have made, you can set it to be another provider if you like. As an example if you would rather use CloudFlare as your DNS provider change the ExecStart line to be
ExecStart=/usr/bin/https_dns_proxy -a 127.0.0.1 -p 5053 -4 -b 1.1.1.1,1.0.0.1 -r "https://cloudflare-dns.com/dns-query"
Once you save and exit out of the editor we’ll need to start and enable the service
systemctl enable https_dns_proxy.service
systemctl start https_dns_proxy.service
Edit /etc/dnsmasq.d/dns.conf and replace your upstream DNS servers with just
server=127.0.0.1#5053
Before you start testing restart dnsmasq
systemctl restart dnsmasq.service
Updating our base firewall
For DNS and DHCP to work we will need to update our firewall’s base config in /etc/nftables.conf
#!/usr/sbin/nft -f
flush ruleset
define WAN_INTERFACE = {:WANInterface:}
define LAN_SUBNET = {:LANSubnet:}
table inet filter {
set DNSMASQ_ALLOW_UDP {
type inet_service
elements = {
53, 67
}
}
set DNSMASQ_ALLOW_TCP {
type inet_service
elements = {
53
}
}
chain outgoing {
type filter hook output priority 100
policy accept
}
chain incoming {
type filter hook input priority 0
policy drop
ct state established,related accept
iif lo accept
meta l4proto {icmp, icmpv6} accept
ip saddr $LAN_SUBNET tcp dport 22 accept
udp sport bootpc udp dport bootps ip saddr 0.0.0.0 ip daddr 255.255.255.255 accept
udp dport @DNSMASQ_ALLOW_UDP accept
tcp dport @DNSMASQ_ALLOW_TCP accept
}
chain forwarding {
type filter hook forward priority 0
policy drop
ip saddr $LAN_SUBNET oifname $WAN_INTERFACE accept
iifname $WAN_INTERFACE ip daddr $LAN_SUBNET ct state related,established accept
}
}
table nat {
chain prerouting {
type nat hook prerouting priority 0
}
chain postrouting {
type nat hook postrouting priority 0
}
}
While what has changed here is mostly self explanatory the following line may have turned some heads.
udp sport bootpc udp dport bootps ip saddr 0.0.0.0 ip daddr 255.255.255.255 accept
Essentially this line is to accept DHCP discovery packets which occur before the device has an IP on the network.
Now this is all we need for our router to handle DNS and DHCP.
Tags: #Networkenjin #Linux #Dns #Dhcp #Dnsmasq