App Support.

We're here to help.



Duo Two-Factor Authentication with OpenVPN and Viscosity

After setting up your own OpenVPN server, you may want to enhance its security by requiring two-factor authentication. This guide will expand on setting up an OpenVPN server on Ubuntu by adding Duo Security support using Viscosity's built-in Challenge/Request support.

Duo authentication adds an extra step after the user's username and password have been accepted. Users can enter a Duo passcode, or request a push notification, phone call, or SMS passcode.

Preparation

For this guide, we assume:

  • You have already installed the latest version of Ubuntu LTS (26.04 at time of writing)
  • You have root access to this installation
  • You have already set up and tested an OpenVPN server using our Setting up an OpenVPN server with Ubuntu guide
  • You already have a copy of Viscosity installed on your client device and already set up for this server
  • You have a Duo Security account with your VPN users enrolled

This guide assumes you are already able to connect to your OpenVPN server using certificate/key authentication. This guide will add two more authentication steps: a username and password using PAM, and a challenge request using Duo Security.

PAM authentication is the simplest form of username/password authentication we can use with OpenVPN. By default this means users authenticate using normal local Ubuntu accounts, so you do not need to manage a separate username/password database. Please note that each user's PAM username must match their username in Duo Security.

If you wish to use Duo Security as your primary authentication method, i.e. without a local password, please refer to Duo Security's own OpenVPN plugin and guide - https://duo.com/docs/openvpn

This guide uses the example Duo authentication script from SparkLabs' OpenVPN two-factor authentication extensions. You can modify this script to use another primary authentication method, such as LDAP, if required.

Ubuntu

The following instructions for Ubuntu assume that you have already set up an OpenVPN server using our Setting up an OpenVPN server with Ubuntu guide.

Duo Security Setup

To get started, we need to do some quick setup in Duo Security's Admin Panel. We'll assume you already have Duo Security set up for 2FA via Duo Mobile, Duo passcodes, SMS, or phone callbacks.

In the Duo Admin Panel, add or protect an Auth API application. Duo's interface may change over time, but this is typically found under Applications or the Application Catalog.

Once the Auth API application has been created, take a copy of the Integration key, Secret key and API hostname. We'll need these later. Give the application a name you'll recognise, make sure the users or groups who should be able to connect are allowed to access it, and then save your changes.

Creating VPN Users

The Duo authentication script uses PAM for username/password authentication. This means users authenticate using normal local accounts by default.

If you need to create a new local user, enter the following and follow the prompts:

sudo adduser exampleuser

Replace exampleuser with the username you want to create. This username should match the user's Duo Security username.

Installing the Duo Authentication Script

First, update your server and install the required packages:

sudo apt update
sudo apt install -y python3 python3-pam python3-duo-client wget ca-certificates

Next, create a directory for the OpenVPN authentication script:

sudo install -d -m 0755 /usr/local/lib/openvpn

Download the Duo authentication script:

sudo wget -O /usr/local/lib/openvpn/openvpn_pam_duo.py https://raw.githubusercontent.com/thesparklabs/openvpn-two-factor-extensions/master/duo-security-script/openvpn_pam_duo.py

After the file is in place, make it executable:

sudo chmod 0755 /usr/local/lib/openvpn/openvpn_pam_duo.py

Setting up OpenVPN

Now we can enable Duo authentication for the OpenVPN server. Edit your server configuration:

sudo nano /etc/openvpn/server/server.conf

Scroll to the bottom of the configuration and add the following:

# Duo Security and PAM authentication
script-security 2
auth-user-pass-verify /usr/local/lib/openvpn/openvpn_pam_duo.py via-file
client-crresponse /usr/local/lib/openvpn/openvpn_pam_duo.py

# Duo Auth API details
setenv duoIntegrationKey YOUR_INTEGRATION_KEY
setenv duoSecretKey YOUR_SECRET_KEY
setenv duoApiHost api-XXXXXXXX.duosecurity.com

# Allow OpenVPN to reauthenticate during renegotiation without prompting for
# Duo again.
auth-gen-token

Replace YOUR_INTEGRATION_KEY, YOUR_SECRET_KEY, and api-XXXXXXXX.duosecurity.com with the values copied from Duo Security's Admin Panel.

Save the configuration and restart OpenVPN:

sudo systemctl restart openvpn-server@server

To check the server status, enter:

sudo systemctl status openvpn-server@server

To which it should reply with:

Active: active (running)

If the server is not listed as active (running), you can view the OpenVPN server log by entering:

sudo journalctl -u openvpn-server@server -xe

The script will automatically create a local pending-challenge database the first time a Duo challenge is generated. For new installations this database is stored at:

/var/lib/openvpn-duo/pending_challenges.sqlite3

Setting up Viscosity

Now the server is set up, you only need to make a single change to your connection in Viscosity:

  • Open Viscosity's Preferences and edit your connection.
  • Go to the Authentication tab and tick 'Use Username/Password authentication'


     

  • Save the connection

Upgrading From an Older Duo Script

Older versions of this guide (prior to 2026) required adding a static-challenge command to the Advanced commands for the Viscosity connection. This is no longer needed, as the updated script sends the Duo challenge prompt to Viscosity automatically.

If you are upgrading from an older copy of the script, edit the connection in Viscosity, go to the Advanced tab, and remove any line similar to:

static-challenge "Enter Duo Security Response" 0

Save the connection after removing it.

Using Viscosity

Now that the changes to your configuration in Viscosity are finished, you can connect to the VPN server to test the connection.

When you connect, you'll be prompted to enter your username and password. After these are accepted, Viscosity will prompt you to enter a Duo response.

Enter a Duo passcode to authenticate using a code from Duo Mobile, SMS, or another Duo passcode source. You can also type push to receive a Duo Push notification, phone to receive a phone callback, or sms to request an SMS passcode. If you request an SMS passcode, you may need to reconnect and enter one of the passcodes you receive.


 

Advanced

Customising the Duo Challenge

By default, users have 300 seconds to answer the Duo challenge, and the challenge text is:

Enter a Duo passcode, or type push, phone, or sms

You can customise these values by adding duoChallengeTimeout and duoChallengeText to your OpenVPN server configuration:

setenv duoChallengeTimeout 300
setenv duoChallengeText "Enter your Duo passcode, or type push, phone, or sms"

After changing these values, restart OpenVPN:

sudo systemctl restart openvpn-server@server

Using a Different Pending-Challenge Database Path

The script stores pending Duo challenges in a local SQLite database so it can match the user's challenge response to their original login attempt. The default database path is:

/var/lib/openvpn-duo/pending_challenges.sqlite3

Advanced administrators can use a different database path by setting duoChallengeDbPath in the OpenVPN server configuration:

setenv duoChallengeDbPath /path/to/pending_challenges.sqlite3

After changing this value, restart OpenVPN:

sudo systemctl restart openvpn-server@server