WireGuard VPN site-to-site
Note:
This documentation has moved to a new home! Please update your bookmarks to the new URL for the up-to-date version of this page.
Another usual VPN configuration where one could deploy WireGuard is to connect two distinct networks over the internet. Here is a simplified diagram:
┌─────── WireGuard tunnel ──────┐
│ 10.10.9.0/31 │
│ │
10.10.9.0 wgA│ xx │wgB 10.10.9.1
┌─┴─┐ xxx xxxx ┌─┴─┐
alpha site │ │ext xx xx ext│ │ beta site
│ ├─── x x ───┤ │
10.10.10.0/24 │ │ xx xx │ │ 10.10.11.0/24
│ │ x x │ │
└─┬─┘ x x └─┬─┘
10.10.10.1│ xx x │10.10.11.1
...┌─────────┬────┘ xx xxx xx └───┬─────────┐...
│ │ xx xxxxx │ │
│ │ │ │
┌─┴─┐ ┌─┴─┐ public internet ┌─┴─┐ ┌─┴─┐
│ │ │ │ │ │ │ │
└───┘ └───┘ └───┘ └───┘
The goal here is to seamlessly integrate network alpha with network beta, so that systems on the alpha site can transparently access systems on the beta site, and vice-versa.
Such a setup has a few particular details:
- Both peers are likely to be always up and running.
- We can’t assume one side will always be the initiator, like the laptop in a coffee shop scenario.
- Because of the above, both peers should have a static endpoint, like a fixed IP address, or valid domain name.
- Since we are not assigning VPN IPs to all systems on each side, the VPN network here will be very small (a
/31
, which allows for two IPs) and only used for routing. The only systems with an IP in the VPN network are the gateways themselves. - There will be no NAT applied to traffic going over the WireGuard network. Therefore, the networks of both sites must be different and not overlap.
This is what an MTR (My Traceroute) report from a system in the beta network to an alpha system will look like:
ubuntu@b1:~$ mtr -n -r 10.10.10.230
Start: 2022-09-02T18:56:51+0000
HOST: b1 Loss% Snt Last Avg Best Wrst StDev
1.|-- 10.10.11.1 0.0% 10 0.1 0.1 0.1 0.2 0.0
2.|-- 10.10.9.0 0.0% 10 299.6 299.3 298.3 300.0 0.6
3.|-- 10.10.10.230 0.0% 10 299.1 299.1 298.0 300.2 0.6
Note:
Technically, a/31
Classless Inter-Domain Routing (CIDR) network has no usable IP addresses, since the first one is the network address, and the second (and last) one is the broadcast address. RFC 3021 allows for it, but if you encounter routing or other networking issues, switch to a/30
CIDR and its two valid host IPs.
Configure WireGuard
On the system that is the gateway for each site (that has internet connectivity), we start by installing WireGuard and generating the keys. For the alpha site:
$ sudo apt install wireguard
$ wg genkey | sudo tee /etc/wireguard/wgA.key
$ sudo cat /etc/wireguard/wgA.key | wg pubkey | sudo tee /etc/wireguard/wgA.pub
And the configuration on alpha will be:
[Interface]
PostUp = wg set %i private-key /etc/wireguard/%i.key
Address = 10.10.9.0/31
ListenPort = 51000
[Peer]
# beta site
PublicKey = <contents of /etc/wireguard/wgB.pub>
AllowedIPs = 10.10.11.0/24,10.10.9.0/31
Endpoint = <beta-gw-ip>:51000
On the gateway for the beta site we take similar steps:
$ sudo apt install wireguard
$ wg genkey | sudo tee /etc/wireguard/wgB.key
$ sudo cat /etc/wireguard/wgB.key | wg pubkey | sudo tee /etc/wireguard/wgB.pub
And create the corresponding configuration file for beta:
[Interface]
Address = 10.10.9.1/31
PostUp = wg set %i private-key /etc/wireguard/%i.key
ListenPort = 51000
[Peer]
# alpha site
PublicKey = <contents of /etc/wireguard/wgA.pub>
AllowedIPs = 10.10.10.0/24,10.10.9.0/31
Endpoint = <alpha-gw-ip>:51000
Important:
WireGuard is being set up on the gateways for these two networks. As such, there are no changes needed on individual hosts of each network, but keep in mind that the WireGuard tunneling and encryption is only happening between the alpha and beta gateways, and NOT between the hosts of each network.
Bring the interfaces up
Since this VPN is permanent between static sites, it’s best to use the systemd unit file for wg-quick
to bring the interfaces up and control them in general. In particular, we want them to be brought up automatically on reboot events.
On alpha:
$ sudo systemctl enable --now wg-quick@wgA
And similarly on beta:
$ sudo systemctl enable --now wg-quick@wgB
This both enables the interface on reboot, and starts it right away.
Firewall and routing
Both gateways probably already have some routing and firewall rules. These might need changes depending on how they are set up.
The individual hosts on each network won’t need any changes regarding the remote alpha or beta networks, because they will just send that traffic to the default gateway (as any other non-local traffic), which knows how to route it because of the routes that wg-quick
added.
In the configuration we did so far, there have been no restrictions in place, so traffic between both sites flows without impediments.
In general, what needs to be done or checked is:
-
Make sure both gateways can contact each other on the specified endpoint addresses and UDP port. In the case of this example, that is port
51000
. For extra security, create a firewall rule that only allows each peer to contact this port, instead of the Internet at large. -
Do NOT masquerade or NAT the traffic coming from the internal network and going out via the WireGuard interface towards the other site. This is purely routed traffic.
-
There shouldn’t be any routing changes needed on the gateways, since
wg-quick
takes care of adding the route for the remote site, but do check the routing table to see if it makes sense (ip route
andip route | grep wg
are a good start). -
You may have to create new firewall rules if you need to restrict traffic between the alpha and beta networks.
For example, if you want to prevent SSH between the sites, you could add a firewall rule like this one to alpha:
$ sudo iptables -A FORWARD -i wgA -p tcp --dport 22 -j REJECT
And similarly on beta:
$ sudo iptables -A FORWARD -i wgB -p tcp --dport 22 -j REJECT
You can add these as
PostUp
actions in the WireGuard interface config. Just don’t forget to remove them in the correspondingPreDown
hook, or you will end up with multiple rules.