Connect FreeRADIUS to LinOTP via perl plugin

Introduction

To improve security significantly of connecting clients (as ssh, openVNV or others) you can add an OneTimePassword based (additional) security layer provided by 'linotp' to your login procedures. An easy and common way of providing this authentification method is for different services is to use the RADIUS protocol. This article shows how to setup a FreeRADIUS server and how to connect it to an existing 'linotp' instance. It will cover a very basic but functional freeRADIUS configuration so you have an usable authentfication system if it is your first RADIUS server and should give experienced administrators enough information to include 'linotp' in their custom freeRADIUS setup.

There are two ways to establish the communication between 'FreeRADIUS' and 'linotp':

  • using a (native) FreeRADIUS plugin, requiring unfortunately a manual rebuild of the FreeRADIUS package
  • or adding a perlplugin to an already installed FreeRADIUS server

For convenience and because it is sufficient for most use cases this guide covers the latter.

Prerequisites

Package versions

This how-to was tested with the following versions of packages (retrieved from Debian Wheezy and linotp repositories):

  • linotp: 2.7-1
  • linotp-freeradius-perl: 1.3-1
  • freeradius: 2.1.12+dfsg-1.2
  • freeradius-utils: 2.1.12+dfsg-1.2

Don't worry - you can use these packages in other version (older as well as newer, but of course newer is recommended) and have a successfully running setup. Only the version of freeradius should remain < 3, in order not to have to make major adjustments to the configuration. But if you encounter obscure problems, please check the changelogs, whether they contain any related modifications.

Installation via apt

  • Install the FreeRADIUS package
  • apt-get install freeradius
  • If not yet done, add the linotp-Repository to your openVPN server.
  • echo 'deb http://dist.linotp.org/debian/linotp2 wheezy linotp' > /etc/apt/sources.list.d/linotp.list
  • Add the gpg-key of linotp to your apt-keyring:
  • apt-key adv --keyserver eu.pool.sks-keyservers.net --recv-keys 913DFF12F86258E5
    apt-get update
  • Install the perl plugin for the FreeRADIUS server provided by 'linotp'
  • apt-get install linotp-freeradius-perl

Installation via Git Repository

  • Install FreeRADIUS from the repository of your distribution (here an example for debian)
  • For debian: apt-get install freeradius
  • Download and install the perl plugin for the FreeRADIUS server provided by 'linotp'
  • wget https://github.com/LinOTP/linotp-auth-freeradius-perl/archive/master.zip
  • Unzip the master.zip file and copy 'radius_linotp.pm' to /usr/lib/linotp/radius_linotp.pm
  • unzip master.zip
    cp 'radius_linotp.pm' /usr/lib/linotp/radius_linotp.pm
    chmod +x /usr/lib/linotp/radius_linotp.pm
  • Installing dependent packages from the distribution (here an example for debian)
  • apt-get install libconfig-file-perl libencode-locale-perl libfile-listing-perl libfont-afm-perl libhtml-form-perl libhtml-format-perl libhtml-parser-perl libhtml-tagset-perl libwww-perl

Configuration

Configure 'FreeRADIUS'

