The missing WireGuard integration into rc.d for FreeBSD 13.2

I want my cake and eat it too.

The configuration described in the last post works, but lacks the comfort the wg-quick(8) script brings, but wg-quick(8) doesn't integrate well with FreeBSD's existing rc.d scripts. To get the best of both worlds I wrote the “missing” WireGuard rc.d script that handles the basic wg-quick(8) features (inner tunnel addresses, DNS configuration using resolvconf(8), PreUp/PostUp/PreDown/PostDown hooks, MTU) and auto-detects the presence of WireGuard configurations in /etc/wireguard (at least by default). It also makes an effort to clean up after failures instead of leaking partially configured network interfaces.

The rc.d script lets the kernel pick the next available unit number for a WireGuard tunnel interface and renames the interface in a single ifconfig(8) invocation like this: ifconfig wg create name $name instead of asking for a specific unit number like this ifconfig wg$unit create. The rc.d script runs after netif allowing users to pre-reserve specific unit numbers by adding them to the cloned_interfaces rc.conf variable.

With all the error recovery code and support for verbose logging, it has the dubious distinction of being longer than any of the rc.d scripts shipped with FreeBSD 13.2.

The resulting WireGuard rc.d script written in FreeBSD sh(1) is available here. Please read it before feeding it to your root shells.

TL;DR: I love copy & paste and blindly trust random people on the Internet.

# Download the WireGuard rc.d script into /tmp.
fetch -o /tmp/

# Install the WireGuard rc.d script into the /etc/rc.d directory.
install -S -m 555 -o root -g wheel /tmp/ /etc/rc.d/wireguard

# Delete the temporary file.
rm /tmp/

# Create the WireGuard configuration directory.
install -d -m 750 -o root -g wheel /etc/wireguard

# Configure a WireGuard interface.
$EDITOR /etc/wireguard/wg-foo.conf

# Start the WireGuard rc.d service.
service wireguard start

Here is an example WireGuard configuration as starting point:

[Interface]           # wg-foo
PrivateKey            = cElrYhZSY8znrhGdn5c/oXrTvuesYJnVsPBXR+56snc=
# PublicKey           = hIrvK/JVH3+CyPmhvh2w/+eN00KfSN+Fro/t4U592h8=
ListenPort            = 51820
MTU                   = 1400
DNS                   =,
DNS                   =
Address               = 2001:db8::1/64,
PreUp                 = logger -t "wireguard" -- "PreUp    : %i"
PostUp                = logger -t "wireguard" -- "PostUp   : %i"
PreDown               = logger -t "wireguard" -- "PreDown  : %i"
PostDown              = logger -t "wireguard" -- "PostDown : %i"

[Peer]                # Restrict AllowedIPs for point to multi-point.
Endpoint	      =
PublicKey             = +lvewJa4CBEUlCOXLv0D+vXFB5mYQTzY6iRmz0zI6zg=
PresharedKey          = aWVYfsvLR1egBz4zPlHPy+UqgkZAAxhjkjEdwDcArAM=
AllowedIPs            = ::0/0,
PersistentKeepalive   = 25