Building a WireGuard Jail with the FreeBSD's Standard Tools


Recently, I had an opportunity to build a WireGuard jail on a FreeBSD 12.1 host.

As it was really quick and easy to setup and it has been working completely fine for a month, I’d like to share my experience with anyone interested in this topic.

I have achieved my goal by roughly taking the following steps.

Create a Jail Filesystem

As usual, I created a jail’s filesystem from a template ZFS snapshot.

$ sudo sh -c "zfs send zroot/vm/tmpl/12.1@p3 | zfs receive zroot/vm/wg"

Refer to this article for details on how to create a template.

Configure Internal Network

I created a bridge (bridge0) for the internal jail network (192.168.20.0/24) and an epair (epair0a/b), attached the ‘b’ side of the epair to the bridge and assigned an IP address to the bridge.
Adding the IP address to the bridge0 makes it the third routed interface on the host and the address becomes the default gateway for the jail.

[/etc/rc.conf]

cloned_interfaces="bridge0 epair0"
ifconfig_bridge0="inet 192.168.20.1/24 addm epair0b up"
ifconfig_epair0b="up"

Network Topology

In the above example, I use the bridge so that I can add more jails to the internal subnet in the future, as shown in the following diagram.
Two jails connected to the bridge0

But if you need only a single jail, it’s not necessary to use the bridge.
You can connect the jail to the host directly with the epair by using the following configuration.
With this config, epair0b becomes the host’s third routed interface.

[/etc/rc.conf]

cloned_interfaces="epair0"
ifconfig_epair0b="inet 192.168.20.1/24"

A jail directly connected with an epair

Adjust Firewall Rules

Next I edited /etc/pf.conf to adjust firewall rules for the jail and WireGuard subnets.

Configure the Jail

On the Host

On the Jail’s Filesystem

Apply Configurations and Start the Jail

Once the jail configurations on the host and the jail’s filesystem were finished, I ran the following commands to actually build the internal network, reload the firewall rules and start the jail.

$ sudo service netif cloneup
$ sudo service pf reload
$ sudo service jail start

Install and Configure WireGuard

On the Jail

Next I used pkg‘s -j option to install WireGuard and libqrencode on the jail. The latter will be used for generating a QR code to pass WireGuard configuration to Android device.

$ sudo pkg -j wg install wireguard libqrencode

Then, finally I logged in to the jail and configured WireGuard. See this article for more details.

$ sudo jexec -l wg
<root@wg># cd /usr/local/etc/wireguard/
<root@wg># wg genkey | tee wg.private | wg pubkey > wg.public
<root@wg># wg genkey | tee android.private | wg pubkey > android.public
<root@wg># vi wg0.conf
<root@wg># chmod 400 *.private *.conf
<root@wg># sysrc wireguard_enable="YES"
<root@wg># sysrc wireguard_interfaces="wg0"

[wg0.conf]

[Interface]
Address = 192.168.250.1/32
PrivateKey = <content of wg.private>
ListenPort = 51820
   
[Peer]
PublicKey = <content of android.public>
AllowedIPs = 192.168.250.2/32

[android.conf]

[Interface]
Address = 192.168.250.2/32
PrivateKey = <content of android.private>
DNS = 192.168.10.1
   
[Peer]
PublicKey = <content of wg.public>
AllowedIPs = 0.0.0.0/0,::/0
Endpoint = wg.example.com:51820

On the Android Device

Install WireGuard App from Google Play.
https://play.google.com/store/apps/details?id=com.wireguard.android

Then configure Android app using a QR code generated by qrencode. See this article for more details.

<root@wg># qrencode -t ansi < android.conf

References

Revision History