Posted by tmackey on 18 Jun 2013

Posted by tmackey on 18 Jun 2013

Earlier we discussed our selection of FreeRADIUS + OpenLDAP to do TOTP. Now for the good part: how this system is setup. As mentioned earlier, we use LDAP as the general data store for user information. Since LDAP is traditionally used as an authentication store, it already has many of the security controls needed to keep the TOTP secrets secure. It can also perform the first factor of user/password-based authentication. The changes done to LDAP to support this method are minimal. First, an attribute is needed to store the TOTP secret for each user, possibly a second to tell the provisioning system if TOTP is allowed for the user. It’s best to use an attribute specific to this purpose, creating a schema for it if possible (there are several easily found online). For this example we will use an attribute we will call “totpSecret.” Create an LDAP account specifically for running the TOTP authentication, and one for the provisioning system if there isn’t one doing this already. Create a new LDAP ACL, restricting access to the chosen attribute so that only these two users can do anything with it. Example:

[text gutter=”false”]to attrs=totpSecret by dn.base="cn=totpprovisioner,dc=example,dc=com" write by
dn.base="cn=totpauthenticator,dc=example,dc=com" read by * none[/text]

With the basic security items in place and ready, we set up FreeRADIUS.

  1. apt-get install freeradius freeradius-ldap (allow it to install the needed dependencies)
  2. Create a Perl script, totp.pl, that contains the TOTP verification algorithm. An example is up on Evernote’s Github. If you use the sample code, be sure to understand what it is doing, and edit it to fit your environment
  3. Enable rlm_ldap, the new TOTP script and rlm_perl, and make FreeRADIUS call it for auth.
    1. Edit modules/perl and point it to use the totp.pl script.
    2. Edit modules/ldap and set it up to work with your LDAP server (be sure your SSL certs are in place and working!)
    3. In the sites_enabled/*, uncomment the LDAP line in the Authorization section and add TOTP to the Authentication section (just under the similar-looking LDAP block) like so:
      [text gutter=”false”]Auth-Type TOTP {
    4. Finally, for FreeRADIUS to actually call TOTP, add a line to users.conf (other criteria can be added to filter who/what uses TOTP):
      [text gutter=”false”]DEFAULT Auth-Type := TOTP
      Fall-Through = No[/text]
  4. Edit the rc or init script for FreeRADIUS to load the Perl libraries it will need. To do this, just add LD_PRELOAD=/usr/lib/libperl.so.5.10 to the startup script line that starts FreeRADIUS (change to your Perl library version if different).
  5. Make sure your clients file has a section for your host or subnet and has a secret set.
  6. Launch FreeRADIUS in debug mode (which is VERY useful to get it working). Change to match your Perl library as necessary:
    [text gutter=”false”]LD_PRELOAD=/usr/lib/libperl.so.5.10 freeradius -X[/text]

Watch the console to see if FreeRADIUS fails to start. It should give you a good idea where to go to fix things. Once it starts cleanly, you should be able to test provisioned users with the radtest utility: radtest $username $password Localhost 1234 $Secret where $password will be passed into the perl script for validation, 1234 is a made-up port number and $Secret is the radius secret for Localhost, which by default is “testing123”.

View more stories in 'Operations'

2 Comments RSS

  • Alan DeKok

    This is a good post. Helpful, descriptive, and leverages the world’s best RADIUS server!

    Just one minor typo in 3.4:

    DEFAULT Auth-Type := TOTP; Fall-Through = No

    should be:

    DEFAULT Auth-Type := TOTP
    Fall-Through = No

    But no worries. Anyone following the instructions exactly will get a descriptive error message telling them how to fix it.

    Thanks also for the recommendation to use “freeradius -X”. It’s something we push a lot. It’s one of the most useful debug tools available for any open source project.

    • tmackey

      Thanks! Fixed it in the post (think it was a transcription typo on my part, copying summarized code snips or reformatting or something). The -X has saved me many hours of digging and testing with precise hints (if not out right solutions). On the openldap side there is -d (see the table here for options), which also helps immensely when things don’t quite work as expected (though much more cryptic until you have watched it a while).