Redundant DHCP server and DNS Resolver using OpenBSD and FreeBSD

       742 words, 4 minutes

Some time ago, I set up Redundant DHCP server and DNS Resolver using OpenBSD . Time has past and one of the Raspberry Pi board I own is now running FreeBSD while the ODROID HC4 is running OpenBSD .

I secured both my DHCP server and DNS resolver services running on those boards so that I can perform maintenance on one machine without turning down the whole services set.

Unbound DNS Resolver

Redundant DNS Resolver is quite simple to achieve. DNS in general knows how to use several servers and switch from one to another in case of unavailability. Put it simple, have two DNS Resolvers configured on the LAN and configure the clients to use them both.

As-of today, OpenBSD 7.8 ships with unbound 1.24.0 and FreeBSD 14.4 ships with local-unbound 1.24.1 and dns/unbound 1.24.2.

My OpenBSD server is already configured to provide DNS resolving to my LAN (and WireGuarded) clients. My FreeBSD server can benefit from this configuration and be configured the same way. The configuration is quite simple: accept recursive queries from my LAN and forward them to some remote private DNS servers.

The FreeBSD handbook recommends again using local_unbound for non-local caching.

§ 2.8.4 local_unbound - Enable the DNS local unbound. It is necessary to keep in mind that this is a configuration only meant for use as a local caching forwarding resolver. If the objective is to set up a resolver for the entire network, install dns/unbound.

§ 32.7.2 Unbound is provided in the FreeBSD base system. By default, it will provide DNS resolution to the local machine only. While the base system package can be configured to provide resolution services beyond the local machine, it is recommended that such requirements be addressed by installing Unbound from the FreeBSD Ports Collection.

I am still unsure why this is. Whether you decide to modify local-unbound configuration or setup dns/unbound depends on you. In both case, the OpenBSD configuration can be borrowed and adapted to the FreeBSD server.

The unbound configuration on OpenBSD is located in /var/unbound/etc.
The unbound configuration on FreeBSD is located in /var/unbound (not recommended) or /usr/local/etc/unbound.

In FreeBSD, there doesn’t seem to be a CA Certificates file that can be used with tls-cert-bundle. On OpenBSD, it is /etc/ssl/cert.pem. On FreeBSD, it seems you need to install the ca_root_nss package.

When a change is required in my LAN DNS zone, I edit the zone file on the first server, reload it, then transfer the zone file to the second server and reload unbound there. DNS clients then always have the same DNS responses no matter why server replies.

Dynamic Host Configuration Protocol (DHCP) daemon

The OpenBSD dhcpd(8) has nice synchronization features:

-Y synctarget Add target synctarget to receive synchronisation messages. synctarget can be either an IPv4 address for unicast messages or a network interface name followed optionally by a colon and a numeric TTL value for multicast messages to the group 224.0.1.240. If the multicast TTL is not specified, a default value of 1 is used. This option can be specified multiple times.

-y synclisten Listen on synclisten for incoming synchronisation messages. The format for synclisten is the same as for synctarget, above. This option can be specified only once.

This DHCP daemon can also been installed on FreeBSD using the ports.

# pkg install dhcpd
(...)
[1/1] Fetching dhcpd-7.8.20260223: 100%    78 KiB  79.9 kB/s    00:01
Checking integrity... done (0 conflicting)
[1/1] Installing dhcpd-7.8.20260223...
[1/1] Extracting dhcpd-7.8.20260223: 100%
(...)

On OpenBSD, the configuration file is /etc/dhcpd.conf.
On FreeBSD, the configuration file is /usr/local/etc/dhcpd.conf.

I simply replicated the OpenBSD configuration onto FreeBSD and the service was ready to be started.

# scp -p openbsd:/etc/dhcpd.conf freebsd:/usr/local/etc/dhcpd.conf

#freebsd# /usr/local/sbin/dhcpd -dvn

As described in the manual, DHCP leases synchronization happens thanks to -y and/or -Y. I want an Active/Active configuration so I have both daemons listen to each other.

#openbsd# rcctl enable dhcpd
#openbsd# rcctl set dhcpd flags -y dwge0 -Y dwge0
#openbsd# rcctl start dhcpd

#freebsd# vi /etc/services
(...)
dhcpd-sync      8067/udp  # dhcpd(8) synchronisation
(...)

#freebsd# service dhcpd enable
#freebsd# sysrc dhcpd_flags="-y ue0 -Y ue0"
#freebsd# service dhcpd start

Both DHCP servers can now answers IP allocation requests and get synchronized with each other.

Conclusion

I like this configuration because it limits the risk of the whole services to go down.
Two separate hardware, using two different OSes, operated under distinct maintenance windows.
Did I already quote Stephen Covey[1]?

“Strength lies in differences, not in similarities.”

[1] Rhetorical question