DB-유

Setting Up a WireGuard VPN Server

· linux, vpn, wireguard, networking

OpenVPN has served me well, but every new setup means a certificate authority, a dozen options, and a client that takes several seconds to connect. After the last rebuild I tried WireGuard on a weekend and haven't gone back for anything small. One key pair per peer, a config that fits on one screen, and connections that come up almost instantly. It isn't in the mainline kernel yet, so it's a DKMS module, but the project's packages make that painless.

Here's a server on Ubuntu 18.04 (public IP, sudo, UDP 51820 reachable) that gives remote clients access to the private network, plus NAT for a full-tunnel setup if you want one. CentOS 7 differs only in package installation (wireguard from EPEL plus the ELRepo kernel module). DKMS needs the linux-headers package matching your kernel.

Install

sudo add-apt-repository ppa:wireguard/wireguard
sudo apt update
sudo apt install -y wireguard linux-headers-$(uname -r)
sudo modprobe wireguard
lsmod | grep wireguard

If modprobe fails, the DKMS build did not succeed; dkms status and /var/lib/dkms/wireguard/*/build/make.log say why, and it is almost always missing headers.

Server keys

sudo mkdir -p /etc/wireguard
sudo chmod 700 /etc/wireguard
cd /etc/wireguard
umask 077
wg genkey | sudo tee server.key | wg pubkey | sudo tee server.pub

umask 077 makes sure the private key is created readable by root only. Keep the public key handy; each client needs it.

Server config

sudo nano /etc/wireguard/wg0.conf
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <contents of server.key>
SaveConfig = false

# NAT so clients can reach the Internet and the LAN through the server.
PostUp   = iptables -t nat -A POSTROUTING -o ens3 -j MASQUERADE; iptables -A FORWARD -i wg0 -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o ens3 -j MASQUERADE; iptables -D FORWARD -i wg0 -j ACCEPT

[Peer]
# laptop
PublicKey = <laptop public key, generated in the client section below>
AllowedIPs = 10.8.0.2/32

Replace ens3 with your public interface from ip route show default. 10.8.0.0/24 is the VPN's own subnet; each peer gets one address from it, and AllowedIPs on the server side acts as both the routing table and the access list for that peer.

Forwarding and firewall

echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
sudo ufw allow 51820/udp

ufw's default forwarding policy is DROP; the iptables -A FORWARD rule in PostUp handles traffic from wg0, but if you rely on ufw exclusively, set DEFAULT_FORWARD_POLICY="ACCEPT" in /etc/default/ufw instead.

A client

On the client (or on the server, then transferred securely):

umask 077
wg genkey | tee laptop.key | wg pubkey > laptop.pub

Client wg0.conf:

[Interface]
PrivateKey = <contents of laptop.key>
Address = 10.8.0.2/24
DNS = 10.0.0.2

[Peer]
PublicKey = <server public key>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

AllowedIPs = 0.0.0.0/0 sends all traffic through the tunnel. For a split tunnel that only reaches the server's LAN, use AllowedIPs = 10.8.0.0/24, 10.0.0.0/24. PersistentKeepalive matters for clients behind NAT so the mapping does not expire. Paste the client's public key into the server's [Peer] section.

Bring it up

On the server, wg-quick reads the config, creates the interface and applies the PostUp rules:

sudo wg-quick up wg0
sudo systemctl enable wg-quick@wg0

On a Linux client the same wg-quick up wg0 works. For iOS, Android, macOS and Windows the official apps import the same file, and qrencode -t ansiutf8 < laptop.conf prints a QR code the mobile apps can scan.

Is it working?

sudo wg show

Once the client connects, the peer entry shows latest handshake a few seconds ago and non-zero transfer counters. From the client:

ping -c 3 10.8.0.1
curl -4 ifconfig.co       # should print the server's public IP for a full tunnel

Things that bit me

  • No handshake at all, wg show never updates latest handshake. UDP 51820 is not reaching the server (firewall or cloud security group), or the keys are swapped. Each side's [Peer] PublicKey must be the other side's public key, and it is easy to paste a private key by mistake.
  • Handshake works but no traffic passes. IP forwarding is off, or the MASQUERADE rule names the wrong interface. sysctl net.ipv4.ip_forward should be 1 and sudo iptables -t nat -L POSTROUTING -n -v should show packets counting up.
  • RTNETLINK answers: Operation not supported from wg-quick up. The kernel module is not loaded. modprobe wireguard, and if that fails rebuild with dkms autoinstall after installing headers.
  • wg-quick: 'wg0' already exists. The interface is up from a previous attempt. sudo wg-quick down wg0 first, then edit and bring it back up.

One catch: there's no revocation list. Removing a client means deleting its [Peer] block and reloading. For a personal or small-team VPN I'm happy with that trade.