Configure DNS and DHCP

DHCP is the process your LAN clients perform when they are first connected to the network: the client asks the router to configure an IP address for them, they recieve the address, and can now get online. DNS is how your LAN clients can ask the router what the IP address is for any domain name, allowing you to easily navigate the Internet by entering names like example.com.

To configure DHCP and DNS for your LAN will require two interrelated services to run on the router:

Info

dnscrypt-proxy will be set to only listen on localhost port 53, therfore the LAN client cannot directly communicate with it. Dnsmasq is what the LAN client will interact with. In addition to being the DHCP server for the LAN, dnsmasq has the job of forwarding any DNS queries to the dnscrypt resolver on localhost.

Configure dnscrypt-proxy

Run this on the Router VM
## Configure the listen address for localhost only:
## Set the upstream cloudflare DNS resolver:
sed -i \
  -e "s/^listen_addresses =.*/listen_addresses = ['127.0.0.1:53','[::1]:53']/" \
  -e "s/^# server_names =.*/server_names = ['cloudflare']/" \
  /etc/dnscrypt-proxy/dnscrypt-proxy.toml

## Hard code the dnscrypt resolver as the default:
## Lock the resolv config so it cannot be modified again:
chattr -i /etc/resolv.conf || true
rm -f /etc/resolv.conf
cat <<EOF > /etc/resolv.conf
nameserver ::1
nameserver 127.0.0.1
options edns0
EOF
chattr +i /etc/resolv.conf

## Enable the dnscrypt service:
systemctl enable dnscrypt-proxy
systemctl restart dnscrypt-proxy

Create dnsmasq service definition

Run this on the Router VM
## Create instantiable dnsmasq service file:
cat <<'EOF' > /etc/systemd/system/dnsmasq@.service
[Unit]
Description=dnsmasq for %i
Documentation=man:dnsmasq(8)
After=network.target
Before=network-online.target nss-lookup.target
Wants=nss-lookup.target

[Service]
ExecStart=/usr/local/sbin/dnsmasq-%i.sh
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
PrivateDevices=true
ProtectSystem=full

[Install]
WantedBy=multi-user.target
EOF

## Reload systemd service files:
systemctl daemon-reload

Configure dnsmasq

nifty-filter can also configure dnsmasq. Specify your entire config in a shell script /usr/local/sbin/dnsmasq-lan.sh:

Run this on the Router VM
cat <<'EOF' > /usr/local/sbin/dnsmasq-lan.sh
#!/bin/bash
set -e

## Bind to the lan interface:
export INTERFACE=lan
export LISTEN_ADDRESS=192.168.10.1

## DHCP config:
export DOMAIN_LAN=lan.example.com
export GATEWAY_LAN=192.168.10.1
export DHCP_LAN_RANGE_START=192.168.10.50
export DHCP_LAN_RANGE_END=192.168.10.250
export DHCP_LAN_LEASE=12h

## Static DHCP Leases:
## bash array of "MAC_ADDRESS,IP_ADDRESS,HOST_NAME,LEASE_TIME"
STATIC_LEASES=(
  ## "9c:69:b4:65:7d:f8,192.168.10.2,arch-client1,12h"
  ## "9c:69:b4:65:7d:f9,192.168.10.3,arch-client2,12h"
)
## convert array to string:
export DHCP_LAN_STATIC_LEASES="${STATIC_LEASES[@]}"

## DNS config - Forward DNS to dnscrypt on localhost
export DNS_LAN=192.168.10.1
export DNS_UPSTREAM_1=::1
export DNS_UPSTREAM_2=127.0.0.1

nifty-filter dnsmasq | dnsmasq -C - --test

echo "## Applying dnsmasq config:"
(echo "## This file is generated by nifty-filter. DO NOT EDIT."; \
 nifty-filter dnsmasq) > /tmp/dnsmasq-${INTERFACE}.conf
dnsmasq -C /tmp/dnsmasq-${INTERFACE}.conf -d --user=dnsmasq --pid-file
EOF

chmod +x /usr/local/sbin/dnsmasq-lan.sh
ln -sf /usr/local/sbin/dnsmasq-lan.sh ~/dnsmasq-lan.sh
Tip

Make sure to edit the following config variables:

  • DOMAIN_LAN customize your own LAN domain name.
  • DHCP_LAN_STATIC_LEASES customize your own list of hosts that should have static DHCP leases
    • STATIC_LEASES is an intermediate array to help build DHCP_LAN_STATIC_LEASES, with a set of examples commented out.

Enable the dnsmasq service

Run this on the Router VM
systemctl enable dnsmasq@lan.service
systemctl restart dnsmasq@lan.service
systemctl status dnsmasq@lan.service --no-pager

Custom host names

dnsmasq is setup to prioritize any host names defined in /etc/hosts and to resolve these by itself (without forwarding the query to dnscrypt). You can put any names you want in this file, whether they are masking real domains, or even if they are completely made up.

# Example /etc/hosts file
192.168.10.2 foo foo.lan.example.com
192.168.10.3 bar boatymcboatface bar.lan.example.com
Info

Dnscrypt will only read the hosts file on startup, so you must restart the dnsmasq service each time you edit /etc/hosts:

Run this on the Router VM
systemctl restart dnsmasq@lan
Run this on a LAN client
## All names from /etc/hosts are resolved.
## Short, long, made up, doesn't matter:

$ dig foo
...
;; ANSWER SECTION:
foo.                    0       IN      A       192.168.10.2

$ dig foo.lan.example.com
...
;; ANSWER SECTION:
foo.lan.example.com.    0       IN      A       192.168.10.2

$ dig boatymcboatface
...
;; ANSWER SECTION:
boatymcboatface.    0       IN      A       192.168.10.3