WireGuard Hub-and-Spoke VPN Setup Guide

A step-by-step guide to creating a secure VPN with a central hub and multiple spokes.

Diagram


Prerequisites

Before you start, gather the public IP addresses of all three virtual machines:

  • VM A Public IP: VM_A_PUBLIC_IP
  • VM B Public IP: VM_B_PUBLIC_IP
  • VM C Public IP: VM_C_PUBLIC_IP

Step 1: Install WireGuard on All Three VMs

On Ubuntu / Debian:

sudo apt update
sudo apt install wireguard -y

On AlmaLinux / Rocky Linux / RHEL:

# Enable the EPEL repository
sudo dnf install epel-release -y

# Install WireGuard tools
sudo dnf install wireguard-tools -y

Step 2: Generate Keys on All Three VMs

Each VM needs its own unique pair of cryptographic keys. Perform these commands on VM A, VM B, and VM C.

# Create a directory for the keys
mkdir -p ~/.wireguard
cd ~/.wireguard

# Generate the keys
wg genkey | tee privatekey | wg pubkey > publickey

# Secure the private key file
chmod 600 privatekey

# View the keys so you can copy them
echo "--- PUBLIC KEY (Share this) ---"
cat publickey
echo "--- PRIVATE KEY (Keep this secret) ---"
cat privatekey

Important: Copy and paste each VM's public and private key into a text editor. Label them clearly (e.g., "VM A Public Key," "VM B Private Key") so you don't mix them up.


Step 3: Configure the Hub (VM B)

This is the central server. It will listen for connections from VM A and VM C and forward their traffic.

1. Enable IP Forwarding

# Enable forwarding now
sudo sysctl -w net.ipv4.ip_forward=1

# Make it permanent across reboots
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf

2. Create /etc/wireguard/wg0.conf on VM B

Replace eth0 in the PostUp/PostDown lines with your VM's actual public network interface (find it with ip a).

# /etc/wireguard/wg0.conf on VM B (The Hub)
[Interface]
# VM B's private key and tunnel IP
PrivateKey = <PASTE_VM_B_PRIVATE_KEY_HERE>
Address = 10.10.0.1/24
ListenPort = 51820

# Firewall rules to NAT traffic from peers to the internet
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 1: VM A ---
[Peer]
# VM A's public key and assigned tunnel IP
PublicKey = <PASTE_VM_A_PUBLIC_KEY_HERE>
AllowedIPs = 10.10.0.2/32

# --- Peer 2: VM C ---
[Peer]
# VM C's public key and assigned tunnel IP
PublicKey = <PASTE_VM_C_PUBLIC_KEY_HERE>
AllowedIPs = 10.10.0.3/32

Step 4: Configure the Spokes (VM A & VM C)

On VM A — create /etc/wireguard/wg0.conf:

# /etc/wireguard/wg0.conf on VM A (Spoke 1)
[Interface]
PrivateKey = <PASTE_VM_A_PRIVATE_KEY_HERE>
Address = 10.10.0.2/24

[Peer]
PublicKey = <PASTE_VM_B_PUBLIC_KEY_HERE>
Endpoint = <VM_B_PUBLIC_IP>:51820
AllowedIPs = 10.10.0.0/24, 93.184.216.34/32
PersistentKeepalive = 25

On VM C — create /etc/wireguard/wg0.conf:

# /etc/wireguard/wg0.conf on VM C (Spoke 2)
[Interface]
PrivateKey = <PASTE_VM_C_PRIVATE_KEY_HERE>
Address = 10.10.0.3/24

[Peer]
PublicKey = <PASTE_VM_B_PUBLIC_KEY_HERE>
Endpoint = <VM_B_PUBLIC_IP>:51820
AllowedIPs = 10.10.0.0/24, 93.184.216.34/32
PersistentKeepalive = 25

Step 5: Start the Tunnels

Bring the wg0 interface up on all three machines:

sudo wg-quick up wg0

To make WireGuard start automatically on boot:

sudo systemctl enable wg-quick@wg0

Step 6: Verification

1. Check Tunnel Status

On any VM, run sudo wg. You should see your interface details and the latest handshake information for your peers.

2. Ping Across the Tunnel

  • From VM A, ping the hub: ping 10.10.0.1
  • From VM A, ping VM C: ping 10.10.0.3
  • From VM C, ping VM A: ping 10.10.0.2

3. Test the Specific Route

On VM A or VM C, use traceroute to see the path your traffic takes:

# Install if needed: sudo apt install traceroute
traceroute example.com

The first hop should be the hub's tunnel IP (10.10.0.1). This confirms traffic is correctly going through the VPN.