Now comes the tricky part. The configuration of 'FreeRADIUS' is quite complicate and normally done starting with the standard setup doing little steps one by one until the wished functionality is reached. Documentation is mostly found in the configuration files themselves. Because we will erase some of the files and recreate them it is a good idea to first save the original configuration somewhere (not loosing the documention inside...)

  • Make a backup of the original configuration
  • cp -a /etc/freeradius /etc/freeradius_original
    
  • remove default configuration:
  • rm /etc/freeradius/{clients.conf,users}
  • create '/etc/freeradius/clients.conf' with:
  • #arbitrary name of the authentification asking client (i.e. VPN server)
    client vpn {
            ipaddr  = 192.168.42.207 #IP of the client
            netmask = 32
            secret  = 'SECRET' #shared secret, the client has to provide
    }
    

    The 'clients.conf' contains all servers (so i.e. the VPN Gateway, Webserver, SSHserver etc.) that should be able to check the validity of an OneTimePassword provided by a client, asking for authorisation. You can also specify entire networks (i.e. if you have local users and would like to check OTPs via PAM for console logins)

  • '/etc/freeradius/users'
  • DEFAULT Auth-type := perl
    

    The 'clients.conf' contains all servers (so i.e. the VPN Gateway, Webserver, SSHserver etc.) that should be able to check the validity of an OneTimePassword provided by a client, asking for authorisation. You can also specify entire networks (i.e. if you have local users and would like to check OTPs via PAM for console logins)

  • replace '/etc/freeradius/modules/perl' with
  • perl {
    	module = /usr/lib/linotp/radius_linotp.pm
    	}
    

    Those lines make 'FreeRADIUS' execute the 'linotp' plugin each time the 'perl' authentification procedure is triggered.

  • configure the 'linotp' perl plugin
  • Now we define the parameter for the plugin of how to connect to the server running 'linotp'. This is done in the configuration file for the perl plugin: '/etc/linotp2/rlm_perl.ini'

    #IP of the linotp server
    URL=https://192.168.42.202/validate/simplecheck
    #optional: limits search for user to this realm
    REALM=example.net
    #optional: only use this UserIdResolver
    #RESCONF=flat_file
    #optional: comment out if everything seems to work fine
    Debug=True
    #optional: use this, if you have selfsigned certificates, otherwise comment out
    SSL_CHECK=False
    
  • finally activate 'linotp' within 'FreeRADIUS'
  • Create a new file '/etc/freeradius/sites-available/linotp' with the following content:

    authorize {
    
    #normalizes maleformed client request before handed on to other modules (see '/etc/freeradius/modules/preprocess')
            preprocess
    
            #  If you are using multiple kinds of realms, you probably
            #  want to set "ignore_null = yes" for all of them.
            #  Otherwise, when the first style of realm doesn't match,
            #  the other styles won't be checked.
    
    #allows a list of realm (see '/etc/freeradius/modules/realm')
            IPASS
    
    #understands something like USER@REALM and can tell the components apart (see '/etc/freeradius/modules/realm')
            suffix
    
    #understands USER\REALM and can tell the components apart (see '/etc/freeradius/modules/realm')
            ntdomain
    
            #  Read the 'users' file to learn about special configuration which should be applied for
            # certain users (see '/etc/freeradius/modules/files')
            files
    
            # allows to let authentification to expire (see '/etc/freeradius/modules/expiration')
            expiration
    
            # allows to define valid service-times (see '/etc/freeradius/modules/logintime')
            logintime
    
            # We got no radius_shortname_map!
            pap
    }
    
    #here the linotp perl module is called for further processing
    authenticate {
            perl
    }
    
  • and activate the configuration by softlinking it in to '/etc/freeradius/sites-enabled'
  • ln -s ../sites-available/linotp /etc/freeradius/sites-enabled'
    

    Important: In order to make this really work, you should erase the default-links for activated configurations ('rm /etc/freeradius/sites-enabled/{default,inner-tunnel}') or adapt them to work together with our linotp-file.

Testing the FreeRADIUS configuration

Now, go to the client of the FreeRADIUS server (i.e. your VPN gateway) and install for testing purposes the freeradius-utils:

apt-get install freeradius-utils

The package brings a number of commandline tools, we will use 'radtest' to verify our setup is working correctly.

Generic usage of radtest:

radtest USERNAME PINOTP IP_OF_RADIUSSERVER NAS_PORTNUMBER SECRET

Here an example:

radtest fritz 1234195767 192.168.42.202 0 SECRET

Important: Make sure, the client can reverse resolve its hostname to an IP (i.e. simply put something like '192.168.42.227 vpnserver vpnserver.example.net' in your '/etc/hosts'), otherwise you will get an error-messages like:

