focuses on the protocol itself and how it is different from others.
So, this post is a summary of my mental model of how WireGuard works,
plus a tutorial for setting up the server.
I assume that you have some knowledge of networking,
but aren't familiar with WireGuard.
I also assume Linux knowledge.
This guide should work generally, but I tested it on Arch Linux.
> **CIDR notation**:
>
> In this post, if you see an `/24` at the end of an IP address, that's [CIDR notation](https://en.m.wikipedia.org/wiki/Classless_Inter-Domain_Routing).
> `192.168.0.0/24` could be written as "any address that fits the pattern
> `192.168.0.*`". Because there are 32 bits in an IPv4 address, the `/24` at
> the end means that the first 24 bits (the first three bytes `192.168.0`) are
> fixed, and the remaining 8 bits can be anything. Also, a `/32` pattern
> matches only one address.
## what is a vpn?
A VPN (virtual private network) is often advertised to the average user as an
encrypted tunnel for their Internet connection.
Mostly, this is useful for changing your IP address.
However, this is not the full picture of what a VPN is.
In corporate networks, there are many important services that can't be exposed publicly,
which is why they're only available on an internal, private network.
Some off-site users, especially remote workers, need to access the internal network,
but from the outside Internet.
This is where a VPN can be useful: users can _virtually_ be on the private network,
as if they were on site.
Using a VPN is more secure than publicly exposing services,
as authentication allows fine-grained control over who can access the network.
For someone like me, VPNs are useful because I self-host private services.
Normally, I would need to expose them to the public Internet to access them.
Instead, I can access them solely through the local network or the VPN,
preventing strangers from even seeing the login pages of my services.
This reduces the attack surface and makes things more secure.
## wireguard setup
WireGuard is a relatively recent protocol for implementing VPNs.
It's shiny and new, and also has a slimmer codebase than older, more established protocols.
Let's go through the process of setting up a VPN to access an internal network.
The setup for tunneling all of a device's Internet traffic is similar, and I will explain it too later.
We will have a _Client_ device, and a _Server_ device.
The Server is in the internal network (let's say in the `192.168.0.0/24` subnet),
and the Client is outside of it.
There is also a publicly accessible domain `vpn.example.com` that resolves to the Server.
To recap, here is a diagram of what we're trying to do:
First, let's look at the `[Interface]` section, which contains information about the Server.
It has the private key, and also the UDP port that WireGuard listens on
(remember to open this in the firewall).
The address marked here is the Server's address.
Its subnet is completely different from the LAN the Server is actually on (`192.168.0.0/24`).
This is because we're creating a brand new private network (`10.0.0.0/24`) inside the VPN connection,
where our Client and Server will coexist.
> Note:
> `10.0.0.0/8`, `172.16.0.0/12` and `192.168.0.0/24` are all entirely reserved for private subnets, and you will not see them on the open Internet.
> To avoid collisions, the new VPN subnet should be different from the real LAN subnets for all peers.
> OpenVPN [recommends](https://openvpn.net/community-resources/numbering-private-subnets/) obscure subnets like `10.66.77.0/24`,
> which are equivalent to the middle of nowhere in IP address space.
There is also the `PostUp` and `PostDown` fields.
These are commands run in `bash` after starting and stopping the VPN Server.
I'm not going to go into the details, but I'll explain in general what they mean:
-`iptables -A` means to add a firewall rule when starting the VPN, and `-D` is to delete the rule when the VPN stops.
- The `%i` variable is part of `wg-quick`, and it expands to the VPN interface name (e.g. `wg0`).
- These rules in general allow the Server to _forward_ packets, while [_masquerading_](https://askubuntu.com/a/1295626).
Essentially, just as a device can access the Internet through a router, the Client accesses the internal network through the Server.
To do this, the Server will act like a router and perform [NAT](https://en.wikipedia.org/wiki/Network_address_translation).
-`-o eth0` means the internal network is accessed over the `eth0` interface.
Meanwhile, in the `[Peer]` section, we write the Client's public key,
which allows it to talk to the Server.
Our Client has an address of `10.0.0.2`, but
instead of an `Address` field, we use `AllowedIPs`.
These are the IP addresses that _can be routed to and from this peer_.
Here are some examples to clarify what that means:
- Let's say the Server wants to send a packet to `10.0.0.2`.
WireGuard sees that peer `xZGlY...` (the Client) has this IP in its allowlist,
so the Server sends the packet to the Client.
- The Server wants to send a packet to `archlinux.org (95.217.163.246)`.
This IP is not in the peer's allowlist, so it will not route the packet to the Client.
- The Client sends a packet to the Server, with the source being its IP address `10.0.0.2`.
The Server sees that this IP is in the allowlist, so it accepts the packet from the Client.
- The Client, who is now evil, decides to send a packet to the Server but impersonating another IP address: `10.0.0.69`.
This IP not being in the Server's allowlist, it rejects the packet.
In general, you can see that the `AllowedIPs` field is what determines where packets can and can't go.
By setting `AllowedIPs = 10.0.0.2`, the server knows that the Client only has control over packets directed to or from that address.
It is not allowed packets to or from any other IP.
This concept of pairing the allowlist with public keys to manage packet routing is called [_cryptokey routing_](https://www.wireguard.com/#cryptokey-routing) in WireGuard.
Also,
by default, Linux disables IP forwarding. To enable it, edit `/etc/sysctl.conf` and add/uncomment the line
```
net.ipv4.ip_forward = 1
```
and run
```
# sysctl -p
```
to load the new configuration.
If your VPN server is on the public internet,
be sure to have sane firewall rules before doing this.
> Note: If you use [UFW](https://wiki.archlinux.org/title/Uncomplicated_Firewall) as a firewall like me, note that it has its own `sysctl.conf`, which lives at `/etc/ufw/sysctl.conf`.