2024-12-01 • 4 min
How to Create a VPN with WireGuard
This guide will show you how to configure a VPN with WireGuard on a Linux server.
Configuration Directory
Create the /etc/wireguard
directory and set up the permissions so only the root user can access its contents:
mkdir -p /etc/wireguard
chown -R root:root /etc/wireguard
chmod -R 700 /etc/wireguard
It’s a good idea to also set the mask so new files are created with the correct permissions by default:
umask 077
Note: files won’t be created with the execute permission set by default, even with the mask.
Directory structure
There is no right or wrong way to struture the configuration directoy, but this is how I like to do it. Don’t worry about the key files for now, we’ll create them in the next section.
/etc/wireguard
├── clients // Client config files
│ ├── client1.conf
│ └── client2.conf
├── keys // Client and server keys
│ ├── client1
│ │ ├── client1 // Private key
│ │ ├── client1.pub // Public key
│ │ └── server-client1.psk // Pre-shared key
│ ├── client2
│ │ ├── client2
│ │ ├── client2.pub
│ │ └── server-client2.psk
│ └── server
│ ├── server
│ └── server.pub
└── wg0.conf // Server config file
I like to keep a copy of the clients’ config files and private keys in the server, but it’s not necessary. It might even be a horrible idea, depending on your circumstances.
Keys
You’ll need to create a key pair for each host in your VPN, including the server:
wg genkey > server # Private key
wg pubkey < server > server.pub # Public key
Additionally, for each client, you’ll need to create a pre-shared key that both the client and server must know:
wg genpsk > server-client1.psk
As one would expect, the server must also know all of the clients’ public keys. Each client must also know the server’s public key. Whether to keep a copy of each client’s private key stored in /etc/wireguard
(as shown in the example above) is up to you.
Configuration Files
WireGuard can be configured through plain text files, which contain an [Interface]
block and one or multiple [Peer]
blocks.
Server
The server has a [Peer]
block for every client and redirects VPN traffic to its local network:
[Interface]
Address = 172.19.0.1/16
ListenPort = 51820
PrivateKey = <SERVER_PRIVATE_KEY>
# VPN to Local Network Redirection
# Substitute "eth0" with the internet-facing interface
PostUp = iptables -A FORWARD -i %i -j ACCEPT
PostUp = iptables -A FORWARD -o %i -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT
PostDown = iptables -D FORWARD -o %i -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
# client1
PublicKey = <CLIENT1_PUBLIC_KEY>
PresharedKey = <SERVER_CLIENT1_PRESHARED_KEY>
AllowedIPs = 172.19.0.2/32
[Peer]
# client2
PublicKey = <CLIENT2_PUBLIC_KEY>
PresharedKey = <SERVER_CLIENT2_PRESHARED_KEY>
AllowedIPs = 172.19.0.3/32
Client
Clients only need to have the server as a [Peer]
, since communication between clients goes through it.
[Interface]
Address = 172.19.0.2/16
ListenPort = 51820
PrivateKey = <CLIENT1_PRIVATE_KEY>
DNS = 192.168.218.1
[Peer]
# server
PublicKey = <SERVER_PUBLIC_KEY>
PresharedKey = <SERVER_CLIENT1_PRESHARED_KEY>
Endpoint = <SERVER_IP_OR_DOMAIN_NAME>:<LISTEN_PORT>
AllowedIPs = 0.0.0.0/0, ::/0
The optional DNS
parameter in the [Interface]
block can be used to make the router in the server’s local network handle DNS queries. This is useful if you want to access other hosts on the server’s local network using their hostnames instead of their IP addresses.
In this case the AllowedIPs
parameter tunnels all traffic through the VPN. If you’re only using the VPN to access, say, your home’s LAN remotely and you don’t want your internet traffic going through your home’s router, change it to something like this:
AllowedIPs = 172.19.0.0/16, 192.168.218.0/24
Note: you can have multiple config files in the same client if to support both behaviors
An easy way to copy a configuration file to a client device is through a QR Code created with the qrencode
utility:
qrencode -t ansiutf8 -r /etc/wireguard/clients/client1.conf
How to start the VPN
If your server config file is called wg0.conf
, you can start or stop the VPN with wg-quick
:
wg-quick up wg0
wg-quick down wg0
Systemd Service
If you want to start WireGuard automatically, you can also create a Systemd service:
systemctl enable --now [email protected]
Tips
Now you should have a working VPN, but here are some additional tips to improve the setup.
How to Reach VPN Clients from the Server’s Local Network
We’ve configured the server to redirect traffic so that other hosts in the same local network as the server are reachable through the VPN.
If you want those local hosts to also be able to reach VPN clients, create a routing rule in your router to redirect traffic directed to addresses in the VPN network through the VPN server.
If that’s not an option or you don’t want to make the routing rule at the router level, you can create it in Linux for a single host like this:
ip route add 172.19.0.0/16 via <VPN_SERVER_LOCAL_IP> dev <INTERFACE_NAME>
How to choose the VPN’s Network Address
Generally, you want to choose an uncommon network address to minimize the chances of overlap. For example, if you were to choose 192.168.0.0/24
you might find that a VPN client at 192.168.0.2
is unreachable if you’re connected to a LAN that has another host in the same address.
In the configuration examples, we’ve used 172.19.0.0/16
, which should be “weird” enough to avoid overlap in most cases.
Firewall
Since the server’s WireGuard port is exposed to the internet, it’s a good idea to configure a firewall. Here’s an example UFW config to get you started:
ufw enable
ufw allow from 192.168.218.0/24
ufw allow from 172.19.0.0/16
ufw limit proto udp from any to any port 51820