TOTP Two-Factor Authentication with OpenVPN and Viscosity
After setting up your own OpenVPN server, you may want to enhance it's security. One way to do that is to use 2FA (Two Factor Authentication). This adds another security measure to prevent unwanted users connecting to your server. One type of 2FA is TOTP (Time-Based One-Time Password Algorithm) with an Authenticator application, for example Google or Microsoft Authenticator on your phone. This guide will expand on setting up an OpenVPN server on Ubuntu by adding TOTP support to that server using Viscosity's built in Challenge/Request support.
Preparation
For this guide, we assume:
- You have already installed the latest LTS version of Ubuntu (22.04 at time of writing)
- You have root access to this installation
- You have already an OpenVPN server running using one of our guides
- You already have a copy of Viscosity installed on your client device and already setup for this server
- You have a computer or phone with the Google or Microsoft Authenticator app installed
This guide assumes you have followed one of our server setup guides and you are already able to connect to the server we will be modifying using certificate/key authentication. This guide will add two more authentication steps. A username and password using PAM, and a challenge request using a TOTP authentication app.
PAM authentication is the simplest form of username/password authentication we can use with OpenVPN. PAM uses the Ubuntu's user management to authenticate against so we don't need to manage an extra database of username and passwords. If you want to add a new user to be able to authenticate, you can simply add the new user with the useradd command in Ubuntu.
This guide should only be used as an example for setting up TOTP on your server. The script used below is designed as an example only.
Ubuntu
The follow instructions for Ubuntu assume that you have already setup an OpenVPN server using our Setting up an OpenVPN server with Ubuntu and Viscosity guide.
Modifying the Server
To get started, we need to update/upgrade our Ubuntu installation to prevent any conflicts.
sudo apt update
sudo apt upgrade
Next install Python3 and PIP3, this will allow us to install extra packages, install it by running the following from your Ubuntu server's command line:
sudo apt install python3 python3-pip
Next, install PAM and PyOTP using PIP:
sudo pip3 install python-pam pyotp
For TOTP to work correctly, we need to make sure the time and date are correct on your server. Run the below to turn NTP off and on again and force a time/date update:
sudo timedatectl set-ntp off sudo timedatectl set-ntp on
Next you will need to download our script for OpenVPN to use. Do this with:
cd /etc/openvpn
sudo wget https://raw.githubusercontent.com/thesparklabs/openvpn-two-factor-extensions/master/TOTP/openvpn_pam_totp.py
After the file is in place, we need to make it executable by running:
sudo chmod +x /etc/openvpn/openvpn_pam_totp.py
Next, we'll generate a secret for a user. Repeat this as many times as you need, once for each user, replacing 'username' below with the actual username:
sudo /etc/openvpn/openvpn_pam_totp.py --genkey username
This will output three lines like below:
username SECRET12345 otpauth://totp/OpenVPN%20Server:test?secret=SECRET12345&issuer=OpenVPN%20Server
The first line is the username you entered, the second line is the user's 2FA Secret, and the third line is a URL which you can use to display a QR code for the user to scan if you wish on a webpage you might have call this script.
To setup your Authenticator app, add a new account, if asked what type, select "Other", then select the option to enter the code manually. Enter a name for the account you will recognise, the Secret, and tap Finish/Done.
Finally, we need to modify the OpenVPN server configuration. First, stop the server using:
sudo systemctl stop openvpn@server
Then edit the server configuration using:
sudo nano /etc/openvpn/server.conf
Scroll all the way to bottom then on a new line add the following:
#Use a script to authenticate username/password and TOTP auth-user-pass-verify openvpn_pam_totp.py via-env script-security 3 #Auth-gen-token for renegotiation without needing to use token auth-gen-token
Now find the following two lines:
user nobody group nobody
And comment them out, changing them to:
#user nobody #group nobody
Save the configuration then restart the server:
sudo systemctl start openvpn@server
Setting up Viscosity
Now the server is setup, we need to make two small changes to our configuration in Viscosity.
- Open Viscosity's Preferences and edit your connection.
- Go to the Authentication tab and tick 'Use Username/Password authentication'
- Go to the Advanced tab, then on a new line add:
static-challenge "Enter Auth Code" 0
- Save the connection
The comment in quotes for the static-challenge command you added, i.e. "Enter Auth Code" is what is displayed to the user when the TOTP code needs to be entered. You can change this message to whatever you like.
Now that changes to your configuration in Viscosity have been made, connect to the server to test the changes! You will see an extra Password window appear during the connection process, simply enter the code displayed on your phone app.