← Back to Wiki
Networking / Security

The WireGuard Setting That Can Silently Take Down Everything Else on the Box

Adding a new WireGuard peer for one narrow purpose — monitoring, a mail relay, whatever — feels like a small, low-risk change. One setting, if you get it wrong, can quietly reroute every other connection on that host into a dead end, including the SSH session you're using to fix it. This happened for real, took down two live public services for about 49 minutes, and the actual cause had nothing to do with the thing the tunnel was supposedly for.

Share on X

What AllowedIPs actually controls

On a WireGuard peer definition, AllowedIPs looks like it just answers "which destination addresses does this tunnel carry." It does more than that. On Linux, wg-quick uses that value to build real kernel-level policy routing (ip rule entries keyed off an fwmark) so that traffic matching it gets forced 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 (a full-tunnel or "road warrior" config, the kind meant for a laptop that should route all its traffic through a VPN) on a server that's supposed to keep doing everything else it was already doing, and you've just 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.

This applies even if the tunnel never actually connects. If the new peer's handshake never completes — wrong endpoint, firewall blocking it, whatever — the policy routing rule is still installed and still takes priority. 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 being added to a production server for an unrelated reason (setting up monitoring), using what looks like 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 — completely unrelated to whatever the new tunnel was for — went totally unreachable: no ping, no HTTPS, no SSH. About 49 minutes of downtime before the actual cause was found.

The dead ends checked first

None of these were the cause, but each looked plausible enough to chase first — worth knowing about so you don't repeat 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, unlike every other, healthy tunnel on the box, which all showed one. A peer with no handshake is a peer that has never actually connected.

ip rule show

This showed the fwmark-based policy rule from the new tunnel, ranked ahead of the normal routing tables — confirming it was intercepting traffic before the kernel ever got to the routing table that would have delivered it normally.

The fix, and the order that matters

  1. wg-quick down <interface> on the bad tunnel — this also tears down the policy-routing rules it created, restoring normal connectivity immediately.
  2. systemctl disable wg-quick@<interface> so it doesn't come back on the next reboot.
  3. Only after confirming everything else recovered: re-enable anything you had to temporarily disable while diagnosing (a service's restart policy, a firewall, etc.) — verify each one individually rather than flipping everything back at once and assuming it's fine.
If you had to disable your firewall mid-diagnosis to keep your own SSH session alive, re-enable it last — after everything else is confirmed working — since re-enabling it earlier risks locking you out again before you've finished.

Verify with real external checks, not just "the service looks up in a log somewhere": an actual curl/ping from outside the network, and — if anything on the box depends on a separate tunnel that was never touched — confirm that one's still carrying real traffic too, not just reporting healthy.

Doing it right from the start

The fix above is a cleanup. The actual prevention is simpler than it looks: scope AllowedIPs to exactly the one address you need to reach, every single time — a /32 (or /128 for IPv6) for a single peer, 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 built this way, done correctly on a similar project, looked like this: a downloaded/generated client config defaulted to the same dangerous 0.0.0.0/0, ::/0 — hand-edited down to the single peer address before ever installing it, so only that one destination's traffic could possibly be affected, no matter what else went wrong with the tunnel.

Worth going further, too: even with AllowedIPs correctly scoped, add a firewall rule on the receiving end that only permits this one new peer to reach the specific port it actually needs — nothing else on the internal network. That way, a mistake in the tunnel config and a mistake in the firewall config would 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

Some VPN providers' downloadable WireGuard client configs default to the identical internal tunnel address every time, regardless of account or server. Adding a second such config from the same provider while a first one was already active failed outright with an IP/subnet overlap error — both configs specified the exact same local tunnel address. The fix was simple (hand-edit the local Address field to something that doesn't collide) but the underlying 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 installing it, every time — especially the two fields that determine what it's allowed to touch: Address and AllowedIPs.