- Package Installation
Begin by updating the package index and installing the core WireGuard utilities alongside a DNS resolver manager to handle dynamic DNS updates across the tunnel.
sudo apt update
sudo apt install wireguard openresolv -y
- Cryptographic Key Generation
Navigate to the configuration directory, restrict file permissions, and generate distinct key pairs for the server and client nodes. Using input/output redirection ensures keys are stored securely without exposing them in shell history.
cd /etc/wireguard
umask 077
wg genkey > node_srv.priv
wg pubkey < node_srv.priv > node_srv.pub
wg genkey > node_cli.priv
wg pubkey < node_cli.priv > node_cli.pub
- Enabling Kernel Packet Forwarding
To route traffic between the VPN interface and the external network, IPv4 forwarding must be activated at the kernel level. Creating a dedicated sysctl drop-in file ensures the setting persists across reboots.
echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/99-wg-forward.conf
sudo sysctl --system
- Server-Side Configuration
Create the primary interface definition at /etc/wireguard/wg0.conf. The configuration below establishes the tunnel interface, assigns an internal subnet, configures NAT masquerading for outbound traffic, and defines the peer relationship. Replace the placeholder commands with the actual key contents generated earlier.
cat <<EOF > /etc/wireguard/wg0.conf
[Interface]
PrivateKey = $(cat /etc/wireguard/node_srv.priv)
Address = 10.50.10.1/24
ListenPort = 49210
DNS = 9.9.9.9
MTU = 1420
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
PublicKey = $(cat /etc/wireguard/node_cli.pub)
AllowedIPs = 10.50.10.2/32
EOF
Key Parameters: The PostUp/PostDown directives dynamically manage firewall rules using %i to reference the active interface. AllowedIPs restricts routing to the specific client adress.
- Client-Side Configuration
On the remote machine, generate a configuration file (e.g., /etc/wireguard/client.conf). This file points to the server's public endpoint and routes all IPv4/IPv6 traffic through the encrypted tunnel.
cat <<EOF > /etc/wireguard/client.conf
[Interface]
PrivateKey = $(cat /etc/wireguard/node_cli.priv)
Address = 10.50.10.2/32
DNS = 9.9.9.9
MTU = 1420
[Peer]
PublicKey = $(cat /etc/wireguard/node_srv.pub)
Endpoint = 203.0.113.15:49210
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
EOF
Key Parameters: PersistentKeepalive prevents NAT timeouts on restrictive networks. The Endpoint must match the server's public IP and listening port.
- Service Lifecycle Management
Initialize the tunnel and bind it to the system's service manager for automatic startup. The wg-quick wrapper handles interface creation, routing table updates, and DNS configuration automatically.
# Bring up the tunnel and enable persistence
sudo wg-quick up wg0
sudo systemctl enable --now wg-quick@wg0
# Tear down the tunnel and disable auto-start
sudo wg-quick down wg0
sudo systemctl disable --now wg-quick@wg0
- Connection Verification
Query the active tunnel state to verify handshake timestamps, data transfer statistics, and peer connectivity.
sudo wg show
- Implementing Policy-Based Routing
For scenarios requiring split tunneling or source-specific routing, inject custom routing rules directly into the interface lifecycle hooks. The example below forces traffic originating from a specific local IP to bypass the VPN and use the default routing table.
[Interface]
PrivateKey = <client_private_key>
Address = 10.50.10.2/32
DNS = 1.1.1.1
PostUp = ip rule add from 192.168.5.20 lookup main
PreDown = ip rule del from 192.168.5.20 lookup main
[Peer]
PublicKey = <server_public_key>
Endpoint = 203.0.113.15:49210
AllowedIPs = 0.0.0.0/0, ::/0