WireGuard Site-to-Site VPN Configuration Guide - 夜莺博客

WireGuard Site-to-Site VPN Configuration Guide

WireGuard has almost no configuration surface compared to IPsec: two peers exchange public keys, and the AllowedIPs field does double duty as both a routing table and an access control list. That elegance is also the main source of mistakes — an AllowedIPs value that is too narrow silently drops traffic, one that is too broad turns the tunnel into an open relay, and a missing IP forwarding or NAT rule breaks connectivity in a way that looks like a firewall problem. This guide builds a working site-to-site tunnel between two Linux gateways and shows how to verify it layer by layer.

How WireGuard Differs from IPsec

  • No IKE. There is no negotiation phase, no proposal matching, and no main/quick mode. Peers are configured statically and authenticate with pre-shared public keys.
  • UDP only. One port, one transport. Any NAT in the path works as long as one side can initiate.
  • AllowedIPs is the routing table. The kernel installs a route to each prefix listed, pointing at the WireGuard interface. It also checks incoming packets against the peer's list, which is why it acts as an ACL.
  • One interface, many peers. A single wg0 interface can carry tunnels to multiple sites.

Step 1 - Generate Keys

Generate a key pair per peer, and set restrictive permissions on the private key:

umask 077
wg genkey | tee sitea.key | wg pubkey > sitea.pub
wg genkey | tee siteb.key | wg pubkey > siteb.pub
cat sitea.pub siteb.pub

Do not share private keys between peers — each peer needs its own key pair, and each peer's configuration holds only its own private key plus the peer's public key.

Step 2 - Configure Site A

Site A has LAN 10.10.1.0/24 and a public address of 203.0.113.10. Site B has LAN 10.10.2.0/24 behind NAT. Because Site B is behind NAT, Site A is the side that listens and Site B is the side that initiates.

# /etc/wireguard/wg0.conf on Site A
[Interface]
Address = 10.99.0.1/24
ListenPort = 51820
PrivateKey = <sitea private key>

# NAT the tunnel for LAN clients that need internet access
PostUp   = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
# Site B
PublicKey = <siteb public key>
AllowedIPs = 10.99.0.2/32, 10.10.2.0/24
PersistentKeepalive = 25

The AllowedIPs line does two things: it installs routes for 10.99.0.2/32 and 10.10.2.0/24 via wg0, and it accepts inbound packets whose source is in either prefix. Omitting 10.10.2.0/24 from this list is the classic error that makes pings fail in one direction.

Step 3 - Configure Site B

# /etc/wireguard/wg0.conf on Site B
[Interface]
Address = 10.99.0.2/24
PrivateKey = <siteb private key>
ListenPort = 51820

[Peer]
# Site A
PublicKey = <sitea public key>
Endpoint = 203.0.113.10:51820
AllowedIPs = 10.99.0.0/24, 10.10.1.0/24
PersistentKeepalive = 25

PersistentKeepalive = 25 on the NAT side keeps the UDP mapping alive so Site A can always reach Site B without waiting for an inbound connection. Without it, the tunnel appears to work until it goes idle, then traffic in the listen direction starts failing for a minute or two.

Step 4 - Enable Forwarding and Firewall Rules

WireGuard is just another interface; the host must still route between its LAN interface and wg0:

sudo sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-wireguard.conf

sudo iptables -A FORWARD -i wg0 -o eth1 -j ACCEPT
sudo iptables -A FORWARD -i eth1 -o wg0 -j ACCEPT
sudo iptables -A INPUT -p udp --dport 51820 -j ACCEPT

sudo systemctl enable --now wg-quick@wg0
sudo systemctl status wg-quick@wg0

MTU and fragmentation

Note that the MTU matters more on WireGuard than on most tunnels: the interface defaults account for UDP and WireGuard headers, but a path with a lower MTU will produce the classic symptom of pings and small requests working while large transfers hang. Test with a large, do-not-fragment ping before blaming the application:

ping -M do -s 1350 10.10.2.1

If that fails while a smaller size succeeds, lower the tunnel MTU explicitly in the [Interface] section (MTU = 1280) and retest.

Verification

sudo wg show
sudo wg show wg0
sudo wg show wg0 latest-handshakes
sudo wg show wg0 transfer
ip -brief addr show wg0
ip route show table all | grep wg0
ping -c3 10.99.0.2
ping -c3 10.10.2.1

Reading wg show output

wg show is the single command that tells you whether the tunnel is alive. Read four fields:

  • latest handshake — a timestamp within the last two minutes means the tunnel is up. A value of "never" means no packets have completed the handshake, which points at the endpoint address, the UDP port, or a blocked path.
  • transfer — received bytes near zero while sent bytes climb means the peer is not receiving your packets or is refusing them. This is the signature of an AllowedIPs mismatch on the far side.
  • endpoint — for a NATed peer this shows the currently learned public address and port.
  • allowed ips — confirms the routes and ACL entries as configured.

Operational Notes

  • Give every peer its own key pair; never reuse a private key across hosts.
  • List all remote subnets in AllowedIPs — it is both the route and the ACL.
  • Set PersistentKeepalive on NATed peers only, and only on those.
  • If the tunnel comes up but drops idle for a while, keepalive is missing.
  • Test with large DF pings; the most common "WireGuard is slow" report is an MTU problem, not a crypto one.

Related reading: our IPsec VTI versus policy-based VPN comparison, the WireGuard MTU and handshake troubleshooting guide, and the strongSwan IKEv2 site-to-site configuration article.

原文链接:https://www.wireguard.com/quickstart/