Skip to content

OpenVPN RADIUS authentication unreliable with NPS+MFA push backends #10270

@fmotzet

Description

@fmotzet

Important notices

Before you add a new report, we ask you kindly to acknowledge the following:

Describe the bug

OpenVPN authentiaction via Radius and NPS is unreliable for useres who take more than 10 seconds to approve the push notification sent to their phone. The same NPS+MFA backend works finr for IPsec authentication via the same exact firewall, meaning the problem is specific to the OpenVPN auth path inside of OPNsense.

I believe this is because of two reasons:

OPNsense\Auth\Radius hardcodes the libradius retry at 3. There is no plumbing to the ui to override this: src/opnsense/mvc/app/library/OPNsense/Auth/Radius.php:87 (private $maxRetries = 3;)

Second, src/opnsense/scripts/openvpn/user_pass_verify.php:109 iterates over the OpenVPN server's authmode it move to the next enry on any non-success return from $authenticator->authenticate() including the radius timeout. This leads to a problem when at least 2 NPS Servers are listed for redundancy:
The first invocation of user_pass_verify.php produces an MFA push from NPS1 and, after NPS1 times out, a second push from NPS2 (each NPS tracks its own pending MFA sessions independently). The script exits with failure, OpenVPN retries the auth, and a fresh user_pass_verify.php invocation iterates the same authmode list, starting again at NPS1. NPS1's application-level dedupe recognizes the same "logical" session and issues no further push to the users device. If the user takes to long and now taps on either of the 2 original pushes, the NPS sends Access-Accept on the original Radius converstation. However the UDP socket from OPNsense was already closed, when authenticate(). The currently running radius_send_request() is listening on a different socket and never sees the Accept.

Result: the user has approved on their phone but the OpenVPN connection still fails, and they cannot log in.

IPsec is unaffected because its RADIUS auth runs through strongswan's eap-radius plugin, a separate code path with its own retransmit policy.

To Reproduce

  1. Configure two RADIUS auth servers under System > Access > Servers, pointing at two Microsoft NPS servers running the NPS Extension for Microsoft Entra MFA (or any RADIUS server that returns Access-Accept only after an asynchronous user approval)
  2. Edit an OpenVPN server instance and select both RADIUS servers under "Authentication"
  3. Connect an OpenVPN client. When prompted for the MFA push, wait ~15+ seconds before tapping approve.
  4. Connection fails even after clicking accept now

Expected behavior

A user with a configured MFA approval time of e.g. 60 seconds should be able to take up to 60 seconds to tap approve and have the OpenVPN connection succeed on the first attempt this would match the IPsec behavior on the same firewall against the same NPS.

Describe alternatives you considered

  • Removing one of the NPS servers from authmode reduces but does not eliminate the problem: with the hardcoded 3 retries, even a single-NPS flow still gives up too quickly. After the script exits, OpenVPN re-invokes it for a fresh attempt, which produces a new Radius conversation with a new Identifier, and NPS-side dedup once again suppresses any new push.

  • Telling users to tap quickly is not a workable solution in production.

Screenshots

If applicable, add screenshots to help explain your problem.

Relevant log files

One Openvpn connection produces 3 requests to each nps server's ip then nothing:

tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on ixl2_vlan30, link-type EN10MB (Ethernet), snapshot length 262144 bytes
16:37:56.526196 IP 10.20.30.11.29398 > 10.20.30.31.1812: RADIUS, Access-Request (1), id: 0x6f length: 120
16:37:59.528377 IP 10.20.30.11.29398 > 10.20.30.31.1812: RADIUS, Access-Request (1), id: 0x6f length: 120
16:38:02.536285 IP 10.20.30.11.29398 > 10.20.30.31.1812: RADIUS, Access-Request (1), id: 0x6f length: 120
16:38:05.537594 IP 10.20.30.11.38712 > 10.20.30.32.1812: RADIUS, Access-Request (1), id: 0x4f length: 120
16:38:08.538473 IP 10.20.30.11.38712 > 10.20.30.32.1812: RADIUS, Access-Request (1), id: 0x4f length: 120
16:38:11.539386 IP 10.20.30.11.38712 > 10.20.30.32.1812: RADIUS, Access-Request (1), id: 0x4f length: 120

Additional context

A configurable radius_max_retries field on each RADIUS auth server (symmetric to the existing radius_timeout) plus a small change to user_pass_verify.php to skip cascading after a no-response from the upstream RADIUS server would resolve the issue end-to-end.

Environment

OPNsense 26.1.6_2 (amd64), CARP HA pair.
RADIUS backend: Microsoft NPS with the NPS Extension for Microsoft Entra MFA.
Two NPS servers configured under System: Access: Servers and both selected as the OpenVPN server's authentication backends.

TLDR: Radius authenticator hardcodes maxRetries=3 and user_pass_verify.php cascades on timeout, breaking interactive 2FA

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions