Implementing Google Authenticator 2FA for OpenVPN Client Certificates (.ovpn) Without Shell Access


2 views

Standard OpenVPN Google Authenticator implementations typically require:

  1. Shell access to server
  2. User home directories
  3. Direct execution of google-authenticator binary

This becomes problematic when using client certificate authentication via .ovpn files where users don't have shell accounts. Here's how to implement it properly:

First ensure your OpenVPN server has these packages:

sudo apt-get install openvpn libpam-google-authenticator

Edit /etc/pam.d/openvpn:

# Standard UNIX authentication
auth requisite pam_unix.so
# Google Authenticator
auth required pam_google_authenticator.so nullok

The nullok parameter allows users without 2FA to still authenticate.

Add these to your OpenVPN server config (server.conf):

plugin /usr/lib/openvpn/openvpn-plugin-auth-pam.so openvpn
username-as-common-name
client-cert-not-required

Create a helper script (/usr/local/bin/generate_otp.sh):

#!/bin/bash
USERNAME=$1
SECRET_FILE="/etc/openvpn/otp-secrets/$USERNAME"

google-authenticator -t -d -f -r 3 -R 30 -w 1 -s "$SECRET_FILE"

Then run for each user:

sudo bash /usr/local/bin/generate_otp.sh username

Modify the .ovpn file to include:

auth-user-pass
script-security 2
auth-retry interact

Test the connection with:

openvpn --config client.ovpn

You should be prompted for both password and OTP code.

For a more advanced approach using TLS crypt, add to server config:

tls-crypt /etc/openvpn/tls-crypt.key

Generate the key with:

openvpn --genkey tls-crypt /etc/openvpn/tls-crypt.key

When working with OpenVPN configurations that rely solely on .ovpn files without shell access for users, traditional Google Authenticator integration approaches won't work. The standard PAM-based method requires:

  1. User shell accounts
  2. Google Authenticator binary execution
  3. Configuration files in user home directories

We'll implement a solution using OpenVPN's plugin system with the openvpn-otp project as our base. This approach:

1. Intercepts authentication requests
2. Validates OTP tokens separately
3. Doesn't require shell access
4. Works with existing .ovpn configurations

Server-Side Configuration

First, install required packages on your Debian VM:

sudo apt-get update
sudo apt-get install openvpn libpam-google-authenticator git build-essential

Clone and build the openvpn-otp plugin:

git clone https://github.com/evgeny-gridasov/openvpn-otp.git
cd openvpn-otp
make
sudo cp openvpn-otp.so /etc/openvpn/

Configuring the OpenVPN Server

Add these lines to your server configuration (/etc/openvpn/server.conf):

plugin /etc/openvpn/openvpn-otp.so /etc/openvpn/otp-secrets
reneg-sec 0

Create the OTP secrets file:

sudo touch /etc/openvpn/otp-secrets
sudo chmod 600 /etc/openvpn/otp-secrets

Generating User Credentials

For each user, generate a Google Authenticator secret:

google-authenticator -t -d -f -r 3 -R 30 -w 3 -q

Add the secret to /etc/openvpn/otp-secrets in this format:

username BASE32SECRETCODE

Client Configuration Modifications

Modify your .ovpn files to include these authentication directives:

auth-user-pass
auth-nocache
reneg-sec 0

Create a test user with this script:

#!/bin/bash
USERNAME="testuser"
SECRET=$(head -10 /dev/urandom | sha256sum | head -c 32 | base32)
echo "$USERNAME $SECRET" | sudo tee -a /etc/openvpn/otp-secrets
echo "Add this secret to Google Authenticator: $SECRET"

Authentication failures: Ensure time synchronization between server and client devices

Plugin loading errors: Verify plugin path and permissions in server.conf

OTP validation problems: Check the otp-secrets file format and permissions

  • Regularly rotate OTP secrets
  • Monitor authentication logs
  • Consider rate-limiting authentication attempts
  • Keep the otp-secrets file strictly secured

For larger deployments, consider:

  1. LDAP integration with OTP
  2. RADIUS authentication with FreeRADIUS
  3. Commercial solutions like Duo Security