WireGuard Logo

Virtual Private Network

WireGuard is an extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography. It aims to be faster, simpler, leaner, and more useful than IPSec, while avoiding the massive headache. It intends to be considerably more performant than OpenVPN. WireGuard is designed as a general purpose VPN for running on embedded interfaces and super computers alike, fit for many different circumstances.

Warning

As of this writing (Dec. 2017) the WireGuard code ..

  • is pre-release software.
  • has to be considered as experimental and not yet complete.
  • has not undergone proper degrees of security auditing.
  • is still subject to change.
  • may contain security vulnerabilities which would not be eligible for CVEs.

Usage Scenario

Several of our internal services have either no built-in support for encrypted communications (Redis) or its hard to get it right (e.g. MariaDB, Memcached).

Others may provide additional features or caches or APIs (e.g. PowerDNS REST API, Postfix lookup tables, status queries) which are meant to be used on the same machine or inside a local network.

With the WireGuard VPN we can let them talk to each other across the globe without disclosing any data to outsiders.

Server-1                     Server-2
--------                     --------
public IP <--- Internet ---> public IP
    |                            |
    |                            |
WireGuard                     WireGuard
    |                            |
    |                            |
private IP <----- VPN -----> private IP
    |                            |
    |                            |
 MariaDB                      MariaDB
 Redis                        Redis
 Postfix                      Postfix
 Rspamd                       Rspamd

Network Design

Networks, subnets, port, and corresponding access rules and rights are already complex and get more complex, the more servers and services are added. VPN add another layer on top of all that and changes all all these already hard to grasp parts. Therefore a clear design concept is needed for reference as we go one.

Providers

Make a list of all providers (hosting companies, co-location data-centers, friends, family members, employers or other companies who agreed to host a device on there premises for you). Decide on a abbreviation for each to use throughout the design.

You can find out the provider by using the whois command of any public IP address:

$ whois 213.133.107.227

Some examples:

Provider Short Name
Hetzner Online AG heztner
OVH ovh
Digital Ocean do
Linode linode
Rackspace rack
Your Home home
Your Office office
Mothers House mama

Locations

Make a list of all geographical or physical remote locations that have one or more servers running. Most providers have their own naming.

Some examples:

Location Short Name
San Francisco SFO
New York City NYC
Toronto TOR
Berlin BER
London LON
Amsterdam AMS
Frankfurt FRA
Singapore SGP
Bangalore BLR

Public IPs and Subnets

You get these normally from your provider and they are location based. Nowadays you should get an IPv6 as well or change your provider otherwise.

In the best case you get a subnet, maybe you get additional IPs for a price.

List the subnet with their net-maskm which detrermines the size and number of IPs. A single IPv4 host has a net-mask of /32.

Some examples:

Provider Location IPv4 Subnet IPv6 Subnet
hetzner SFO 185.73.22.8/32 n/a
rack LON 212.51.156.17/32 2a02:168:4a01::/64
roller PHO 208.79.240.0/22 2607:fe70::/32

Define Private Subnets

For each location we define a private subnet from the range private network address spaces set by by RFC 1918.

See Private Network on Wikipedia:

Network Address Net Mask Prefix
10.0.0.0 255.0.0.0 10/8
172.16.0.0 255.240..0.0 172.16/12
192.168.0.0 255.255.0.0.0 192.168/16
fd00::/48 n/a fd00::/48

First we define a global private subnet out of one of the private address spaces:

$ echo 172.$((RANDOM%16+16)).0.0/24
172.27.0.0/24
Global IPv4 Subnet Netmask Prefix
172.27.0.0 255.255.0.0 172.27.0.0/16

Next we define a /24 subnet out of our global private subnet for each location:

