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:
1# echo "deb http://deb.debian.org/debian buster-backports main" \
2| tee /etc/apt/sources.list.d/debian-backports.list && apt update
Also for Debian Buster, we can optionally install a more recent kernel:
1# apt install linux-image-5.7.0-0.bpo.2-amd64
Now we can install some packages:
1# apt install wireguard iptables resolvconf qrencode unattended-upgrades unbound
Wireguard needs a public and a private key for each connection member, so let’s create a key pair for the server:
1# wg genkey | tee /root/server.privkey
2sL1UKqiqMraaQarCy7UIUpkQnpgzR6Gm+L1RCgp2TEM=
3
4# cat /root/server.privkey | wg pubkey | tee /root/server.pubkey
5UgvmsCnMWWW9XAHNbc6+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:
1# touch /etc/wireguard/wg0.conf
2
3# cat /etc/wireguard/wg0.conf
4[Interface]
5Address = 192.168.199.1/24
6SaveConfig = true
7ListenPort = 51194
8PrivateKey = sL1UKqiqMraaQarCy7UIUpkQnpgzR6Gm+L1RCgp2TEM=
9
10# systemctl enable wg-quick@wg0
11
12# wg-quick up wg0
13[#] ip link add wg0 type wireguard
14[#] wg setconf wg0 /dev/fd/63
15[#] ip -4 address add 192.168.199.1/24 dev wg0
16[#] ip link set mtu 1420 up dev wg0
17
18# wg
19interface: wg0
20 public key: UgvmsCnMWWW9XAHNbc6+lkbCLSF5Mt3b85A4PrG4mRE=
21 private key: (hidden)
22 listening port: 51194
Let’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:
1# sudo apt install wireguard resolvconf
2
3# wg genkey | tee ~/server.privkey
4AGrgj2nh5T/3VMddrno/FIOgiotgKVQ9ydjw2AHzbno=
5
6# cat ~/server.privkey | wg pubkey | tee ~/server.pubkey
77c0uRl/F4jcpEPLOTA8zs0vcpQ3lTiljYbWb2QmJ11M=
8
9# sudo touch /etc/wireguard/wg0.conf
10
11# sudo tee /etc/wireguard/wg0.conf <<EOF
12[Interface]
13SaveConfig = true
14PrivateKey = AGrgj2nh5T/3VMddrno/FIOgiotgKVQ9ydjw2AHzbno=
15Address = 192.168.199.2/32
16DNS = 192.168.199.1
17
18[Peer]
19PublicKey = phoJ2IBLJXEjaJJXzcEM6TGidh/rGxCdpXvKOP0HK0E=
20Endpoint = 11.22.33.44:51194
21AllowedIPs = 0.0.0.0/0
22# AllowedIPs = 192.168.199.0/24
23PersistentKeepalive = 20
24EOF
25
26# sudo systemctl enable wg-quick@wg0
27
28# sudo wg-quick up wg0
29[#] ip link add wg0 type wireguard
30[#] wg setconf wg0 /dev/fd/63
31[#] ip -4 address add 192.168.199.2/32 dev wg0
32[#] ip link set mtu 1420 up dev wg0
33[#] ip -4 route add 0.0.0.0/0 dev wg0
in 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:
1# wg set wg0 peer 7c0uRl/F4jcpEPLOTA8zs0vcpQ3lTiljYbWb2QmJ11M= \
2allowed-ips 192.168.199.2
3
4# wg
5interface: wg0
6 public key: UgvmsCnMWWW9XAHNbc6+lkbCLSF5Mt3b85A4PrG4mRE=
7 private key: (hidden)
8 listening port: 51194
9 fwmark: 0xca6c
10
11peer: 7c0uRl/F4jcpEPLOTA8zs0vcpQ3lTiljYbWb2QmJ11M=
12 endpoint: 3.4.5.6:51820
13 allowed ips: 192.168.199.2/32
14 latest handshake: 39 seconds ago
15 transfer: 7.20 MiB received, 6.63 MiB sent
16
17# wg-quick save wg0
18[#] wg showconf wg0
The 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:
1# echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/99-forwarding.conf
2
3# sysctl -w net.ipv4.ip_forward=1
Let’s also create some firewall rules for traffic management:
1# iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
2# iptables -A FORWARD -i wg0 -j ACCEPT
3# iptables -P FORWARD DROP
4# iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
Which 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:
1# iptables-save > /etc/iptables.up.rules
2
3# cat > /etc/network/if-pre-up.d/iptables <<EOF
4#!/bin/sh
5/sbin/iptables-restore < /etc/iptables.up.rules
6EOF
7
8# chmod +x /etc/network/if-pre-up.d/iptables
DNS
Client uses the DNS service on the server, so we need to configure it:
1# cat > /etc/unbound/unbound.conf.d/custom.conf <<EOF
2interface: 0.0.0.0
3access-control: 192.168.199.0/24 allow
4access-control: 127.0.0.1/8 allow
5
6forward-zone:
7 name: "."
8 forward-addr: 9.9.9.9
9 forward-addr: 1.1.1.2
10EOF
11
12# service unbound restart
We 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?