Accessing a machine behind NAT

I have a remote machine that I have to occasionally maintain. There is no easy way to connect to it, since all available ISPs at that place can only offer IPv4 (which nowadays means NAT) and not IPv6.

For this purpose I chose WireGuard, which is probably the simplest and fastest VPN solution available today. There is a great number of articles on this topic on the web. I mostly followed this one, but used other available sources as well.

Base setup

Generate private and public keys for each peer using wg genkey and wg pubkey:

wg genkey | tee private.key | wg pubkey > public.key

Configure /etc/wireguard/wg0.conf on the server. Well, in the WireGuard world all machines are just “peers”, however let’s call the machine with a public IP a “server” for brevity. I use this as a template:

[Interface]
PrivateKey = $PRIVATE_KEY # The server private key
Address = 10.8.0.1
ListenPort = $PORT
SaveConfig = true

Configure /etc/wireguard/wg0.conf on other peers that connect to our server. I use this as a template:

[Interface]
PrivateKey = $PRIVATE_KEY # The current peer private key
Address = 10.8.0.$NUMBER/24

[Peer]
PublicKey = $PUBLIC_KEY # The server public key
AllowedIPs = 10.8.0.0/24
PersistentKeepalive = 25
Endpoint = $ADDRESS:$PORT

Note that I set PersistentKeepalive, without this settings you might not be able to access machines behind NAT after some time.

Add a new [Peer] section to your server config:

[Peer]
PublicKey = $PUBLIC_KEY # The public key of the new peer
AllowedIPs = 10.8.0.$NUMBER/32

That is it! You can bring up the interface using wg-quick up wg0 on both ends. You can also use wg-quick@wg0.service to start the tunnel automatically using systemd.

Forwarding

In order to allow the peers to communicate with each other we also need to enable packet forwarding.

Set the following variables in /etc/sysctl.conf:

net.ipv4.ip_forward = 1

Enable forwarding on wg0:

iptables -A FORWARD -i wg0 -o wg0 -j ACCEPT

Masquerading (optional)

If you also want to route external traffic through the server, enable packet forwarding and masquerading on the external interface:

iptables -A FORWARD -i wg0 -o $INTERFACE -j ACCEPT
iptables -t nat -I POSTROUTING -o $INTERFACE -j MASQUERADE

In this case also make sure to update AllowedIPs to 0.0.0.0/0 on the peers:

[Peer]
PublicKey = $PUBLIC_KEY
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
Endpoint = $ADDRESS:$PORT

At this point you should have a working setup that allows peers to send packets to each other and (optionally) even route your external traffic through the server. Have fun using WireGuard!