$ echo hetzner-sfo 172.27.$((RANDOM%255+16)).0/24
$ echo rack-lon 172.27.$((RANDOM%255+16)).0/24
$ echo rollernet-pho 172.27.$((RANDOM%255+16)).0/24
Provider Location Local IPv4 Subnet Netmask Prefix
hetzner SFO 172.27.88.0 255.255.255.0 172.27.18.0/24
rack LON 172.27.126.0 255.255.255.0 172.27.25.0/24
roller PHO 172.27.74.0 255.255.255.0 172.27.22.0/24

For the IPv6 subnets we can use the online tool IPv6 private address range generator.

It will create a random global ID and subnet IDs out of the unique local address (ULA) block fd00::/8.

Global ID c1d89eb128
Global IPv6 Subnet Prefix
fdc1:d89e:b128::/48 fdc1:d89e:b128::/48

Repeat for every location, by providing the same global ID to generate a /64 subnet for each.

https://www.ultratools.com/tools/rangeGeneratorResult?globalId=c1d89eb128&subnetId=

Provider Location Subnet ID Local IPv6 Subnet
hetzner SFO 13a6 fdc1:d89e:b128:13a6::/64
rack LON 2615 fdc1:d89e:b128:2615::/64
roller PHO 41c5 fdc1:d89e:b128:41c5::/64

Define the Tunnel Subnet

To glue all our locations subnets together we need another one. The tunnel subnets will connect all the VPN gateways together.

Traditionally something out of the 10/8 block is used. This makes it easy to distinguish from the 172.16/12 block addresses:

$ echo 10.$((RANDOM%255+16)).$((RANDOM%255+16)).0/24
10.195.171.0/24

For IPv6 address of the tunnel subnet we define an additional subnet ID.

Global ID c1d89eb128
Subnet ID 6a04

Combined IPv4 and IPv6 together it may look like the following:

Provider Location IPv4 Subnet IPv6 Subnet
n/a Global 172.27.0.0/16 fdc1:d89e:b128::/48
hetzner SFO 172.27.88.0/24 fdc1:d89e:b128:13a6::/64
rack LON 172.27.126.0/24 fdc1:d89e:b128:2615::/64
roller PHO 172.27.74.0/24 fdc1:d89e:b128:41c5::/64
vpn n/a 10.195.171.0/24 fdc1:d89e:b128:6a04::/64

Register a Domain

Register a domain for where all your servers reside in. It doesn’t matter if it is the same domain at which our public services like homepage or mail are reachable, as we will use virtual domains hosting anyway.

The important thing is, that all subnets with their sub-domains and all servers with they host-names reside under your one single domain name which we control.

That way we can relay on trust between them based on DNS information secured by DNSSEC, which greatly simplifies things in many areas (e.g. trusting SSH servers keys).

Domain Registrar
example.net name.com

Define Sub-domains

The sub-domains allow us to quickly assert where any host or service resides. It slow helps with the definitions of access controls, mail routing and other areas. These are intended to help us internally, they are not necessarily visible to outside visitors or users of our services. We still stick to simple host.domain.tld or service.domain.tld for those.

Provider Subdomains:

Provider Sub-Domain
hetzner heztner.example.net
rack rack.example.net
roller roller.example.net

Provider Location Subdomains:

Provider Location Public Sub-Domain
hetzner SFO sfo.heztner.example.net
rack LON lon.rack.example.net
roller PHO pho.roller.example.net

Define Host Names

Some examples:

Provider Location FQ Domain Name Public IPv4 Public IPv6
hetzner SFO wg.sfo.hetzner.example.net 185.73.22.8  
rack LON wg.tor.rack.example.net 212.51.156.17 2a02:168:4a01::1
roller PHO wg.lon.roller.example.net 208.79.240.1 2607:fe70::1

Define Port Number

Also we need a UDP port number where our VPN hosts will listen for incoming connections:

$ echo $((1024 + RANDOM % 65535))
33541

Software Installation

Om each host:

$ sudo add-apt-repository ppa:wireguard/wireguard
$ sudo apt update
$ sudo apt install wireguard-dkms wireguard-tools
$ sudo touch /etc/wireguard/.reload-module-on-update

Key Generation

On each host.

