WireGuard configuration for FreeBSD 13.2+
FreeBSD 13.2 imported WireGuard into the base system, but so far the official documentation on it is limited only the man pages wg(4) and wg(8). The invasive wg-quick(8) script used in most examples wasn't imported breaking those. Despite this FreeBSD 13.2 has everything needed to comfortably use WireGuard except for the documentation needed to make it accessible to new users. This article is an attempt to to fix that.
How the parts fit together
Most existing examples configure everything about a WireGuard tunnel using a single WireGuard configuration file per tunnel interface processed by the wg-quick(8) script. Instead the setup described in this article uses the WireGuard configuration only for those parameters directly understood by wg setconf
. The remaining network interface configuration of the WireGuard tunnel interfaces is left to the existing FreeBSD rc.d scripts. WireGuard tunnel interfaces have to be explicitly created as they do not correspond to any physical network interface that can be discovered by enumerating the installed physical network interfaces. The recommended way to have the rc.d scripts create tunnel interfaces is to add them to (space separated) cloned_interfaces
list in /etc/rc.conf
. Variables in /etc/rc.conf
are used to have the netif
and routing
rc.d scripts configure the interface IP addresses, MTU, and static routes as required. Which variables are recognised by the base system rc.d scripts and a short description of their semantics can be looked up in the rc.conf(5) man page.
The /etc/rc.conf
configuration is a shell script sourced by the various rc.d scripts to obtain their configuration. As long as /etc/rc.conf
contains only variable assignments it can be queried and updated like a key-value store using sysrc(8). Getting too clever here will cause (a lot) more pain than gain over time. Tested for you sigh.
As part of the boot process FreeBSD runs its rc.d service scripts one after the other. For this article it's enough to know that devd is started before netif configures network interfaces allowing us to rely on devd(8) to load the rest of the WireGuard tunnel configuration on demand.
Prepare the system to load WireGuard configurations on demand.
Configure devd(8) to load WireGuard configuration files on demand by placing this devd.conf(5) configuration snippet in
/etc/devd/wireguard.conf
:notify 0 { match “system” “IFNET”; match “type” “LINK_UP”; media-type “unknown”;
action “. /etc/rc.subr . /etc/network.subr load_rc_config network if autoif $subsystem && [ -r /etc/wireguard/$subsystem.conf ] then grep -vE '^[[:space:]]*(Address|DNS|MTU|Table|PreUp|PostUp|PreDown|PostDown)[[:space:]]*=' /etc/wireguard/$subsystem.conf | wg setconf $subsystem /dev/stdin fi”; };
Restart devd(8) to apply the new configuration.
Create a
/etc/wireguard
directory to hold the WireGuard configuration files:install -d -m 700 -o root -g wheel /etc/wireguard
(only accessible by root).
Create your first WireGuard interface.
- Pick a free interface name. This article assumes WireGuard interfaces to be created under a name starting with “wg” followed by an index. The potential consequences of renaming network interfaces aren't covered in this article. The interface will be referred to as
$WG
. Runread WG
to have the shell perform the substitution for you. - Write the WireGuard tunnel configuration into
/etc/wireguard/${WG}.conf
.- Start with just a new private key:
(echo '[Interface]' && echo -n 'PrivateKey = ' && wg genkey) >/etc/wireguard/${WG}.conf
. - Write the rest of the configuration as needed:
${EDITOR:-vi} /etc/wireguard/${WG}.conf
. Tunnels without without at least one configured peer are of little use.
- Start with just a new private key:
- Configure the
$WG
interface in rc.conf(5).- Set the interface MTU:
sysrc "create_args_${WG}=1400"
(optional, defaults to 1420). - Assign a human friendly description to the interface:
sysrc "ifconfig_${WG}_descr=first_tunnel"
(optional). - Bring the interface up:
sysrc "ifconfig_${WG}=up"
(required). - Waste no time on futile IPv6 duplicate address detection:
sysrc "ifconfig_${WG}_ipv6=no_dad"
(required). - Configure the interface IPv6 address (and prefix length):
sysrc "ifconfig_${WG}_alias0=2001:DB8:1::1/64"
(required). - Configure the interface IPv4 address (and prefix length):
sysrc "ifconfig_${WG}_alias1=198.51.100.1/24"
(optional). - Add the tunnel to the list of cloned interfaces:
sysrc "cloned_interfaces+=${WG}"
(required).
- Set the interface MTU:
- Create the new interface without rebooting:
service netif start $WG
.
Inspect the result.
- Use
ifconfig -l | tr ' ' '\n' | grep $WG
to check if the interface was created. - Use
ifconfig $WG
to print the FreeBSD interface configuration. - Use
wg show $WG
to print the WireGuard tunnel state. An active session to a peer should have alatest handshake: …
below two minutes. - Use
wg showconf $WG
dump the running WireGuard interface configuration. The private key is omitted unless the command is executed as root. - Use
netstat -rn | grep $WG
to find routes going through the tunnel.
Mastodon: https://bsd.network/web/@crest