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
echo 'deb http://dist.linotp.org/debian/linotp2 wheezy linotp' > /etc/apt/sources.list.d/linotp.list
apt-key adv --keyserver eu.pool.sks-keyservers.net --recv-keys 913DFF12F86258E5 apt-get update
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
wget https://github.com/LinOTP/linotp-auth-freeradius-perl/archive/master.zip
unzip master.zip cp 'radius_linotp.pm' /usr/lib/linotp/radius_linotp.pm chmod +x /usr/lib/linotp/radius_linotp.pm
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
rm /etc/freeradius/{clients.conf,users}
#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)
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)
perl { module = /usr/lib/linotp/radius_linotp.pm }
Those lines make 'FreeRADIUS' execute the 'linotp' plugin each time the 'perl' authentification procedure is triggered.
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
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 }
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
- Very good guide for troubleshooting the RADIUS server: http://wiki.freeradius.org/guide/Troubleshooting
- Here you can paste the RADIUS server messages and the tool will help you to identify problems: http://networkradius.com/freeradius.html
- The commented configuration files: http://wiki.freeradius.org/config/Configuration-files