wireguard VPN server

Wireguard is a tool for creating VPNs, it’s modern, secure and performant. Let’s see how to create a VPN server and send all our traffic through it.
prerequisites
We’ll need a server with at lease these specs:
- 1 core
- 512mb of RAM (it works even with less than that, but let’s try to be reasonable)
- 5GB of storage
- bandwidth!
- linux with a recent kernel
Pay attention to the available bandwidth: if you’re so lucky to have a fast internet connection, let’s say >100 mbit, we need to have at least the same bandwidth on the server.
As of today, we can rent a VPN with these specs for less than 5€/month.
In this howto, we’ll consider these operating systems:
- Ubuntu Server 20.04
- Debian 10 Buster
definitions
We’ll use these conventions:
- Tunnel network: 192.168.199.1/24
- Server IP: 192.168.199.1
- Clients IP: from 192.168.199.2
- Server external IP: 11.22.33.44
- Server external interface: eth0
server setup
For simplicity, suppose we start with a fresh server, to which we have access as root user.
If we’re using Debian Buster, we have to enable backports:
# echo "deb http://deb.debian.org/debian buster-backports main" \
| tee /etc/apt/sources.list.d/debian-backports.list && apt updateAlso for Debian Buster, we can optionally install a more recent kernel:
# apt install linux-image-5.7.0-0.bpo.2-amd64Now we can install some packages:
# apt install wireguard iptables resolvconf qrencode unattended-upgrades unboundWireguard needs a public and a private key for each connection member, so let’s create a key pair for the server:
# wg genkey | tee /root/server.privkey
sL1UKqiqMraaQarCy7UIUpkQnpgzR6Gm+L1RCgp2TEM=
# cat /root/server.privkey | wg pubkey | tee /root/server.pubkey
UgvmsCnMWWW9XAHNbc6+lkbCLSF5Mt3b85A4PrG4mRE=To simplify the configuration, we’ll use a wireguard tool called wg-quick: this allows the management of each wireguard interface in a file; moreover, it’ll manage IP addresses and routing at each interface ativation/deactivation. The public keys and IP addresses of every other VPN member will be saved in this configuration file. Let’s start by creating the configuration file (the file name MUST be [interface_name].conf) and populate it:
# touch /etc/wireguard/wg0.conf
# cat /etc/wireguard/wg0.conf
[Interface]
Address = 192.168.199.1/24
SaveConfig = true
ListenPort = 51194
PrivateKey = sL1UKqiqMraaQarCy7UIUpkQnpgzR6Gm+L1RCgp2TEM=
# systemctl enable wg-quick@wg0
# wg-quick up wg0
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 192.168.199.1/24 dev wg0
[#] ip link set mtu 1420 up dev wg0
# wg
interface: wg0
public key: UgvmsCnMWWW9XAHNbc6+lkbCLSF5Mt3b85A4PrG4mRE=
private key: (hidden)
listening port: 51194Let’s put the server aside for a moment and check the client.
client setup (linux)
We have to replicate on the client the same commands for the server, with some little differences: clearly the key pair will be different, we’re also missing IP and port for connection receive because the connection will only be outgoing.
Suppose we’re using an Ubuntu 20.04:
# sudo apt install wireguard resolvconf
# wg genkey | tee ~/server.privkey
AGrgj2nh5T/3VMddrno/FIOgiotgKVQ9ydjw2AHzbno=
# cat ~/server.privkey | wg pubkey | tee ~/server.pubkey
7c0uRl/F4jcpEPLOTA8zs0vcpQ3lTiljYbWb2QmJ11M=
# sudo touch /etc/wireguard/wg0.conf
# sudo tee /etc/wireguard/wg0.conf <<EOF
[Interface]
SaveConfig = true
PrivateKey = AGrgj2nh5T/3VMddrno/FIOgiotgKVQ9ydjw2AHzbno=
Address = 192.168.199.2/32
DNS = 192.168.199.1
[Peer]
PublicKey = phoJ2IBLJXEjaJJXzcEM6TGidh/rGxCdpXvKOP0HK0E=
Endpoint = 11.22.33.44:51194
AllowedIPs = 0.0.0.0/0
# AllowedIPs = 192.168.199.0/24
PersistentKeepalive = 20
EOF
# sudo systemctl enable wg-quick@wg0
# sudo wg-quick up wg0
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 192.168.199.2/32 dev wg0
[#] ip link set mtu 1420 up dev wg0
[#] ip -4 route add 0.0.0.0/0 dev wg0in summary, the client has a single IP address (/32) and a DNS. In the [Peer] section, we’re pointing at a specific server IP and port (but you can also use a FQDN), as well as the public key to certify its identity and decrypt traffic.
The AllowedIPs line indicates which IP addresses are forwarded over the tunnel, in this case all. Consequently, when the VPN is active, it will be impossible to see the local network (and any other network not reachable by the VPN server) and all traffic will pass through the VPN.
client configuration on the server
The server must be instructed to accept client connections:
# wg set wg0 peer 7c0uRl/F4jcpEPLOTA8zs0vcpQ3lTiljYbWb2QmJ11M= \
allowed-ips 192.168.199.2
# wg
interface: wg0
public key: UgvmsCnMWWW9XAHNbc6+lkbCLSF5Mt3b85A4PrG4mRE=
private key: (hidden)
listening port: 51194
fwmark: 0xca6c
peer: 7c0uRl/F4jcpEPLOTA8zs0vcpQ3lTiljYbWb2QmJ11M=
endpoint: 3.4.5.6:51820
allowed ips: 192.168.199.2/32
latest handshake: 39 seconds ago
transfer: 7.20 MiB received, 6.63 MiB sent
# wg-quick save wg0
[#] wg showconf wg0The last command is used to immediately commit the configuration into /etc/wireguard/wg0.conf.
We still need a couple of tweaks: masquerading and DNS.
Masquerading (and forwarding)
At the moment, the traffic from wireguard VPN arrives on the server and is discarded. Forwarding must be enabled:
# echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/99-forwarding.conf
# sysctl -w net.ipv4.ip_forward=1Let’s also create some firewall rules for traffic management:
# iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
# iptables -A FORWARD -i wg0 -j ACCEPT
# iptables -P FORWARD DROP
# iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADEWhich means:
- the traffic to be forwarded, if related to existing connections, is accepted
- the traffic to be forwarded, coming from the VPN is accepted
- every other traffic to be forwarded, if not managed by other rules, will be dropped
- traffic coming out from eth0 server port is masqueraded
Let’s make it permanent:
# iptables-save > /etc/iptables.up.rules
# cat > /etc/network/if-pre-up.d/iptables <<EOF
#!/bin/sh
/sbin/iptables-restore < /etc/iptables.up.rules
EOF
# chmod +x /etc/network/if-pre-up.d/iptablesDNS
Client uses the DNS service on the server, so we need to configure it:
# cat > /etc/unbound/unbound.conf.d/custom.conf <<EOF
interface: 0.0.0.0
access-control: 192.168.199.0/24 allow
access-control: 127.0.0.1/8 allow
forward-zone:
name: "."
forward-addr: 9.9.9.9
forward-addr: 1.1.1.2
EOF
# service unbound restartWe simply enable the DNS service on ALL interface, but we’re accepting requests only from VPN and localhost; every request is forwarded to quad9 and cloudflare DNS.
TODO
- malware/ad filtering
- Windows and OSX client instructions?