WireGuard NAT Traversal Made Easy

WireGuard UDP NAT Traversal
A Traditional NAT translates network addresses but not ports

NAT traversal lets two computers behind their respective NATs establish a secure VPN connection with each other, without using a relay. Many believe you need a service (such as ourselves) to do NAT traversal. In truth, any WireGuard peer sitting behind a traditional NAT can easily be accessed using the hole-punch technique.

This technique may not be applicable for coffee house road warriors, but if you have two relatively stable external IP addresses for the machines, you can simply configure WireGuard correctly, set PersistentKeepalive to 5, and viola, you’ve defeated the NATs. Consider yourself a true professional the first time you do it. It does not require port forwarding, nor does it require UPnP. But it does require a “traditional” NAT, and not a carrier-grade NAT.

NAT Traversal: Hole Punch Technique

The hole-punch technique lets WireGuard peers with relatively stable IP addresses connect through their respective NATs. WireGuard is a UDP-based protocol similar to DNS, so we will use DNS as an example.

If one does a DNS query like this:

C:\Users\nettica>nslookup google.com 8.8.8.8
Server:  dns.google
Address:  8.8.8.8

Non-authoritative answer:
Name:    google.com
Addresses:  2607:f8b0:400a:80b::XXXX
          142.250.69.XXX

Programmatically, what is happening with your computer and your NAT is this:

  • Your UDP packet is sent from 192.168.2.2:12345 to port 8.8.8.8:53 (DNS) with a query for google.com
  • 8.8.8.8:53 replies with the answer you see above

In reality, the UDP packet is sent to 192.168.2.1, your default gateway. The NAT on this gateway translates the address from 192.168.2.2:12345 to 172.16.45.23:12345 and then sends it to 8.8.8.8:53. The reply goes to 172.16.45.23:12345. The NAT sees it recently sent a packet from that port, so it forwards the reply to 192.168.2.2:12345. Note that the NAT translates the network address but not the port.

This is how and why you can defeat the NATs. Simply knowing each other’s IP addresses (and ports) and then persistently pinging them with a keepalive will cause the request of one machine to look like a reply to the other, allowing WireGuard to synchronize the connection. It is safe and secure. Only the addresses you are actively pinging can connect through the NAT. WireGuard secures the connection and everything sent over it.

Pro Tip:

Use the same port number on both sides of the connection to simplify the process. You can use the WireGuard port 51820, or, a nice round number like 50000.

Example Configuration

Peer A traversal.conf

[Interface]
Address = 10.0.0.1/32
PrivateKey = sMuRf...

[Peer]
PublicKey = pEeRbXTBbe...
PresharedKey = b0tH69dNA...
AllowedIPs = 10.0.0.2/32
Endpoint = A.B.C.D:50000
PersistentKeepalive = 5

Peer B traversal.conf

[Interface]
Address = 10.0.0.2/32
PrivateKey = dEadB...

[Peer]
PublicKey = PeErAwet9X...
PresharedKey = b0tH69dNA...
AllowedIPs = 10.0.0.1/32
Endpoint = W.X.Y.Z:50000
PersistentKeepalive = 7

These are two configuration files with the minimum requirements for connecting two WireGuard peers sitting behind their respective NATs. A.B.C.D and W.X.Y.Z are their external public IP addresses.

Having a keepalive of five is rather noisy; pick a nice prime number like 23 to tone it down a little. Even better: two different primes! So your first ping might time out, so what? Why use primes? Using primes helps keep the connection in sync by ensuring the pings stay out of sync. Boom! Mind blown!

Multiple Machines Behind the same NAT

You must use unique ports for each machine behind the same NAT. While most NATs do use tuples to map who is talking to whom, they can get confused if two machines using the same port are talking to the same remote machine (over UDP). Trust us when we say this tip will save you hours of debugging.

IPv6 NAT Traversal

   IPv6 Address. . . . . . . . . . . : 2607:fbXX:ec1d:9bb5:5c38:d969:0:XXXX
   Temporary IPv6 Address. . . . . . : 2607:fbXX:ec1d:9bb5:796b:af45:52a6:XXXX
   Link-local IPv6 Address . . . . . : fe80::5b43:XXXX:69de:b4a7%83

If IPv4 doesn’t work, give IPv6 a try. While an ISP may use a carrier-grade NAT for their limited supply of IPv4 addresses, they don’t need to for IPv6. In Windows (above), both the IPv6 address and temporary IPv6 address are real routable IPv6 addresses. The temporary address is typically the IPv6 address of your router. The link-local address (which you can tell from both the % sign and fe80) are not routable. But either of the first two addresses will work. You’re still behind your gateway’s NAT, so you still must do all the same things for NAT traversal: persistent keepalive, etc.

If it appears not to work, then turn off your local (public) firewall. There can be different firewall rules for IPv4 and IPv6, and even if you have IPv4 working like a charm, chances are you didn’t consider IPv6. If you find it then works, turn it back on and track down the firewall rule you need to allow.

Note that you do not need to use IPv6 addresses for your Interfaces and AllowedIPs just because your endpoint is IPv6. You can use IPv4; WireGuard encapsulates the packets.

Relay Service

If the above doesn’t solve your problem, then take a look at our Relay Services. We provide high-performance bandwidth, so you’re not stuck with mediocre performance. And if you found this article informative, imagine our support!

More Information

Setting Up Nettica Tunnel and Relay Services

Getting Started with Nettica VPN Service

Related Reading

Use Docker & WSL2 to access your home network with WireGuard

Easily Create a VPN with a Raspberry PI and WireGuard

Securely Setting Up Remote Desktop

WireGuard Cheatsheet