# fail2ban for OpenVPN

> Set up fail2ban for OpenVPN: the openvpn filter for TLS handshake and auth failures, a UDP 1194 jail, journal vs log-append logging, and the ovpn-server name trap.
>
> Source: https://fail2ban.dev/guides/openvpn/ · Updated 2026-10-25 · Checked against fail2ban 1.1.1
> fail2ban.dev is an independent guide, not the official fail2ban project.

To ban OpenVPN attackers with fail2ban, enable the `[openvpn]` jail with `port = 1194` and `protocol = udp`, and point it at the log your server actually writes. Use the systemd journal (`backend = systemd`) with a `journalmatch` for your OpenVPN unit, or a `log-append` file. The stock [openvpn filter](/guides/openvpn/#filter-openvpn) bans on `TLS Error: TLS handshake failed`, `VERIFY ERROR` and `TLS Auth Error`.

```ini
[openvpn]
enabled      = true
port         = 1194
protocol     = udp
backend      = systemd
journalmatch = _SYSTEMD_UNIT=openvpn-server@server.service _SYSTEMD_UNIT=openvpn@server.service
filter       = openvpn[_daemon="(?:ovpn-\S+|openvpn)"]
maxretry     = 3
```

## What the openvpn filter matches

The filter was added in **fail2ban 1.1.1**. The packaged versions on Debian 12/13 and Ubuntu 24.04/26.04 (1.0.2 and 1.1.0) don't include it. Check first:

```bash
```

```ini
[INCLUDES]
before = common.conf

[Definition]
_daemon = ovpn-server\d*

failregex = ^%(__prefix_line)s<HOST>:\d{4,5} (?:TLS Auth Error:|VERIFY ERROR:|TLS Error: TLS handshake failed\b|SIGUSR1\[soft,connection-reset\] received\b)
            ^%(__prefix_line)sTLS Error: cannot locate HMAC in incoming packet from \[AF_INET\]\s*<HOST>:\d{4,5}
ignoreregex =
```

| Log message | Meaning |
|---|---|
| `TLS Error: TLS handshake failed` | Client never completed the TLS handshake: scanners, wrong protocol, wrong `ta.key` |
| `VERIFY ERROR: depth=..., error=...` | Client certificate rejected (unknown CA, revoked, expired) |
| `TLS Auth Error: Auth Username/Password verification failed for peer` | Wrong username or password with `auth-user-pass-verify` or a PAM/LDAP plugin |
| `TLS Error: cannot locate HMAC in incoming packet from [AF_INET]...` | Packet without the right `tls-auth` HMAC |
| `SIGUSR1[soft,connection-reset] received` | TCP-mode client reset (bad packet lengths, probes) |

Example lines from a server on UDP:

```log
Oct 25 18:36:12 vpn ovpn-server[901]: 198.51.100.7:40122 TLS Auth Error: Auth Username/Password verification failed for peer
```

The filter expects the syslog tag `ovpn-server`. That's what Debian's legacy unit `openvpn@server.service` produces: it starts OpenVPN with `--daemon ovpn-%i`, so a config named `server.conf` logs as `ovpn-server`.

The newer upstream unit, `openvpn-server@server.service` (config in `/etc/openvpn/server/`), doesn't use `--daemon`. Its journal lines are tagged `openvpn`, and the stock filter misses every one of them:

```log
```

## Pick the log source

OpenVPN can log to the journal, to syslog or to its own file. Point the jail at the one you use.


#### systemd journal (recommended)
Without `log` or `log-append` in the server config, OpenVPN writes to stdout and the journal stores it. Use the journal backend and match your unit name:

```ini
[openvpn]
enabled      = true
port         = 1194
protocol     = udp
backend      = systemd
journalmatch = _SYSTEMD_UNIT=openvpn-server@server.service
filter       = openvpn[_daemon="(?:ovpn-\S+|openvpn)"]
```
#### log-append file
With `log-append /var/log/openvpn/openvpn.log` in the server config, read that file:

```ini
[openvpn]
enabled  = true
port     = 1194
protocol = udp
backend  = auto
logpath  = /var/log/openvpn/openvpn.log
```
#### /var/log/syslog
On systems with rsyslog, OpenVPN's journal lines are also copied to `/var/log/syslog` (Debian/Ubuntu) or `/var/log/messages` (RHEL). That's the stock jail's default `logpath`. It works, but the jail then scans every syslog line on the host.

Don't point fail2ban at the status file (`status /var/log/openvpn/openvpn-status.log`). It's a table of connected clients that OpenVPN rewrites, not a log.

## UDP port and protocol

The stock `[openvpn]` jail uses `port = 443` and the default `protocol = tcp`, which suits OpenVPN on TCP 443. For the usual UDP 1194 set both:

```ini
protocol = udp
```

## Test the jail

Check the filter against real data before relying on it:

```bash
sudo fail2ban-regex -m "_SYSTEMD_UNIT=openvpn-server@server.service" systemd-journal 'openvpn[_daemon="(?:ovpn-\S+|openvpn)"]'

# file
sudo fail2ban-regex /var/log/openvpn/openvpn.log openvpn
```

```bash
sudo systemctl restart fail2ban
sudo fail2ban-client status openvpn
```

## How useful is this on OpenVPN?

A few honest caveats:

- **`tls-crypt` already hides the server.** With `tls-crypt` (or `tls-crypt-v2`), packets without the right key are dropped before any TLS work. Scanners mostly produce no matching log line at all, so the jail sees little. That's a good outcome, not a broken jail.
- **`TLS handshake failed` also hits your own users** when a laptop roams between networks or a client has an outdated `ta.key`. Keep `maxretry` at 3 or more and put office networks in [`ignoreip`](/config/whitelist/).
- **UDP source addresses can be spoofed.** A forged handshake can't complete, so `VERIFY ERROR` and `TLS Auth Error` (which need a finished handshake) are the strongest signals. A purely handshake-based ban could, in theory, be triggered for an address the attacker doesn't own.
- **IPv6:** if your server listens on IPv6 and logs addresses in another format (for example with an `[AF_INET6]` prefix), the stock regex won't match them. Test with your own log lines.

For a modern alternative that doesn't answer unauthenticated packets at all, see [WireGuard and fail2ban](/guides/wireguard/).

## The openvpn filter reference

{{component:filter-ref:openvpn}}

## FAQ

### Is there a fail2ban filter for OpenVPN?

Yes, `filter.d/openvpn.conf`, added in fail2ban 1.1.1. It matches `TLS Error: TLS handshake failed`, `VERIFY ERROR`, `TLS Auth Error` and missing `tls-auth` HMAC lines. Older packaged versions (1.0.2, 1.1.0) don't include it, so copy its rules into `/etc/fail2ban/filter.d/openvpn.local`.

### Why does my fail2ban openvpn jail not match anything?

The filter expects the syslog tag `ovpn-server`, which comes from Debian's `openvpn@server` unit. The upstream `openvpn-server@server` unit logs as `openvpn`. Use `filter = openvpn[_daemon="(?:ovpn-\S+|openvpn)"]` and point the jail at the right journal unit or log file.

### How do I configure the fail2ban openvpn jail for UDP 1194?

Set `port = 1194` and `protocol = udp` in the `[openvpn]` section of `jail.local`. The stock jail defaults to port 443 over TCP, so without those two lines bans land on the wrong port and protocol and the attacker can keep connecting.

### Where does OpenVPN log failed connections?

With systemd and no `log`/`log-append` option, in the journal: `journalctl -u openvpn-server@server`. With `log-append`, in the file you named, often `/var/log/openvpn/openvpn.log`. With rsyslog, also in `/var/log/syslog`. The `status` file isn't a log and doesn't work for fail2ban.

### Should I use fail2ban with OpenVPN tls-crypt?

It does no harm, but it has less to do. With `tls-crypt`, unauthenticated packets are dropped silently, so scanners rarely produce a log line. fail2ban still bans clients that hold the key but present a bad certificate or password, which is where it adds value.