radclient:: Failed to find IP address for vpnserver
radclient: Nothing to send.

To find errors please start the freeRADIUS server in debug mode:

service freeradius stop
freeradius -X

Next: try to use a wrong OTP (or username) just to see if the connection to the RADIUS server works at all (and your request for authentification is correctly rejected). You should read something like this:

Output of radtest (at the client)

root@vpnmaster:~# radtest fritz 1234195767 192.168.42.202 0 SECRET
Sending Access-Request of id 198 to 192.168.42.202 port 1812
        User-Name = "fritz"
        User-Password = "1234195767"
        NAS-IP-Address = 192.168.42.207
        NAS-Port = 0
        Message-Authenticator = 0x00000000000000000000000000000000
rad_recv: Access-Reject packet from host 192.168.42.202 port 1812, id=198, length=50
        Reply-Message = "LinOTP server denied access!"

Output of freeRADIUS started in debug mode (freeradius -X)

rad_recv: Access-Request packet from host 192.168.42.207 port 40197, id=198, length=75
        User-Name = "fritz
        User-Password = "1234195767
        NAS-IP-Address = 192.168.42.207
        NAS-Port = 0
        Message-Authenticator = 0xad01ee66726aaa32f682ef3b1a923ebc
# Executing section authorize from file /etc/freeradius/sites-enabled/linotp
+- entering group authorize {...}
++[preprocess] returns ok
[IPASS] No '/' in User-Name = "fritz", looking up realm NULL
[IPASS] No such realm "NULL
++[IPASS] returns noop
[suffix] No '@' in User-Name = "fritz", looking up realm NULL
[suffix] No such realm "NULL
++[suffix] returns noop
[ntdomain] No '\' in User-Name = "fritz", looking up realm NULL
[ntdomain] No such realm "NULL
++[ntdomain] returns noop
[files] users: Matched entry DEFAULT at line 1
++[files] returns ok
Found Auth-Type = perl
# Executing group from file /etc/freeradius/sites-enabled/linotp
+- entering group authenticate
rlm_perl: Config File /etc/linotp2/rlm_perl.ini found
rlm_perl: Default URL https://192.168.42.202/validate/simplecheck
rlm_perl: RAD_REQUEST: User-Name = fritz
rlm_perl: RAD_REQUEST: User-Password = 1234195767
rlm_perl: RAD_REQUEST: NAS-Port = 0
rlm_perl: RAD_REQUEST: NAS-IP-Address = 192.168.42.207
rlm_perl: RAD_REQUEST: Message-Authenticator = 0xad01ee66726aaa32f682ef3b1a923ebc
rlm_perl: Auth-Type: perl
rlm_perl: Url: https://192.168.42.202/validate/simplecheck
rlm_perl: User: fritz
rlm_perl: urlparam client = 192.168.42.207
rlm_perl: urlparam pass = 1234195767
rlm_perl: urlparam realm = example.net
rlm_perl: urlparam user = fritz
rlm_perl: Content
rlm_perl: return RLM_MODULE_REJECT
rlm_perl: Added pair User-Name = fritz
rlm_perl: Added pair User-Password = 1234195767
rlm_perl: Added pair NAS-Port = 0
rlm_perl: Added pair NAS-IP-Address = 192.168.42.207
rlm_perl: Added pair Message-Authenticator = 0xad01ee66726aaa32f682ef3b1a923ebc
rlm_perl: Added pair Reply-Message = LinOTP server denied access
rlm_perl: Added pair Auth-Type = perl
++[perl] returns reject
Failed to authenticate the user
Delaying reject of request 1 for 1 seconds
Going to the next request
Waking up in 0.7 seconds
Sending delayed reject for request 1
Sending Access-Reject of id 198 to 192.168.42.207 port 40197
Reply-Message = "LinOTP server denied access

