WireGuard Notes
After being first exposed to WireGuard, it took a while before its simple-yet-powerful nature started to dawn on me. By "simple" I mean simple from an end-user's perspective; the implementation, while supposedly a few orders of magnitude less hairy than e.g. OpenVPN or IPsec, isn't something that I can claim to have grasped. But the ascetic, no bells and whistles, do one thing well approach is something that resonates and got me to invest some time to play with the thing to see how it works in some common scenarios.
Getting packets to travel from A to B to C and back doesn't happen automagically with WireGuard. In fact there's
no magic going on behind the scenes to begin with, which can be A Good Thing™ or not, depending on cirumstances.
The only thing that WireGuard provides is making packets flow between the two ends of a link in a secure manner,
everything else is left to be taken care of by whatever means is appropriate. For setting things up there's the
wg program which helps with
key generation and with setting up the WireGuard interface, and further the
wg-quick script
which does a bit more and sets up the interfaces, peers and routes based on an INI-format file. The latter
also ships with a handy
systemd service
for running things permanently. These tools can be installed on Debian-like systems by saying
apt install wireguard-tools
. Getting the thing running on Raspberry Pis requires a few extra steps,
see here.
Before going into the example configuration, a refresher about configuring cryptokey routing. When peers are configured, the "allowed IPs" settings for the peers mean that:
- when a packet is sent to the WireGuard interface (e.g.
wg0
), the packet gets passed to the peer that has a prefix matching the packets destination address - which means that the prefixes can't overlap between the peers
- when a packet is received from a peer, if the peer has a prefix mathing the packet's source address, the packet is allowed in
- when either sending to or receiving from a peer, if no matching prefix is found for a packet, the packet is dropped
In other words, when sending packets, the list of allowed IPs behaves as a sort of routing table, and when receiving packets, the list of allowed IPs behaves as a sort of access control list.
Connecting two sites, both behind NAT
This example connects two sites that are both behind NAT, which requires that there's a publicly accesible host running in between:
+-----------+ +-----------+ +-----------+ | | | | | | | NAT +------+ WireGuard +-------+ Network 1 | | | | | | | +-----+-----+ +-----------+ +-----------+ | | | +-------------+ | | | +---------->+ example.com +<-----------+ | | | +-------------+ | | | +-----------+ +-----------+ +-----+-----+ | | | | | | | Network 2 +------+ WireGuard +-------+ NAT | | | | | | | +-----------+ +-----------+ +-----------+
Suppose that the public host's network is 10.0.0.0/24
,
Network 1 is 192.168.1.0/24
,
Network 2 is 172.16.1.0/24
,
and that the WireGuard network is 10.1.1.0/24
. And that all hosts
in both behind-NAT networks should see each other.
The WireGuard hosts on the behind-NAT networks connect to
example.com:51820
, which has the following configuration:
[Interface] Address = 10.1.1.1/32 ListenPort = 51820 PrivateKey = kKELbYxqmwHGUyjdHiVhQ/lzyiLep2kLgAocLF4CR3Q= [Peer] PublicKey = D+GcHTk8uRiggEj79IhbbsLWHSdZynYjUVPWcP8aJFg= AllowedIPs = 10.1.1.11/32,192.168.1.0/24 [Peer] PublicKey = up9LDZjYw8/LHH29ZQdp7Mg9bB+LIE7T4OsYLlEXLng= AllowedIPs = 10.1.1.12/32,172.16.1.0/24
WireGuard host on Network 1 (192.168.1.0/24
):
[Interface] Address = 10.1.1.11/32 PrivateKey = 4DQYFpL2kkVd/rjEYLTES8Ah6K2BMOrH504TXRQyv0E= Table = off PostUp = ip -4 route add 10.1.1.0/24 dev %i PostUp = ip -4 route add 172.16.1.0/24 dev %i [Peer] PublicKey = 2LbLqgg0hGjsQ+Y15l+mPhEtGN53Uhvzj8n9dpxVqDQ= AllowedIPs = 10.1.1.0/24,192.168.1.0/24,172.16.1.0/24 Endpoint = example.com:51820 PersistentKeepalive = 25
WireGuard host on Network 2 (172.16.1.0/24
):
[Interface] Address = 10.1.1.12/32 PrivateKey = CPZBHHLywkMqgW70MIgnvJRculKKGyYaBP7rIUJbpXs= Table = off PostUp = ip -4 route add 10.1.1.0/24 dev %i PostUp = ip -4 route add 192.168.1.0/24 dev %i [Peer] PublicKey = 2LbLqgg0hGjsQ+Y15l+mPhEtGN53Uhvzj8n9dpxVqDQ= AllowedIPs = 10.1.1.0/24,192.168.1.0/24,172.16.1.0/24 Endpoint = example.com:51820 PersistentKeepalive = 25
Routing Table
is off
and routes are manually added in
PostUp
because otherwise wg-quick
would set routes for the
local network's traffic which is in AllowedIPs
.
All three hosts should enable forwarding:
sysctl -w net.ipv4.ip_forward=1
In order to route traffic to WireGuard from other hosts in the networks, the hosts
need a route. If fiddling with the gateway isn't an option, the route needs to be set on each
separately, for example (assuming the WireGuard hosts on each network are .1.10
):
# Network 1 ip -4 route add 10.1.1.0/24 via 192.168.1.10 ip -4 route add 172.16.1.0/24 via 192.168.1.10 # Network 2 ip -4 route add 10.1.1.0/24 via 172.16.1.10 ip -4 route add 192.168.1.0/24 via 172.16.1.10
Then, supposing that each of the configurations was in /etc/wireguard/wg0.conf
,
one can say wg-quick up wg0
. To make the WireGuard configuration come up automatically,
the Systemd service should be enabled:
systemctl enable wg-quick@wg0.service systemctl daemon-reload systemctl start wg-quick@wg0.service
Categorised as: note