← Back to Wiki
Networking / Security

WireGuard AllowedIPs 0.0.0.0/0 Hijacks All Traffic and Kills SSH

Adding a WireGuard peer for one narrow purpose feels like a small, low-risk change. Monitoring, a mail relay, whatever. Get one setting wrong and it quietly reroutes every other connection on that host into a dead end. Including the SSH session you are using to fix it. This happened for real. It took down two live public services for about 49 minutes, and the cause had nothing to do with what the tunnel was for.

Share on X

What AllowedIPs actually controls

On a WireGuard peer definition, AllowedIPs looks like it only answers "which destination addresses does this tunnel carry". It does more. On Linux, wg-quick uses that value to build real kernel-level policy routing. ip rule entries keyed off an fwmark, forcing matching traffic through the tunnel's routing table instead of the host's normal one. Set it to a single peer's address, a /32, and only traffic to that one address is affected. Exactly as expected. Set it to 0.0.0.0/0, ::/0 on a server that is supposed to keep doing everything else it already does, and you have told the kernel to route all outbound traffic from that host through this one new tunnel. Its own SSH replies. Its own outbound HTTPS. Everything. That value is a full-tunnel or road-warrior config, meant for a laptop that should route all its traffic through a VPN.

BE WARNED: this applies even if the tunnel never connects. If the new peer's handshake never completes, the policy routing rule is still installed and still takes priority. Wrong endpoint, firewall blocking it, whatever the reason. Every packet that matches gets routed into a tunnel that goes nowhere. Not slow. Not degraded. Gone.

How this played out for real

A new WireGuard tunnel was added to a production server to set up monitoring. It started from a standard full-tunnel client template rather than one scoped to the single monitoring host it needed to reach. Within minutes the server's live public services went unreachable. No ping, no HTTPS, no SSH. Those services had nothing to do with the new tunnel. About 49 minutes of downtime before anyone found the cause.

The dead ends checked first

None of these was the cause. Each looked plausible enough to chase, so here they are, to save you the detour:

The actual tell

Two commands confirmed it directly:

wg show

The new peer's entry had allowed ips: 0.0.0.0/0, ::/0. And the real giveaway, no latest handshake line at all. Every other healthy tunnel on the box showed one. A peer with no handshake is a peer that has never connected.

ip rule show

That showed the fwmark-based policy rule from the new tunnel, ranked ahead of the normal routing tables. It was intercepting traffic before the kernel reached the routing table that would have delivered it normally.

The fix, and the order that matters

  1. Run wg-quick down <interface> on the bad tunnel. That tears down the policy-routing rules it created and restores normal connectivity immediately.
  2. Run systemctl disable wg-quick@<interface> so it does not come back on reboot.
  3. Only after confirming everything else recovered, re-enable whatever you disabled while diagnosing. A service's restart policy, a firewall. Verify each one individually. Do not flip everything back at once and assume it is fine.
BE WARNED: if you disabled your firewall mid-diagnosis to keep your own SSH session alive, re-enable it last. After everything else is confirmed working. Re-enable it earlier and you risk locking yourself out again before you have finished.

Verify with real external checks, not "the service looks up in a log somewhere". A real curl or ping from outside the network. And if anything on the box depends on a separate tunnel you never touched, confirm that one is still carrying real traffic too, not just reporting healthy.

Doing it right from the start

The fix above is cleanup. The prevention is simpler than it looks. Scope AllowedIPs to exactly the one address you need to reach, every single time. A /32 for a single peer, or a /128 for IPv6. Never a broad range on any tunnel whose purpose is "reach this one specific thing", which is nearly every server-side WireGuard use case. A tunnel done correctly on a similar project looked like this. The generated client config defaulted to the same dangerous 0.0.0.0/0, ::/0, and got hand-edited down to the single peer address before it was ever installed. Only that one destination's traffic could be affected, whatever else went wrong with the tunnel.

Go further, too. Even with AllowedIPs scoped correctly, add a firewall rule on the receiving end permitting this one peer to reach the one port it needs. Nothing else on the internal network. Then a mistake in the tunnel config and a mistake in the firewall config both have to happen at once for anything to go wrong, instead of either one alone being enough.

A related gotcha: don't trust a downloaded config's address either

BE WARNED: some VPN providers' downloadable WireGuard client configs default to the identical internal tunnel address every time, whatever the account or server. Adding a second config from the same provider while the first was active failed outright with an IP overlap error. Both configs specified the exact same local tunnel address. The fix was simple. Hand-edit the local Address field to something that does not collide. The lesson is the same as above. A config file generated by someone else's tooling is a starting point, not something to trust blindly. Open it and read it before you install it. Every time. Especially the two fields that decide what it can touch, Address and AllowedIPs.