DHCP, DNS and time
Addresses, names and the clock for every network, from the firewall.
Why DNS, on the tour
On this page 4 sections
As in the last chapter, each file is copied from your laptop to /tmp on the firewall, then installed:
scp dhcpd.conf fw:/tmp/1. DHCP#
Each network gets the firewall as its router, DNS and time server. Devices that need a fixed address are listed by their hardware address; a few networks also get a pool for everything else.
Show the whole file, 81 lines
# /etc/dhcpd.conf - fw.internal
# Layout: .1 firewall, .2-.9 infra, .10-.149 fixed, .150-.199 pool (only where useful)
option domain-name "internal";
default-lease-time 86400;
max-lease-time 86400;
# ADMIN - wired admin laptop
subnet 192.168.1.0 netmask 255.255.255.0 {
option routers 192.168.1.1;
option domain-name-servers 192.168.1.1;
option ntp-servers 192.168.1.1;
range 192.168.1.150 192.168.1.199;
host laptop { hardware ethernet @MAC@; fixed-address 192.168.1.10; }
}
# APMGMT - access point only, no pool
subnet 192.168.2.0 netmask 255.255.255.0 {
option routers 192.168.2.1;
option domain-name-servers 192.168.2.1;
option ntp-servers 192.168.2.1;
host unifi-ap { hardware ethernet @MAC@; fixed-address 192.168.2.2; }
}
# PS5 - no pool
subnet 192.168.3.0 netmask 255.255.255.0 {
option routers 192.168.3.1;
option domain-name-servers 9.9.9.9, 149.112.112.112;
option ntp-servers 192.168.3.1;
host ps5 { hardware ethernet @MAC@; fixed-address 192.168.3.10; }
}
# MEDIA - no pool
subnet 192.168.4.0 netmask 255.255.255.0 {
option routers 192.168.4.1;
option domain-name-servers 192.168.4.1;
option ntp-servers 192.168.4.1;
host kodi { hardware ethernet @MAC@; fixed-address 192.168.4.10; }
}
# SERVERS - cluster, UniFi host, Home Assistant; no pool
subnet 192.168.5.0 netmask 255.255.255.0 {
option routers 192.168.5.1;
option domain-name-servers 192.168.5.1;
option ntp-servers 192.168.5.1;
host k8s-cp1 { hardware ethernet @MAC@; fixed-address 192.168.5.10; }
host k8s-cp2 { hardware ethernet @MAC@; fixed-address 192.168.5.20; }
host k8s-cp3 { hardware ethernet @MAC@; fixed-address 192.168.5.30; }
host k8s-worker1 { hardware ethernet @MAC@; fixed-address 192.168.5.40; }
host k8s-worker2 { hardware ethernet @MAC@; fixed-address 192.168.5.50; }
host unifi { hardware ethernet @MAC@; fixed-address 192.168.5.60; }
host homeassistant { hardware ethernet @MAC@; fixed-address 192.168.5.140; }
}
# TRUSTED - phones and laptop on Wi-Fi
subnet 192.168.10.0 netmask 255.255.255.0 {
option routers 192.168.10.1;
option domain-name-servers 192.168.10.1;
option ntp-servers 192.168.10.1;
range 192.168.10.150 192.168.10.199;
host laptop-wifi { hardware ethernet @MAC@; fixed-address 192.168.10.10; }
host phone { hardware ethernet @MAC@; fixed-address 192.168.10.11; }
host watch { hardware ethernet @MAC@; fixed-address 192.168.10.12; }
}
# IOT - pool for new IoT devices, client isolation on the SSID
subnet 192.168.20.0 netmask 255.255.255.0 {
option routers 192.168.20.1;
option domain-name-servers 192.168.20.1;
option ntp-servers 192.168.20.1;
range 192.168.20.150 192.168.20.199;
host vacuum { hardware ethernet @MAC@; fixed-address 192.168.20.10; }
}
# GUEST - visitors, pool only
subnet 192.168.30.0 netmask 255.255.255.0 {
option routers 192.168.30.1;
option domain-name-servers 9.9.9.9, 149.112.112.112;
option ntp-servers 192.168.30.1;
range 192.168.30.150 192.168.30.199;
}- Replace every
@MAC@or delete the line. On most devices the hardware address is in the network settings; on phones, use the private address shown for this Wi-Fi network.
doas install -o root -g wheel -m 0644 /tmp/dhcpd.conf /etc/dhcpd.conf
doas dhcpd -n -c /etc/dhcpd.conf
doas rcctl enable dhcpd
doas rcctl set dhcpd flags igc1 igc2 igc3 igc4 igc5 vlan10 vlan20 vlan30
doas rcctl start dhcpdCheck
rcctl check dhcpd
doas tail -5 /var/log/daemondhcpd(ok), and no errors. Devices already connected start getting their addresses: DHCPACK on … lines.
Keep the laptop on its manual address until chapter 5. On DHCP it would use the firewall as its router, and the firewall cannot pass traffic to the internet until pf adds NAT. If the laptop reaches the internet another way meanwhile, such as a phone hotspot, it would lose that.
2. DNS with unbound#
unbound answers for every network: local names itself, everything else by asking the internet's name servers directly, with DNSSEC validation. In chapter 6 it starts forwarding through the tunnel instead.
Show the whole file, 103 lines
# /var/unbound/etc/unbound.conf - fw.internal
server:
interface: 127.0.0.1
interface: 192.168.1.1
interface: 192.168.2.1
interface: 192.168.3.1
interface: 192.168.4.1
interface: 192.168.5.1
interface: 192.168.10.1
interface: 192.168.20.1
interface: 192.168.30.1
do-ip6: no
access-control: 0.0.0.0/0 refuse
access-control: 127.0.0.0/8 allow
access-control: 192.168.1.0/24 allow
access-control: 192.168.2.0/24 allow
access-control: 192.168.3.0/24 allow
access-control: 192.168.4.0/24 allow
access-control: 192.168.5.0/24 allow
access-control: 192.168.10.0/24 allow
access-control: 192.168.20.0/24 allow
access-control: 192.168.30.0/24 allow
# Hardening and privacy
hide-identity: yes
hide-version: yes
harden-glue: yes
harden-dnssec-stripped: yes
qname-minimisation: yes
aggressive-nsec: yes
prefetch: yes
# DNSSEC validation
auto-trust-anchor-file: "/var/unbound/db/root.key"
val-log-level: 1
# DNS rebinding protection: public names may not resolve to internal addresses
private-address: 10.0.0.0/8
private-address: 100.64.0.0/10
private-address: 169.254.0.0/16
private-address: 172.16.0.0/12
private-address: 192.168.0.0/16
private-domain: "internal."
# Tell Firefox not to switch to DNS over HTTPS on its own
local-zone: "use-application-dns.net." always_nxdomain
# iCloud Private Relay: switch it off for this network (Apple's documented method),
# so Safari uses unbound and the VPN's DNS instead of Apple's DNS partners
local-zone: "mask.icloud.com." always_nxdomain
local-zone: "mask-h2.icloud.com." always_nxdomain
# Local names
local-zone: "internal." static
local-data: "fw.internal. IN A 192.168.1.1"
local-data: "laptop.internal. IN A 192.168.1.10"
local-data: "unifi-ap.internal. IN A 192.168.2.2"
local-data: "ps5.internal. IN A 192.168.3.10"
local-data: "kodi.internal. IN A 192.168.4.10"
local-data: "k8s-cp1.internal. IN A 192.168.5.10"
local-data: "k8s-cp2.internal. IN A 192.168.5.20"
local-data: "k8s-cp3.internal. IN A 192.168.5.30"
local-data: "k8s-worker1.internal. IN A 192.168.5.40"
local-data: "k8s-worker2.internal. IN A 192.168.5.50"
local-data: "unifi.internal. IN A 192.168.5.60"
local-data: "k8s-api.internal. IN A 192.168.5.100"
local-data: "homeassistant.internal. IN A 192.168.5.140"
local-data: "siem.internal. IN A 192.168.5.200"
local-data: "ingress.internal. IN A 192.168.5.201"
# Every *.apps.internal name goes to the ingress (Traefik on .201)
local-zone: "apps.internal." redirect
local-data: "apps.internal. IN A 192.168.5.201"
local-data: "laptop-wifi.internal. IN A 192.168.10.10"
local-data: "phone.internal. IN A 192.168.10.11"
local-data: "watch.internal. IN A 192.168.10.12"
local-data: "vacuum.internal. IN A 192.168.20.10"
# Reverse lookups (so logs and tools show names instead of IPs)
local-data-ptr: "192.168.1.1 fw.internal."
local-data-ptr: "192.168.1.10 laptop.internal."
local-data-ptr: "192.168.2.2 unifi-ap.internal."
local-data-ptr: "192.168.3.10 ps5.internal."
local-data-ptr: "192.168.4.10 kodi.internal."
local-data-ptr: "192.168.5.10 k8s-cp1.internal."
local-data-ptr: "192.168.5.20 k8s-cp2.internal."
local-data-ptr: "192.168.5.30 k8s-cp3.internal."
local-data-ptr: "192.168.5.40 k8s-worker1.internal."
local-data-ptr: "192.168.5.50 k8s-worker2.internal."
local-data-ptr: "192.168.5.60 unifi.internal."
local-data-ptr: "192.168.5.100 k8s-api.internal."
local-data-ptr: "192.168.5.140 homeassistant.internal."
local-data-ptr: "192.168.5.200 siem.internal."
local-data-ptr: "192.168.5.201 ingress.internal."
local-data-ptr: "192.168.10.10 laptop-wifi.internal."
local-data-ptr: "192.168.10.11 phone.internal."
local-data-ptr: "192.168.10.12 watch.internal."
local-data-ptr: "192.168.20.10 vacuum.internal."
remote-control:
control-enable: yes
control-interface: /var/run/unbound.sock- The two
always_nxdomainzones are signal names: they tell Firefox not to switch to its own DNS over HTTPS, and Apple devices to switch off iCloud Private Relay on this network. private-addressstops public names from resolving to your internal addresses, a defence against DNS rebinding.
scp unbound.conf fw:/tmp/doas install -o root -g wheel -m 0644 /tmp/unbound.conf /var/unbound/etc/unbound.conf
doas unbound-anchor -v
doas unbound-checkconf
doas rcctl enable unbound
doas rcctl start unboundunbound-anchor fetches the DNSSEC root key into /var/unbound/db/root.key, the file the configuration points to. The rc script would do it at the first start, but unbound-checkconf runs before that and fails without it. On a fresh key it reports that the anchor was written and exits with status 1; that is expected.
Check
dig @192.168.1.1 openbsd.org +short
dig @192.168.1.1 fw.internal +short
dig @192.168.1.1 dnssec-failed.org | grep statusAn address for openbsd.org, 192.168.1.1 for fw.internal, and status: SERVFAIL for the last one: that domain is signed wrongly on purpose, so validation is working.
3. The firewall uses its own DNS#
Until now the firewall has used your provider's DNS servers. From here on it asks unbound, and the DHCP client on the WAN ignores the DNS servers it is offered:
nameserver 127.0.0.1
search internal
lookup file bindinterface igc0 {
ignore dns
}scp resolv.conf dhcpleased.conf fw:/tmp/doas rcctl disable resolvd && doas rcctl stop resolvd
doas install -o root -g wheel -m 0644 /tmp/resolv.conf /etc/resolv.conf
doas install -o root -g wheel -m 0644 /tmp/dhcpleased.conf /etc/dhcpleased.conf
doas rcctl restart dhcpleasedCheck
ping -c 1 openbsd.orgA reply, and /etc/resolv.conf still says nameserver 127.0.0.1 after a few minutes.
4. Time#
ntpd keeps the clock right, checks it against HTTPS servers so a forged answer cannot shift it, and serves time to every network.
# /etc/ntpd.conf - fw.internal
servers pool.ntp.org
server time.cloudflare.com
sensor *
# Sanity check against HTTPS servers, so a forged NTP answer can't shift the clock
constraint from "9.9.9.9"
constraints from "www.google.com"
# Serve time to the internal networks
listen on 192.168.1.1
listen on 192.168.2.1
listen on 192.168.3.1
listen on 192.168.4.1
listen on 192.168.5.1
listen on 192.168.10.1
listen on 192.168.20.1
listen on 192.168.30.1doas install -o root -g wheel -m 0644 /tmp/ntpd.conf /etc/ntpd.conf
doas ntpd -n && doas rcctl restart ntpdCheck
ntpctl -s statusWithin a few minutes: clock synced, with all peers and the constraint valid. Right after the restart it says clock unsynced while ntpd collects samples.
Clients do not have the internet yet: that needs NAT, which comes with pf in the next chapter.