This document describes the process of authenticating Users in OpenVPN against AD or LDAP.

How does this extension work?

The extension is a script that is called by the OpenVPN server which starts a query towards AD/LDAP asking if the given user is is the specified OU or below, a person object and if the username(UPN)/password is correct.

  • User inserts the LoginData when connecting through OpenVPN client
  • OpenVPN server calls /var/ipfire/ovpnldapauth.sh
  • the bash script queries the AD/LDAP server and returns 0 if the user is authenticated successfully or 1 if the authentication failed

Prerequisites

  • A user to search AD/LDAP
  • Shell access to IPFire
  • You must enable "Additional configuration" in the Advanced Server-Settings Menu

openvpn-server configuration

#add this line to /var/ipfire/ovpn/scripts/server.local.conf
auth-user-pass-verify /var/ipfire/ovpnldapauth.sh via-env
#uncomment the next line to use the same connection file and client certificate for all connections
#duplicate-cn

duplicate-cn allows you to create one single connection for all users sharing the same access rights to networks on the IPFire side and the same dhcp options, which makes administration of the same access groups much easier (e.g.: employees vs. contractors). Furthermore, it allows the same username to connect multiple times, for example from a laptop, a mobile phone and a tablet. Since you activate a 2-factor authentication (certificate on the device + AD/LDAP user and password) there is no security issue in reusing the certificate for all your devices since you still need to provide a valid username and password of an active user in your directory. To increase security you can use a password for the certificate of this connection created, which might be depending on your use-case unpractical (e.g.: always-on VPN, VPN on boot or through service).

openvpn-client configuration

# add this line to /var/ipfire/ovpn/scripts/client.local.conf
auth-user-pass

OVPN Authentication

#save this file as /var/ipfire/ovpnldapauth.sh
#!/bin/bash
searchDN = "DC=contoso,DC=com"
searchUser = "CN=ipfire,OU=users,DC=contoso,DC=com"
searchUserPW = "password"
LDAPHost = "10.0.0.1"

RES=$(echo "$username $password" | /usr/lib/squid/basic_ldap_auth -b "$searchDN" -f "(&(userPrincipalName=%s)(objectClass=Person))" -D "$searchUser" -w "$searchUserPW" -R -H $LDAPHost)

if [ $RES = "OK" ]
then
 exit 0
else
 exit 1
fi



Change user to nobody and rights to 0755

If you want your users to logon without the domain name, change userPrincipalName to sAMAccountName
You might also want to follow the suggestion and save the password of the binding user in a separate file wile using -W instead of -w to provide the password. See https://community.ipfire.org/t/openvpn-auth-user-pass-to-active-directory/9172 for details.

Troubleshooting

A correctly verified authentication is visible in the OpenVPN log as

TLS: Username/Password authentication deferred for username '$username' 

A failed authentication is visible in the OpenVPN log as:

openvpnserver[19462]:   *.*.*.*:36679 TLS Auth Error: Auth Username/Password verification failed fo r peer
openvpnserver[19462]:   *.*.*.*:36679 WARNING: Failed running command (--auth-user-pass-verify): ex ternal program exited with error status: 1

To find out if your LDAP query is correct you can run the command as following from the console:

echo "jan.novak@company.com password123" | /usr/lib/squid/basic_ldap_auth -b "OU=Employees,OU=User,OU=Country,OU=Continent,DC=ad,DC=company,DC=com" -f "(&(userPrincipalName=%s)(objectClass=Person))" -D "CN=SVC OpenVPN,OU=Impersonated accounts,OU=User,OU=Country,OU=Continent,DC=ad,DC=company,DC=com" -w "myVery5trongP@ssword" -R -H 192.168.1.100 -d



assuming:

  • jan.novak@company.com (Jan Novák) is stored in AD/LDAP under OU=Employees,OU=User,OU=Country,OU=Continent,DC=ad,DC=company,DC=com and wants to login with password123 from an OpenVPN client
  • the user binding to AD/LDAP is an impersonated service account stored in AD/LDAP under CN=SVC OpenVPN,OU=Impersonated accounts,OU=User,OU=Country,OU=Continent,DC=ad,DC=company,DC=com with the password myVery5trongP@ssword
  • the AD/LDAP server is located at 192.168.1.100:389, the connection is unencrypted
  • -d as parameter activates the debug mode

A correct authentication results in:

basic_ldap_auth.cc(702): pid=24910 :user filter '(&(userPrincipalName=%s)(objectClass=Person))', searchbase 'OU=Employees,OU=User,OU=Country,OU=Continent,DC=ad,DC=company,DC=com'
basic_ldap_auth.cc(757): pid=24910 :attempting to authenticate user 'CN=Jan Novák,OU=Employees,OU=User,OU=Country,OU=Continent,DC=ad,DC=company,DC=com'
OK



otherwise you get BH Success, which indicates the squid Basic Helper /usr/lib/squid/basic_ldap_auth was running succesfully, but not returning any positive result (OK):

basic_ldap_auth.cc(757): pid=12227 :attempting to authenticate user 'CN=Jan Novák,OU=Employees,OU=User,OU=Country,OU=Continent,DC=ad,DC=company,DC=com''
BH Success

Tweaking

Additionally, you might want to tweak your LDAP searches to fit your installation:

-Z to enable TLS
-c/t for connection and search timeouts
-P for a persistent connection to the LDAP server when authenticating larger user bases or re-authenticate more often without token

See https://www.systutorials.com/docs/linux/man/8-basic_ldap_auth/ for more parameter and switches.