# fail2ban and WireGuard

> Does fail2ban work with WireGuard? Why WireGuard logs no failed handshakes, the kernel debug-log workaround and its risks, and what to protect with fail2ban instead.
>
> Source: https://fail2ban.dev/guides/wireguard/ · Updated 2026-10-25 · Checked against fail2ban 1.1.1
> fail2ban.dev is an independent guide, not the official fail2ban project.

fail2ban can't protect WireGuard itself, and it doesn't need to. WireGuard silently drops every packet that isn't signed with a valid peer key: it sends no reply and writes no log line. Without failed-login lines there's nothing for a fail2ban filter to match, which is why fail2ban ships no `wireguard` filter or jail. Protect the services you reach through the tunnel, and SSH, instead.

## Why WireGuard has nothing for fail2ban to ban

fail2ban works by reading a log, finding repeated failures from one IP and banning it. That model fits SSH, mail and web logins, where an attacker can try passwords and the server logs each wrong one.

WireGuard has no passwords. Each peer is identified by a public key, and the first handshake packet must already carry a valid MAC computed from the server's public key. A scanner that doesn't know the key can't produce a valid packet. The interface:

- **Doesn't respond.** From outside, a WireGuard port looks like a closed or filtered UDP port. Port scanners can't even tell it's there.
- **Doesn't log.** The kernel module writes nothing for dropped packets at the default log level.
- **Costs almost nothing per bad packet.** Checking the MAC is cheap, so a flood of junk doesn't load the CPU the way TLS handshakes do on OpenVPN. Under real load WireGuard switches to cookie replies on its own.

So there's no brute-force surface to defend. Brute-forcing a 256-bit Curve25519 key isn't a realistic attack, and fail2ban couldn't see it anyway.

## The kernel debug-log workaround (and why to skip it)

The Linux WireGuard module has debug messages that aren't printed by default. You can switch them on with dynamic debug:

```bash
```

```log
Oct 25 18:36:12 vpn kernel: wireguard: wg0: Invalid handshake initiation from 198.51.100.7:40122
```

```ini
[Definition]
failregex = ^\s*(?:\S+ )?kernel: (?:\[\s*\d+\.\d+\]\s*)?wireguard: \S+: (?:Invalid MAC of handshake, dropping packet|Invalid handshake initiation) from \[?<ADDR>\]?:\d+$
ignoreregex =
journalmatch = _TRANSPORT=kernel
```
[wireguard]
enabled  = true
port     = 51820
protocol = udp
backend  = systemd
filter   = wireguard
maxretry = 5
```

- **The messages are rate-limited.** The kernel uses `net_ratelimit()` for them, so under a flood most packets produce no line. fail2ban sees an unpredictable sample.
- **Debug mode is noisy and needs debugfs.** It also logs every normal handshake and keepalive, it doesn't survive a reboot unless you add a boot parameter or a unit, and it needs a kernel built with `CONFIG_DYNAMIC_DEBUG`.
- **UDP source addresses are trivially spoofed.** An unauthenticated packet proves nothing about who sent it. Anyone can send junk with *your* office IP as the source and get it banned from your own VPN.
- **There's nothing to protect.** Those packets were already dropped at no cost. Banning their source changes nothing about WireGuard's security.

If you want to see who's probing the port, count packets in the firewall (`nft` counters or a `log` rule with a rate limit). That's information, not a reason to ban.

## What to protect on a WireGuard server

The useful fail2ban jails on a WireGuard box are the services around the tunnel:


1. **SSH on the public interface.** If SSH is reachable from the internet at all, run the [sshd jail](/guides/ssh/). Better still, bind SSH to the WireGuard address only (`ListenAddress 10.0.0.1` in `sshd_config`) so it never appears on the public IP.
2. **Web UIs exposed publicly.** Management tools such as wg-easy, a router UI or a dashboard on the same host need their own login jail, or should only be reachable through the tunnel.
3. **Services behind the tunnel.** [Nextcloud](/guides/nextcloud/), [Home Assistant](/guides/home-assistant/) and similar apps should still have their jails, because a stolen laptop with a valid peer config can still try passwords. Their logs will show the client's tunnel address (`10.0.0.x`), and fail2ban can ban that address.
4. **Repeat offenders across jails.** Enable [recidive](/config/recidive/) so an address that keeps getting banned elsewhere is blocked on all ports.

If the services behind the tunnel are on other machines, a ban on the WireGuard host's `input` chain doesn't reach them. Run fail2ban where the service runs, or ban in the forward path. The [nftables guide](/guides/nftables/) covers the table and hook fail2ban uses.

## Ban a misbehaving peer

A peer with valid keys that abuses a service (password guessing on an internal app, scanning the LAN) shows up with its tunnel IP. A fail2ban ban on that tunnel IP works like any other ban. The stronger fix is to remove or rotate the peer:

```bash
```

## Alternatives if you want WireGuard-side controls

- **Restrict the listening port by source** when your peers have stable IPs: an nftables or firewalld rule allowing UDP 51820 only from known networks.
- **Rate-limit UDP to the port** with an nftables `limit` rule if junk traffic costs bandwidth.
- **Change the port.** It doesn't add security, but it cuts the scan noise in your firewall logs.
- **Use a different tool** if you need reputation-based blocking of scanners at the network edge. See [alternatives to fail2ban](/alternatives/) for options such as CrowdSec.

## FAQ

### Does fail2ban work with WireGuard?

Not for WireGuard itself. WireGuard drops packets that lack a valid key without replying or logging, so there's no failed-login line for fail2ban to match. Use fail2ban for SSH, web UIs and the services you reach through the tunnel, where password failures are logged.

### Is there a fail2ban filter for WireGuard?

No. fail2ban ships no `wireguard` filter or jail. You can build one from WireGuard's kernel debug messages, but they are rate-limited and need dynamic debug enabled. They are also triggered by unauthenticated UDP packets with spoofable source addresses, so banning on them is unreliable.

### Can WireGuard be brute forced?

Not in any practical sense. Peers authenticate with Curve25519 key pairs, and the server ignores any handshake without a valid MAC derived from its public key. There's no password to guess. The realistic risks are a stolen peer private key and weak logins on the services behind the tunnel.

### How do I see failed WireGuard handshakes?

At the default log level you can't, by design. `wg show` lists the latest handshake of each known peer. For packets from unknown sources, enable dynamic debug (`echo 'module wireguard +p' > /sys/kernel/debug/dynamic_debug/control`) temporarily and read `journalctl -k`, or count packets with an nftables rule.

### Should I still run fail2ban on a WireGuard server?

Yes, if anything else on it is reachable: SSH, a web UI or apps behind the tunnel. Bind SSH to the tunnel address if you can, enable the sshd and recidive jails, and add jails for each service with a login page.
