Use OpenLDAP TOTP as authentication for VPN

For this setup, we will be using FreeRADIUS and compiled OpenLDAP from source along with the TOTP (Time-based One Time Password ) module on CentOS 7. We will be using TOTP as authentication for OpenLDAP and use it as authentication for VPN if your router supports L2TP/IP Protocol. L2TP tunneling protocol can be deployed without any additional software on PCs, Macs, iOS devices, and Android devices, since all of these operating systems natively support L2TP VPN connections.


yum update
yum install make gcc openssl-devel libtool-ltdl-devel libdb-devel cyrus-sasl-devel git tcpdump vim libtool-ltdl-devel
yum group install "Development Tools"

Download OpenLDAP and compile it along with TOTP. Check for any error during compilation. use ./configure –help for other available options.

cd /opt/
git clone
cd openldap
./configure --enable-modules --enable-dynamic --enable-syslog --enable-spasswd --enable-debug --enable-crypt --enable-spasswd
make depend
make -j$(nproc)
make install

Go to the slapd module directory to enable OpenLDAP to manage TOTP and install it.

cd contrib/slapd-modules/passwd/totp/
make install

slapd-totp.c provides support for RFC 6238 TOTP Time-based One Time Passwords in OpenLDAP using SHA-1, SHA-256, and SHA-512.

TOTP Libraries will be installed in:

Run the slappasswd command to set an LDAP root password and save the output because we are going to need it to configure OpenLDAP:


Edit slapd.conf file and load the TOTP module. Define rootpw here which was generated earlier.

vim /usr/local/etc/openldap/slapd.conf
include /usr/local/etc/openldap/schema/core.schema
include /usr/local/etc/openldap/schema/cosine.schema
include /usr/local/etc/openldap/schema/nis.schema
include /usr/local/etc/openldap/schema/inetorgperson.schema
pidfile /usr/local/var/run/
argsfile /usr/local/var/run/slapd.args
modulepath /usr/local/libexec/openldap
password-hash {TOTP1}
database mdb
maxsize 1073741824
suffix "dc=bidhankhatri,dc=com,dc=np"
rootdn "cn=ldapadmin,dc=bidhankhatri,dc=com,dc=np"
rootpw {SSHA}xxxxxxxxxxxxxxxxxx
directory /usr/local/var/openldap-data
index objectClass eq
overlay totp

Create a directory for LDAP Database.

mkdir /usr/local/var/openldap-data

Create systemd file for slapd service.

vim /etc/systemd/system/slapd.service
Description=OpenLDAP Server Daemon

Test the configuration using the following command:

slaptest -u
 config file testing succeeded

Now start LDAP Daemon and enable it on boot.

systemctl start slapd
systemctl enable slapd

And now create a base.ldif file for your domain:

vim base.ldif

dn: dc=bidhankhatri,dc=com,dc=np
dc: bidhankhatri
objectClass: domain
objectClass: top
# users,
dn: ou=users,dc=bidhankhatri,dc=com,dc=np
ou: users
objectClass: organizationalUnit
objectClass: top
ldapadd -x -D "cn=ldapadmin,dc=bidhankhatri,dc=com,dc=np" -W -f base.ldif

Enter the root password when prompted.
Create another ldif file for users and add those users to LDAP Database.

vim users.ldif

# bidhan.khatri, users,
dn: uid=bidhan.khatri,ou=users,dc=bidhankhatri,dc=com,dc=np
objectClass: inetOrgPerson
objectClass: top
uid: bidhan.khatri
ou: users
cn: Bidhan Khatri
sn: bidhan.khatri
ldapadd -x -D "cn=ldapadmin,dc=bidhankhatri,dc=com,dc=np" -W -f user.ldif

Managing Password

There is a requirement for the secret key, the minimum size has to be 128 bit, it is recommended to extend this to 160 bit (20bytes).
Here, I am using 21 bytes strings as a password for user bidhan.khatri.

echo "helloworldnepal12345" | wc -c

