WireGuard VPN Hardening: Remote Access Tunnel Config Guide
WireGuard is light and fast, but defaults leave key management, egress, and client access weakly controlled. This guide covers key generation, firewall rules, AllowedIPs, multi-client ops, and handshake audits for a repeatable WireGuard VPN hardening setup.
Scope
WireGuard VPN hardening fits: staff remote access to the LAN; ops tunnels to cloud and IDC hosts; encrypted branch interconnect; personal access to home NAS and cameras. Kernel-level speed and a tiny config made it popular. Many installs stop at "it pings"—keys never rotate, clients are unconstrained, tunnel egress is open. Full deploy-to-harden config below.
Prerequisites
- Linux VPN server (Ubuntu 22.04 / Debian 12; kernel ≥ 5.6 loads the in-kernel module)
- Public IP or UDP port forward
- root or sudo; ufw or iptables available
Mechanism
WireGuard: ChaCha20-Poly1305, Curve25519, Noise IK. No user auth unlike OpenVPN—identity is the keypair. Each client holds a public/private pair; the server Peer list decides who connects. Four hardening axes: (1) key management—the private key is the only credential, lock perms and rotate; (2) AllowedIPs—limit which subnets a client can reach; (3) firewall—open only the tunnel port, enable NAT; (4) client access control and audit—contain leaked keys.
Procedure
1. Install and generate keys
# Install WireGuard
sudo apt update && sudo apt install -y wireguard-tools
# Generate the server key pair (umask ensures private-key mode 600)
umask 077
wg genkey | tee /etc/wireguard/privatekey | wg pubkey > /etc/wireguard/publickey
cat /etc/wireguard/privatekey # Keep the private key on the server only
cat /etc/wireguard/publickey # Distribute the public key to clients
Key params: umask 077 sets private-key file mode 600. A leaked private key is a stolen VPN identity—confirm with chmod 600.
2. Server config and hardening
# Edit /etc/wireguard/wg0.conf
sudo tee /etc/wireguard/wg0.conf > /dev/null << 'EOF'
[Interface]
Address = 10.8.0.1/24 # tunnel subnet, server address
ListenPort = 51820
PrivateKey = server private key
# hardening 1: persist keepalive so reconnects survive
PersistentKeepalive = 25
# hardening 2: firewall rules (auto-configured via PostUp/PostDown)
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
# hardening 3: enable IP forwarding
PostUp = sysctl -w net.ipv4.ip_forward=1
PostDown = sysctl -w net.ipv4.ip_forward=0
# Client Peer example (add one Peer per client in production)
[Peer]
PublicKey = client1 public key
AllowedIPs = 10.8.0.2/32 # allow only this client's own IP
EOF
# hardening 4: private-key permissions
sudo chmod 600 /etc/wireguard/wg0.conf /etc/wireguard/privatekey
3. Generate client config
# Generate a unique key pair per client (run on the client machine)
umask 077
wg genkey | tee client_priv | wg pubkey > client_pub
# Add the new Peer on the server
sudo wg set wg0 peer <client public key> allowed-ips 10.8.0.2/32
# Generate the client config (write to /etc/wireguard/wg0.conf on the client)
# [Interface]
# Address = 10.8.0.2/24
# PrivateKey = client private key
# [Peer]
# PublicKey = server public key
# Endpoint = server public IP:51820
# AllowedIPs = 10.8.0.0/24 # tunnel subnet only; do not take over all traffic
# PersistentKeepalive = 25
Key params: client AllowedIPs should be 10.8.0.0/24 or the LAN subnets you need. Do not use 0.0.0.0/0—full-tunnel makes the box a public proxy if a key leaks.
4. Firewall and host hardening
# ufw: allow the WireGuard port and restrict sources
sudo ufw allow 51820/udp
sudo ufw allow from 10.8.0.0/24
# Restrict SSH to the tunnel subnet or specific IPs
sudo ufw deny 22/tcp
sudo ufw allow from 10.8.0.0/24 to any port 22 proto tcp
# Enable TCP BBR to improve tunnel throughput (optional)
echo "net.core.default_qdisc=fq" | sudo tee -a /etc/sysctl.conf
echo "net.ipv4.tcp_congestion_control=bbr" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
5. Start and enable on boot
# Start the tunnel
sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
# Check tunnel status
sudo wg show
6. Key rotation and client revocation
# Rotate the server key (keep the existing Peer list; redistribute the public key)
umask 077
wg genkey | sudo tee /etc/wireguard/privatekey | wg pubkey | sudo tee /etc/wireguard/publickey
# Give the new public key to all clients; client Endpoints do not change
# Remove a compromised or departed client
sudo wg set wg0 peer <client public key> remove
# Inspection: check latest handshake time per Peer
sudo wg show wg0 latest-handshakes
Verification
# On the server: inspect tunnel status and per-client handshake times
sudo wg show
# On the client: verify tunnel connectivity
ping -c 3 10.8.0.1
# Verify NAT forwarding: from the client, reach LAN resources behind the server
ping -c 3 192.168.1.10
# Verify encrypted traffic: tcpdump should show encrypted tunnel payloads
sudo tcpdump -i eth0 -n udp port 51820
FAQ
Q1: Risk of client AllowedIPs = 0.0.0.0/0?
All client internet traffic goes through the tunnel. The VPN server becomes a public proxy. If the client key leaks, an attacker uses your egress for attacks or LAN access, and burns your bandwidth and IP reputation. Unless you need full-tunnel, allow only the LAN subnets required.
Q2: How to contain a leaked key fast?
Two steps: (1) on the server, wg set wg0 peer <leaked public key> remove kicks the Peer immediately; (2) rotate the server private key, redistribute the public key, tell all clients to update. If the attacker may already be on the LAN, check handshake source IPs in journalctl -u wg-quick@wg0.
Q3: Site-to-site AllowedIPs?
For site interconnect, each server Peer must list the remote LAN. Example: server A (10.8.0.1) needs B's backend 172.16.0.0/16 → on A, B's Peer gets AllowedIPs = 172.16.0.0/16, 10.8.0.3/32, and B must forward. AllowedIPs also writes the routing table; both sides must match.
Takeaway
WireGuard hardening is not hard. Identity is the key: mode 600 on the private key, unique keys per client, revoke on compromise, rotate on a schedule. Pair with minimal AllowedIPs, firewall port/source limits, and tight NAT/forwarding. Put key rotation on the quarterly ops calendar; ship handshake audit logs to your existing log stack.