WireGuard is a modern VPN protocol that is designed to be fast, secure, and easy to configure. In this note, I will show you how to set up a site-to-site VPN using WireGuard.

Install required packages:

apt install -qy wireguard wireguard-tools

Generate the private and public keys for the server:

mkdir -p /etc/wireguard/keys
wg genkey | tee /etc/wireguard/keys/wg-s2s.private.key | wg pubkey > /etc/wireguard/keys/wg-s2s.public.key
[Interface]
PrivateKey = <payload from /etc/wireguard/keys/wg-s2s.private.key>
ListenPort = 1024
Address = 169.254.20.1/32
SaveConfig = false

Add the peer configuration (another server):

[Peer]
PublicKey = <payload from another server>
AllowedIPs = 169.254.20.2/32
Endpoint = 192.0.2.1:1024
PersistentKeepalive = 20

Example configuration

In this example, let’s create connect from the office to AWS with 3 aviablity zones.

Create file: /etc/wireguard/wg-s2s.conf

[Interface]
PrivateKey = <payload from /etc/wireguard/keys/wg-s2s.private.key>
Address = 169.254.20.129/32
SaveConfig = false

# AWS us-west-2a
[Peer]
PublicKey = <public key from us-west-2a>
Endpoint = <EC2 public IP>:1024
PersistentKeepalive = 10
AllowedIPs = 168.254.20.1/32,172.30.0.0/22

# AWS us-west-2b
[Peer]
PublicKey = <public key from us-west-2b>
Endpoint = <EC2 public IP>:1024
PersistentKeepalive = 10
AllowedIPs = 168.254.20.2/32,172.30.4.0/22

# AWS us-west-2c
[Peer]
PublicKey = <public key from us-west-2c>
Endpoint = <EC2 public IP>:1024
PersistentKeepalive = 10
AllowedIPs = 168.254.20.3/32,172.30.8.0/22

After enable and start the WireGuard service:

systemctl enable wg-quick@wg-s2s.service
systemctl start wg-quick@wg-s2s.service

Example from AWS side

[Interface]
PrivateKey = <payload from /etc/wireguard/keys/wg-s2s.private.key>
Address = 168.254.20.1/32
ListenPort = 1024
SaveConfig = false

# office
[Peer]
PublicKey = <public key from office>
PersistentKeepalive = 10
AllowedIPs = 169.254.20.129/32,192.168.192.0/24

Small explanation

Interface:

  • PrivateKey - the private key of the server. Each server must have a unique private key.
  • ListenPort - the port that the server listens on. We need this only for the server with a public IP. (required at least one server with a public IP). If we have a servers with public IP from both sides, we can use the same port. (same port not required, but it’s easier to manage).
  • Address - the IP address of the server. Or we can use this address as link-local address.

Peer:

  • AllowedIPs - the list of IP addresses that are allowed to be routed through the WireGuard interface. We can use CIDR notation. Otherwise, the kernel drops the packets.
  • Endpoint - the public IP address of the peer. We need this only for the server with a public IP.

Enchanced Security

To enhance the security of the WireGuard VPN, we need to use PSK (Pre-Shared Key) for each peer.

Why need to have for each peer? We can easy rotate the PSK without changing the public/private keys (if we change public/private keys, we need to update all peers - it’s a pain with many peers).

Let’s create a PSK for the server:

wg genpsk > /etc/wireguard/keys/wg-s2s.psk.peer1.key

And add the PSK to the server configuration:

[Peer]
...
PresharedKey = <payload from /etc/wireguard/keys/wg-s2s.psk.peer1.key>

Dynamic routing

If we want to use dynamic routing, like BGP or Babel, we need to change the configuration of the WireGuard interface.

  1. We need to disable expose routing into kernel
  2. Allow all IPs to be routed through the WireGuard interface
[Interface]
...
Table = off

[Peer]
...
AllowedIPs = 0.0.0.0/0,::/0