As there is a password hashing scheme defined in slapd.conf {TOTP1}, the attribute userPassword may now be modified by means of LDAP Password Modify Extended Operation.

Now define a password for user bidhan.khatri. and also need to define ldapadmin password.

ldappasswd -x -D "cn=ldapadmin,dc=bidhankhatri,dc=com,dc=np" -W -S "uid=bidhan.khatri,ou=users,dc=bidhankhatri,dc=com,dc=np"

This string will be internally converted into a base32 string, while the output of ldapsearch or slapcat will be additionally base64 encoded.

Search LDAP Directory to verify. Your new users should be listed.

ldapsearch -x -b 'dc=bidhankhatri,dc=com,dc=np'
# bidhan.khatri, users,
dn: uid=bidhan.khatri,ou=users,dc=bidhankhatri,dc=com,dc=np
objectClass: inetOrgPerson
objectClass: top
uid: bidhan.khatri
ou: users
cn: Bidhan Khatri
sn: bidhan.khatri

Just to verify the bytes used by the shared key.


Share that secret key to people either by generating a QR code or through sending by email.

User now adds this shared key to their authenticator. In the Google Authenticator app, they would select ‘Enter a setup key’ option and then any account name and below that name there is key section where secret key need to be set. and after that click on add.

Trailing ‘=’ signs in base32 encoding for TOTP will fail with Google Authenticator in iOS but it’s fine in android OS. so it’s better to use fixed-size password length which doesn’t involve padding in the secret key.

If you want to encode secret keys in QR codes as a URI then follow below format. otpauth://TYPE/LABEL?PARAMETERS


Default period is of 30sec. Though you compile TOTP with a time step of 60 seconds, authenticator apps like Google Authenticator will not work with a defined period of seconds. 30sec is the default time in Google Authenticator. You can not change this period in google authenticator. The period parameter is ignored by Google Authenticator.

Now check LDAP authentication for the user.

ldapwhoami -x -D "uid=bidhan.khatri,ou=users,dc=bidhankhatri,dc=com,dc=np" -W
Enter LDAP Password: (Enter token from Google Authenticator)


Supported authenticator

  1. Google Authenticator
  2. Sophos Authenticator
  3. FreeOTP

Installing and Configuring FreeRADIUS

Install freeradius package.

yum install freeradius freeradius-utils freeradius-ldap

Edit radius default file and enable LDAP auth type.

cat /etc/raddb/sites-enabled/default | grep -v "#" | sed '/^$/d'

change these fields only.

authorize {
 # The ldap module reads passwords from the LDAP database.
authenticate {
    Auth-Type PAP {
Auth-Type LDAP {
cd /etc/raddb/mods-enabled/
ln -s ../mods-available/ldap ldap

Define LDAP details.

cat /etc/raddb/mods-enabled/ldap | grep -v "#" | sed '/^$/d'

server = 'localhost'
port = 389
identity = 'cn=ldapadmin,dc=bidhankhatri,dc=com,dc=np'
password = secret(your ldap password)
base_dn = 'dc=bidhankhatri,dc=com,dc=np'

Define Your Router client along with secret key in /etc/raddb/client.conf file like below.

cat /etc/raddb/clients.conf | grep -v "#" | sed '/^$/d'
client router_name {
ipaddr =
secret = yoursecret

Start and enable radius service.

systemctl start radiusd
systemctl enable radiusd

Now test radius server by querying directly with requests through radtest command. Here, “537769” is TOTP for user bidhan.khatri. your reply should be Access-Accept

radtest bidhan.khatri 537769 1812 testing123
Sent Access-Request Id 59 from to length 83
	User-Name = "bidhan.khatri"
	User-Password = "537769"
	NAS-IP-Address = x.x.x.x
	NAS-Port = 1812
	Message-Authenticator = 0x00
	Cleartext-Password = "537769"
Received Access-Accept Id 59 from to length 20

That’s it. Now you can point radius server IP from your router end to authenticate users through OpenLDAP TOTP for VPN access.