Create a private key:

$ cd /etc/wireguard
$ umask 077
$ wg genkey > privatekey

Create the public key:

$ wg pubkey < privatekey > publickey

Create public and private keys:

$ wg genkey | tee privatekey | wg pubkey > publickey

Configurations

Create the file /etc/wireguard/wg0.conf: on each host:

Hetzner - San Francisco

Public IPv4 Address 185.73.22.8/32
Public IPv6 Address n/a
Tunnel IPv4 Address 10.195.171.88/24
Tunnel IPv6 Address fdc1:d89e:b128:6a04::13a6/64
Private IPv4 Subnet 172.27.88.0/24
Private IPv6 Subnet fdc1:d89e:b128:13a6::/64
; WireGuard Configuration for example.net
; Hetzner, San Francisco

[Interface]
PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=
ListenPort = 33541
Address = 10.195.171.88/24, fdc1:d89e:b128:6a04::2615/64

[Peer]
; Rackspace, London
PublicKey = TrMvSoP4jYQlY6RIzBgbssQqY3vxI2Pi+y71lOWWXX0=
Endpoint = wg.tor.rack.example.net:33541
AllowedIPs = 10.195.171.126/32, fdc1:d89e:b128:6a04::2615/128
AllowedIPs = 172.27.126.0/24, fdc1:d89e:b128:2615::/64

[Peer]
; Rollnet, Phoenix
PublicKey = gN65BkIKy1eCE9pP1wdc8ROUtkHLF2PfAqYdyYBz6EA=
Endpoint = wg.lon.roller.example.net:33541
AllowedIPs = 10.195.171.74/32, fdc1:d89e:b128:6a04::41c5/128
AllowedIPs = 172.27.74.0/24, fdc1:d89e:b128:41c5::/64

Rackspace - London

; WireGuard Configuration for example.net
; Rackspace, London

[Interface]
PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=
ListenPort = 33541
Address = 10.195.171.126/24, fdc1:d89e:b128:6a04::2615/64

[Peer]
; Hetzner, San Francisco
PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=
Endpoint = wg.sfo.hetzner.example.net:33541
AllowedIPs = 10.195.171.88/32, fdc1:d89e:b128:6a04::2615/128
AllowedIPs = 172.27.88.0/24, fdc1:d89e:b128:13a6::/64

[Peer]
; Rollnet, Phoenix
PublicKey = gN65BkIKy1eCE9pP1wdc8ROUtkHLF2PfAqYdyYBz6EA=
Endpoint = wg.lon.roller.example.net:33541
AllowedIPs = 10.195.171.74/32, fdc1:d89e:b128:6a04::41c5/128
AllowedIPs = 172.27.74.0/24, fdc1:d89e:b128:41c5::/64

Rollnet - Phoenix

; WireGuard Configuration for example.net
; Rollnet, Phoenix

[Interface]
PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=
ListenPort = 33541
Address = 10.195.171.74/24, fdc1:d89e:b128:6a04::41c5/64

[Peer]
; Hetzner, San Francisco
PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=
Endpoint = wg.sfo.hetzner.example.net:33541
AllowedIPs = 10.195.171.88/32, fdc1:d89e:b128:6a04::2615/128
AllowedIPs = 172.27.88.0/24, fdc1:d89e:b128:13a6::/64

[Peer]
; Rollnet, Phoenix
PublicKey = gN65BkIKy1eCE9pP1wdc8ROUtkHLF2PfAqYdyYBz6EA=
Endpoint = wg.lon.roller.example.net:33541
AllowedIPs = 10.195.171.74/32, fdc1:d89e:b128:6a04::41c5/128
AllowedIPs = 172.27.74.0/24, fdc1:d89e:b128:41c5::/64

Open Firewall

Allow Incoming WireGuard Connections

On all hosts:

$ sudo ufw allow 33541/udp

Allow Private Subnets

$ sudo ufw allow in on wg0

Start

sudo systemctl start wg-quick@wg0