There are many tools that exist to configure Wireguard VPNs; some are simple, some complex; some are free, others are limited and exorbitant; and some may work, while others may not. These are mine. I wrote them to strike a specific balance with respect to complexity; I wanted something uncomplicated enough that I wouldn't have to do incredible amounts of setup and ad hoc work just to get a VPN running (so I intentionally left out automatic provisioning and config distribution), while also eliminating the tedious and error-prone work of manually copying configuration data from one file to another.
The tools operate on a JSON5 file; this format was chosen because JSON5 allows comments (and JSON doesn't, which is a stupid decision in my opinion) and also permits more liberal syntax without compromising integrity. This file is formatted as an array of objects (associative arrays); each object represents a host in the network. Annotated examples are below.
There are, at the moment, two tools in use:
keys.py
and files.py
.
Both take JSON5 input on STDIN,
and keys.py
sends JSON5 output to STDOUT.
After writing a config file for the first time,
or after adding new hosts,
pipe it to keys.py
to populate it with public/private keypairs.
This script is idempotent—that is, it will never modify hosts that already have
keypairs.
Once all hosts have keys, pipe the populated JSON5 configuration file to
files.py
.
For every host in the configuration file,
files.py
will write a WireGuard configuration to <sname>.conf
;
furthermore, for phones, files.py
will create a QR code with the configuration
at <sname>.svg
.
After these files are generated, it is your responsibility to distribute and
update them.
Good luck!
This script has, thus far, only been tested on Linux and FreeBSD, but there is
no inherent reason for it not to work on other systems.
My WireGuard configuration tools require the latest version of Python 3, along
with the below packages (I recommend to install them with conda
):
nacl
(forkeys.py
)json5
qrcode
(forfiles.py
)
A JSON5 configuration file must be an array of objects with the following keys; each object represents a host.
Key | Type | Description | Remarks |
---|---|---|---|
name |
String | The long name of the host. | Used to populate the friendly_name field used by the WireGuard Promethius exporter among others. Required. |
sname |
String | The short name of the host. | Gives its name to the WireGuard configs generated by files.py . Required. |
server |
Boolean | Whether or not this host is a server. | Required. |
primary |
Boolean | Whether or not this host is the primary server. | Required for servers. |
phone |
Boolean | Whether or not this host is running the WireGuard iOS or Android app. | files.py will generate QR codes for phones. Required. |
ips |
Array of IP/CIDR values | The IPs of a host. Servers automatically own the entire subnet; other hosts simply own one IP. | Required. |
hostname |
IP or DNS name | The hostname of a host. | Required for servers, optional for all others. |
port |
An integer between 0 and 65535, inclusive. | The port that WireGuard is to listen on. | Required if hostname is set; optional otherwise. Can be set when hostname is unset. |
addl_nets |
Array of IP/CIDR values (as strings) | Lists additional subnets handled by this host. | Optional. |
All hosts directly route to servers, and vice versa.
That is, they route traffic directly from one host to another over the public
Internet.
When non-server hosts send packets to each other, they must route them through
the primary server; needless to say, the primary must have an
appropriately-configured firewall.
Furthermore, packets sent to subnets listed in a host's addl_nets
key are
unconditionally routed thereto.
{
"name": "Expensive server",
"sname": "short_name",
"server": true,
"primary": true,
"phone": false,
"ips": [
"10.84.0.1/16",
"fdf9:167d:2a5a::1/112"
],
"hostname": "adams.example.com",
"port": 50000
},
{
// The spectral desktop has no fixed location and does not seem to exist,
// but it is kept on the VPN to appease the WireGods.
"name": "Spectral desktop",
"sname": "spectral",
"server": false,
"primary": false,
"phone": false,
"ips": [
"10.84.0.2/16",
"fdf9:167d:2a5a::2/112"
],
"hostname": "foo.bar.org",
"port": 50000
},
- WireGuard has terrible documentation, but is otherwise an excellent, reliable, and secure VPN system.
wg-quick
will take care of most WireGuard setup necessary, including the setup of thewg
n network interface and routing table stuff.- Configuring alternate routing (i.e. using an Ethernet connection for a particular subnet but falling back on WireGuard when not available) is possible, but extremely difficult. Woe betide those who try it.
- Phone apps work well, but will not accept configuration files that have
entries that they don't recognize (like
SaveConfig
). - Configuration files are pretty simple TOML/INI files. If I had known that, I would've used a library to write those files.
- There are many ways to bring up WireGuard interfaces on boot.
On Ubuntu, use
NetworkManager
; on Void, use thewireguard
service that comes with the WireGuard tools; on FreeBSD, put the config files in/etc/wireguard
and use a custom RC script.