You see? The RADIUS server recieves the request, authorizes the client to connect, forwards the authentification request to the linotp server via the perl plugin and denies the access, because linotp states that there is no such user with this OTP.

The next step is to provide a correct OTP. You should see something like this:

RADIUS client

root@vpnmaster:~# radtest fritz 1234693212 192.168.42.202 0 SECRET
Sending Access-Request of id 72 to 192.168.42.202 port 1812
        User-Name = "fritz
        User-Password = "1234693212
        NAS-IP-Address = 192.168.42.207
        NAS-Port = 0
        Message-Authenticator = 0x00000000000000000000000000000000
        rad_recv: Access-Accept packet from host 192.168.42.202 port 1812, id=72, length=43
        Reply-Message = "LinOTP access granted

RADIUS server (debug mode: 'freeradius -X')

ad_recv: Access-Request packet from host 192.168.42.207 port 50010, id=72, length=75
        User-Name = "fritz"
        User-Password = "1234693212"
        NAS-IP-Address = 192.168.42.207
        NAS-Port = 0
        Message-Authenticator = 0xac5d63e229ad47bfb4ab3b8069abcbb1
# Executing section authorize from file /etc/freeradius/sites-enabled/linotp
+- entering group authorize
++[preprocess] returns ok
[IPASS] No '/' in User-Name = "fritz", looking up realm NULL
[IPASS] No such realm "NULL
++[IPASS] returns noop
[suffix] No '@' in User-Name = "fritz", looking up realm NULL
[suffix] No such realm "NULL
++[suffix] returns noop
[ntdomain] No '\' in User-Name = "fritz", looking up realm NULL
[ntdomain] No such realm "NULL
++[ntdomain] returns noop
[files] users: Matched entry DEFAULT at line 1
++[files] returns ok
Found Auth-Type = perl
# Executing group from file /etc/freeradius/sites-enabled/linotp
+- entering group authenticate
rlm_perl: Config File /etc/linotp2/rlm_perl.ini found
rlm_perl: Default URL https://192.168.42.202/validate/simplecheck
rlm_perl: RAD_REQUEST: User-Name = fritz
rlm_perl: RAD_REQUEST: User-Password = 1234693212
rlm_perl: RAD_REQUEST: NAS-Port = 0
rlm_perl: RAD_REQUEST: NAS-IP-Address = 192.168.42.207
rlm_perl: RAD_REQUEST: Message-Authenticator = 0xac5d63e229ad47bfb4ab3b8069abcbb1
rlm_perl: Auth-Type: perl
rlm_perl: Url: https://192.168.42.202/validate/simplecheck
rlm_perl: User: fritz
rlm_perl: urlparam client = 192.168.42.207
rlm_perl: urlparam pass = 1234693212
rlm_perl: urlparam realm = example.net
rlm_perl: urlparam user = fritz
rlm_perl: Content
rlm_perl: LinOTP access granted
rlm_perl: return RLM_MODULE_OK
rlm_perl: Added pair User-Name = fritz
rlm_perl: Added pair User-Password = 1234693212
rlm_perl: Added pair NAS-Port = 0
rlm_perl: Added pair NAS-IP-Address = 192.168.42.207
rlm_perl: Added pair Message-Authenticator = 0xac5d63e229ad47bfb4ab3b8069abcbb1
rlm_perl: Added pair Reply-Message = LinOTP access granted
rlm_perl: Added pair Auth-Type = perl
++[perl] returns ok
  WARNING: Empty post-auth section.  Using default return values
Sending Access-Accept of id 72 to 192.168.42.207 port 50010
        Reply-Message = "LinOTP access granted
Finished request 0

Congratulation! Now you can start integrating your new RADIUS-linotp authentification to your services. To see (and try) an example please consult our openVPN how-to. http://www.linotp.org/howtos/howto-openvpn.html

Tip: Do not forget to deactivate debugmode in '/etc/linotp2/rlm_perl.ini' ... :-)

Troubleshooting and Bibliography