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.

image-center

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 https://github.com/openldap/openldap.git
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
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:
/usr/local/libexec/openldap

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

slappasswd

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/slapd.pid
argsfile /usr/local/var/run/slapd.args
 
modulepath /usr/local/libexec/openldap
moduleload pw-totp.so
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
[Unit]
Description=OpenLDAP Server Daemon
After=syslog.target network-online.target
 
[Service]
Type=forking
PIDFile=/usr/local/var/run/slapd.pid
 
ExecStart=/usr/local/libexec/slapd
 
[Install]
WantedBy=multi-user.target

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

# bidhankhatri.com.np
dn: dc=bidhankhatri,dc=com,dc=np
dc: bidhankhatri
objectClass: domain
objectClass: top
 
# users, bidhankhatri.com.np
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, bidhankhatri.com.np
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
mail: bidhan.khatri@bidhankhatri.com.np
userPassword:
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
21

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, bidhankhatri.com.np
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
mail: bidhan.khatri@bidhankhatri.com.np
userPassword:: e1RPVFAxfU5CU1dZM0RQTzVYWEUzREVOWlNYQVlMTUdFWkRHTkJW
echo "e1RPVFAxfU5CU1dZM0RQTzVYWEUzREVOWlNYQVlMTUdFWkRHTkJW" | base64 -d
{TOTP1}NBSWY3DPO5XXE3DENZSXAYLMGEZDGNBV

Just to verify the bytes used by the shared key.

echo "NBSWY3DPO5XXE3DENZSXAYLMGEZDGNBV" | wc -c
33

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.

NOTE:
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

otpauth://totp/BDN:bidhan.khatri?secret=NBSWY3DPO5XXE3DENZSXAYLMGEZDGNBV&issuer=BDN&period=30&digits=6&algorithm=SHA1

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.
Ref: https://github.com/google/google-authenticator/wiki/Key-Uri-Format

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)

dn:uid=bidhan.khatri,ou=users,dc=bidhankhatri,dc=com,dc=np

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 {
    filter_username
    preprocess
    files
    -sql
 
 # The ldap module reads passwords from the LDAP database.
    ldap
 
    expiration
    logintime
    pap
}
authenticate {
    Auth-Type PAP {
        ldap
    }
 
Auth-Type LDAP {
    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 = xxx.xxx.xxx.xxx
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 127.0.0.1 1812 testing123
Sent Access-Request Id 59 from 0.0.0.0:38107 to 127.0.0.1:1812 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 127.0.0.1:1812 to 0.0.0.0:0 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.

Comments