OpenVPN with Two-Factor authentication

Roman Vynar
The Quiq Blog
Published in
3 min readApr 21, 2021

This article explains how to setup 2FA with OpenVPN server and how it’s supposed to work with VPN clients.

OpenVPN is one of the most popular open-source VPN server software. Nowadays when security plays a critical role we try to protect various authorization services by implementing multi-factor authentication. This introduces “something you have” in addition to “something you know” to verify your identity. In case of VPN, it is notably important.

Assuming you have already got OpenVPN server<>clients working and you just need to add 2FA capability. Let’s say we want to ask for OTP (one-time password) at least once a week, when started using a different client, when coming from a different IP or whenever user fully disconnects. Laptop sleep/awake is not considered as full disconnect unless your VPN client does this explicitly.

Client config

The client should do password auth with static challenge using the following options:

auth-user-pass
static-challenge "Enter your OTP" 0

Then the client software such as Tunnelblick, Viscosity will prompt for an OTP code after the regular username/password:

Server config

Server config should include the following options:

# User auth script.
auth-user-pass-verify /etc/openvpn/openvpn_otp_auth.py via-file
script-security 2
tmp-dir /dev/shm
# Allow to reconnect automatically w/o triggering a static-challenge prompt for OTP.
# Generate auth-token for 12h on every control channel/connection renegotiation.
auth-gen-token 43200
# Perform data channel renegotiation every 1h (default), so the server can validate auth-token.
reneg-sec 3600

Note, it’s just an excerpt related to 2FA and you need other options to employ a secure setup, TLS authentication etc. The script openvpn_otp_auth.py mentioned is the key item and we have an example for you below. However, at first let’s see how things work together.

How it works

There are 2 channels established when user session is created: control channel (CC) and data channel(DC).

openvpn_otp_auth.py is run only on CC renegotiation which is triggered on specific events listed on the picture. Only from the script we can do all kinds of session validation, password and OTP code checks.

DC renegotiation doesn’t involve the script run and only checks auth-token expiration and when expired it triggers CC renegotiation when our script is run.

Python script

Here is a sample of openvpn_otp_auth.py written in Python: https://github.com/roman-vynar/random-scripts/blob/main/openvpn_otp_auth_sample.py

You can get a clue what’s done there and use it on your own. The script does a password check, OTP code check, VPN client version check and IP validation.

Such setup should work great with Tunnelblick (however you need to disable “Disconnect when computer goes to sleep” in Settings > Advanced) and Viscosity VPN clients.

Thanks for reading!

--

--