Table of content
  1. VPN as exit point
    1. Selecting a VPN provider
    2. Configuration
  2. Incoming VPN
    1. Server configuration
    2. Generating device configuration
  3. FAQ

At least FreeBSD 13.0 is required

VPN as exit point

Selecting a VPN provider

Very few VPN providers allow to use WireGuard with a manual configuration, they usually force you to use their own binary utilities. One exception (I haven’t looked for others) is Mullvad, which allows you to download raw configuration file.

Further more it give you a web API to check the state of your connection:

Mullvad check api
1
2
3
curl https://am.i.mullvad.net/connected   # Plain text connection status
curl https://am.i.mullvad.net/ip          # IP used for connection
curl https://am.i.mullvad.net/json        # Full connection description

Configuration

Create VPN interface vpn0:

rc.conf
1
2
3
cloned_interfaces="wg0"             # Clone wireguard interface
create_args_wg0="group vpn fib 1"   # Associate wg0 to fib 1 and group vpn
ifconfig_wg0_name="vpn0"            # Rename wg0 to vpn0

Now we need to generate a key pair that will be used for requesting a configuration for our device.

Generate private/public key pair
1
wg genkey | tee device.key | wg pubkey > device.pubkey

Once the configuration information has been retrieved, it will be used to configure FreeBSD startup scripts.

In the case of Mullvad, the configuration can be generated here:

And you will get something like:

1
2
3
4
5
6
7
8
9
[Interface]
PrivateKey = ...........censored.private.key.............
Address = 10.65.247.214/32,fc00:bbbb:bbbb:bb01::2:f7d5/128
DNS = 193.138.218.74

[Peer]
PublicKey = ............censored.public.key.............
AllowedIPs = 0.0.0.0/0,::0/0
Endpoint = 193.138.218.83:51820

Note that the configuration is generated such that:

  • IPv4 and IPv6 assigned addresses are tight to the interface key
  • Each endpoint address have a different key

The Address and DNS fields are not used/understood by wg setconf, and will need to be stripped down from the configuration for later use with it, using for example:

1
grep -Ev '^(DNS|Address)' > /etc/wireguard/vpn0.conf

These removed fields will need to be used in other configuration parts:

Address
will be used directly by ifconfig in /etc/rc.conf
DNS
can be used in resolv.conf to rely on the VPN provided DNS server to avoid DNS leak to your ISP… but if you are already using your own DNS that can be unnecessary

Interface vpn0 is initialized with the IP addresses that have been allocated for it on the VPN server side:

rc.conf
1
2
3
ifconfig_vpn0_descr="WireGuard VPN interface"
ifconfig_vpn0="inet 10.65.247.214/32"
ifconfig_vpn0_ipv6="inet6 fc00:bbbb:bbbb:bb01::2:f7d5/128"

The directive defaultif is not supported by the interface, it will not be possible to use it to indicate that we want to be the route by default if none are present. So we need to add a default route ourselves:

rc.conf
1
2
3
4
5
# Define the route by default on the fib 1 routing table as being our vpn
route_vpn0_default="-iface vpn0 -fib 1"

# Adding vpn0_default to the list of static routes
static_routes="... vpn0_default ..."

As FreeBSD integration with WireGuard has been delayed to 13.1 release, we will need to rely on /etc/rc.local and /etc/rc.shutdown.local to perform additional configuration steps that can’t be performed yet in /etc/rc.conf:

When using wg setconf, only the following fields are allowed in the configuration file:

Section Fields
Interface PrivateKey, ListenPort, FwMark
Peer PublicKey, PresharedKey, AllowedIPs, Endpoint, PersistentKeepalive
/etc/rc.local
1
2
# Set initiale interface configuration
/usr/local/bin/wg setconf vpn0 /etc/wireguard/vpn0.conf
/etc/rc.shutdown.local
1
2
# Synchronize interface configuration on shutdown
/usr/local/bin/wg syncconf vpn0 /etc/wireguard/vpn0.conf

Incoming VPN

Server configuration

Generate private/public key pair
1
wg genkey | tee server.key | wg pubkey > server.pubkey
1
2
3
4
5
$ cat server.key
sIauUbKlL6/KK4VD8LgiLfoQcG2ppzQeojtXeoFz/XY=

$ cat server.pubkey
mQ45752zydpryUITEevyPSOsqdC4M2+QWoQhGIX4jVY=
/etc/rc.local
1
2
3
4
# Incoming VPN
wg set vpn0                             \
   listen-port 51820                    \
   private-key /etc/wireguard/server.key

If you have firewall, you will need to tailor the following configuration fragment to your need:

/etc/pf.conf
1
2
3
4
5
6
scrub in on vpn0 all      # Reassemble fragments to avoid ambiguities
antispoof quick for vpn0  # Basic antispoofing rules
pass in on vpn0           # Allows incoming traffic

# If being a NAT and you want to give access to other network ressources
nat pass on wan0 inet from (vpn0:network) to any -> wan0

Generating device configuration

This will need to be done for each device that is allowed to connect to the VPN

  1. Create a key pair

    Generate private/public key pair
    1
    
    wg genkey | tee device.key | wg pubkey > device.pubkey
    
    1
    2
    3
    4
    5
    
    $ cat device.key
    CJ4r+v57yCwRfDR4uYqa7R8RUQQ9fe33DDGAyOcpFUI=
    
    $ cat device.pubkey
    VQIfnjUiX4RJzg+6mx6KgYEZu9UtouKEUrWpfLUwQQ0=
    
  2. Create the configuration file. Here the peer is the server to which we will connect to establish the VPN connection, and we will allow routing all the traffic to the VPN (look for: 0.0.0.0/0,::0/0)

    `device`.conf
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    [Interface]
    PrivateKey = CJ4r+v57yCwRfDR4uYqa7R8RUQQ9fe33DDGAyOcpFUI=
    Address = 192.168.110.2/32
    DNS = 192.168.1.5
    
    [Peer]
    PublicKey = gMvXaaghDj74eBjjnvX2Qy8vyv5uul1cpP7pzRBHcTw=
    AllowedIPs = 0.0.0.0/0,::0/0
    Endpoint = 1.2.3.4:51820
    
  3. Generate a QR-code (optional).

    QR-code allows easy integration with android WireGuard application, which can be found on:

    Generate QR-code from config file
    1
    
    qrencode -t ansiutf8 < device.conf
    
  4. Add device peer to the server.

    You usually want to restrict the allowed-ips to the same set of addresses defined in Address in the client configuration file.

    Adding peer to the server side
    1
    2
    
    wg set vpn0 peer VQIfnjUiX4RJzg+6mx6KgYEZu9UtouKEUrWpfLUwQQ0=     \
                allowed-ips 192.168.110.2/32
    

FAQ

Generate private/public key pair
1
wg genkey | tee device.key | wg pubkey > device.pubkey