DEV Community

ricco020
ricco020

Posted on

UFW and WireGuard: the tunnel is up and nothing goes through

The tunnel comes up. wg show prints a recent handshake. The client has its address inside the tunnel. And not a single byte reaches the internet.

Almost every guide answers this with "open UDP 51820 in the firewall". You already did that — it is why the handshake works at all. The problem is somewhere else, and UFW makes the distinction easy to miss:

Entering a machine and traversing it are two different permissions.

ufw allow 51820/udp lets packets arrive at the server. Your clients' traffic does not stop there — it goes through the box and out the public interface. That path lives in the FORWARD chain, which UFW denies by default and which no allow rule touches.

The four things to check, in order

1. IP forwarding — and the file that overwrites the other file

This is the one that costs hours, because the setting looks done.

UFW loads its own sysctl file at startup, and it takes precedence over the system one. A value you carefully set in /etc/sysctl.conf can be silently overwritten on the next ufw enable.

The right place is /etc/ufw/sysctl.conf:

net/ipv4/ip_forward=1
net/ipv6/conf/default/forwarding=1
net/ipv6/conf/all/forwarding=1
Enter fullscreen mode Exit fullscreen mode

Then check the effective value, not the file you just edited:

sysctl net.ipv4.ip_forward
Enter fullscreen mode Exit fullscreen mode

2. Forwarding, which is not the same as ingress

Targeted, and the one to prefer:

sudo ufw route allow in on wg0 out on eth0
Enter fullscreen mode Exit fullscreen mode

Or globally, in /etc/default/ufw:

DEFAULT_FORWARD_POLICY="ACCEPT"
Enter fullscreen mode Exit fullscreen mode

The second opens forwarding for every interface. It is a good ten-second diagnostic and a poor permanent configuration.

3. NAT, which UFW never adds on its own

Without it, packets leave carrying their tunnel address, which nothing on the internet knows how to answer. In /etc/ufw/before.rules, at the very top, before the *filter line:

*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
COMMIT
Enter fullscreen mode Exit fullscreen mode

Two classic mistakes here: putting this block after *filter (it is then ignored), and copying eth0 without checking.

4. The real name of the egress interface

eth0 is an article convention, not a fact. Depending on the host it will be ens3, enp1s0, something else. The command that answers without guessing:

ip route get 1.1.1.1
Enter fullscreen mode Exit fullscreen mode

The test that separates the two families of causes

From a connected client, try to reach the server's address inside the tunnel (often 10.8.0.1):

What you observe What it means
tunnel address answers, nothing external does tunnel is fine — forwarding or NAT missing
tunnel address does not answer either upstream: keys, AllowedIPs, client routing
everything answers but names do not resolve not UFW at all — it is the DNS pushed to the client

That third row is worth knowing. "I have no internet" very often means "no name resolves". Pinging an IP directly tells you in one command.

After applying

before.rules is only re-read on reload:

sudo ufw disable && sudo ufw enable
sudo ufw status verbose
sudo iptables -t nat -L POSTROUTING -n -v
Enter fullscreen mode Exit fullscreen mode

One last thing that produces duplicate configurations: if your PostUp/PostDown rules in the WireGuard config already add MASQUERADE, you do not need the *nat block above. Both together rarely break anything, but they make the next outage much harder to read — and there will be a next one.


If you are still choosing a port or wondering whether 443/UDP is worth it, I wrote a longer piece on WireGuard ports, changing them and opening them properly.

One line to keep: an open port means packets can enter, not that they can traverse.

Top comments (0)