Server crashes when DCO key install fails for a peer the kernel already removed
Describe the bug
On a busy server with DCO enabled (Linux in-tree ovpn module), the whole openvpn process sometimes exits with:
kernel error: ovpn: no peer with id 21 to set key for
Impossible to install key material in DCO: No such file or directoryAll connected clients get dropped when this happens (systemd restarts it, but every client has to reconnect). It happened 4 times on one of our servers so far.
Looking at the log right before the exit, the kernel had already stopped knowing about that peer. There were a few "received data for a non-existing peer" lines for the same id a couple of seconds earlier:
2026-09-24 14:14:04 ovpn_handle_peer: received data for a non-existing peer 21
...
2026-09-24 14:14:10 ovpn_handle_peer: received data for a non-existing peer 21
2026-09-24 14:14:11 ovpn_handle_peer: received data for a non-existing peer 299
2026-09-24 14:14:12 kernel error: ovpn: no peer with id 21 to set key for
2026-09-24 14:14:12 Impossible to install key material in DCO: No such file or directoryMy guess is the kernel deleted the peer on its own (we have a lot of TCP clients, so probably the socket closed or keepalive expired) and userspace hadn't processed the delete notification yet when it went to install a new key for it. So it's a race, but the result is that one client's failure takes down the whole server.
The problem is in init_key_contexts() in ssl.c, where a failed init_key_dco_bi() is M_FATAL. A bit further down, in tls_session_update_crypto_params_do_work(), a failed dco_set_peer() for the same kind of reason just returns false and only that client is affected. I think key install should behave the same way.
To Reproduce
I can't reproduce it on demand. It shows up on servers with a few hundred clients, a mix of UDP and TCP, with DCO on. The "non-existing peer" messages are constant in the background (a few thousand per hour on each server), and every now and then one of them lines up with a key install and the process exits.
Expected behavior
The failing client gets disconnected (or its key state goes to error) and the server keeps running for everyone else.
This is what we're running now, and it's been fine so far. init_key_contexts() returns bool and generate_key_expansion() fails on it, so the existing error handling in the callers takes over (on the server side that ends up as SIGUSR1 for that one instance):
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -1395,7 +1395,7 @@
-static void
+static bool
init_key_contexts(struct key_state *ks, struct tls_multi *multi, const struct key_type *key_type,
bool server, struct key2 *key2, bool dco_enabled)
{
@@ -1414,7 +1414,12 @@
int ret = init_key_dco_bi(multi, ks, key2, key_direction, key_type->cipher, server);
if (ret < 0)
{
- msg(M_FATAL, "Impossible to install key material in DCO: %s", strerror(-ret));
+ /* The kernel may already have deleted this peer (e.g. its TCP
+ * socket closed) before userspace processed the notification.
+ * Fail only this session instead of exiting the whole server. */
+ msg(D_TLS_ERRORS, "TLS Error: Impossible to install key material in DCO: %s",
+ strerror(-ret));
+ return false;
}
@@ -1437,6 +1442,7 @@
init_key_ctx_bi(key, key2, key_direction, key_type, "Data Channel");
}
+ return true;
}
@@ -1546,7 +1552,11 @@
- init_key_contexts(ks, multi, &session->opt->key_type, server, &key2, session->opt->dco_enabled);
+ if (!init_key_contexts(ks, multi, &session->opt->key_type, server, &key2,
+ session->opt->dco_enabled))
+ {
+ goto exit;
+ }
ret = true;Happy to send it as a proper patch to the mailing list / gerrit if this looks like the right direction.
Version information (please complete the following information):
- OS: Ubuntu 26.04 LTS, kernel 7.0.0-31-generic (in-tree ovpn module)
- OpenVPN version: 2.7.7 built from the release tarball,
[SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [MH/PKTINFO] [AEAD] [DCO] - Peers: mixed, mostly Android and iOS OpenVPN Connect plus some openvpn 2.x clients, both UDP and TCP
Additional context
Server mode, around 250-450 clients per server, verb 0, client-connect script enabled. The config has no --auth (AEAD only). We see the "non-existing peer" noise on two separate servers, and both had unexplained restarts. I only caught the fatal line on one of them.
Source: OpenVPN/openvpn