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.
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
- 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
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: 220.127.116.11
- Server external interface: eth0
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 = 18.104.22.168: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: 22.214.171.124: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
- 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
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: 126.96.36.199 9 forward-addr: 188.8.131.52 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.
- malware/ad filtering
- Windows and OSX client instructions?