FreeRADIUS (2.1.12, Ubuntu 14.04) server with LDAP authentication and LDAP fail-over.

Hello again.

This post will be about the exciting process of setting up FreeRADIUS server with LDAP authentication and LDAP server failover.

Some time ago, I had a project to upgrade company’s WiFi network with RADIUS authentication.

As soon as the time frame for the project has been approved and requirements for completion set, the project began.

Part One: RADIUS

If you ever have the need to set up a RADIUS server, the obvious choice for this is FreeRADIUS.

Why is it the obvious choice?

Here’s quote from freeradius.org:

FreeRADIUS is the most widely deployed RADIUS server in the world. It is the basis for multiple commercial offerings. It supplies the AAA needs of many Fortune-500 companies and Tier 1 ISPs.

And it’s open source, so a win by default :)

So, let’s start!

FreeRADIUS packages install

This part of the guide is based on freeradius 2.1.12+dfsg-1.2ubuntu8.1 packages from Ubuntu 14.04, because that is what was available at the time and, why not? :)

After you have your Ubuntu 14.04 environment up and running, install the following packages:

$ sudo apt install freeradius

$ sudo apt install freeradius-ldap

freeradius is the server itself, and freeradius-ldap, you guessed it correctly —the LDAP module! :)

After the packages have been installed, proceed to server setup.

FreeRADIUS setup and configuration

For all you awesome SysOps guys and girls who will jump straight into plowing through config files and deleting important information like comments, unused functionality and punctuation marks, here’s a quote/warning from wiki.freeradius.org:

“Always start with the “default” configuration. It will work everywhere, and do almost everything you need. The only change you will need to make is to define users and passwords.
Making a lot of changes to the configuration files is the best way to break the server. Don’t do that.”

I myself did not abide by this warning the first time, so I spent a couple of extra hours “debugging” my own awesomeness that I’ve done. :)

Anyway, let’s start configuring LDAP authentication in FreeRADIUS!

You’ll need to edit the following files:

clients.conf (/etc/freeradius/clients.conf) — here you define your “clients” (Access points, routers etc.)

For example:

client %router_name% {
ipaddr = %router_ip%
secret = %router_password%
limit {
max_connections = 50
lifetime = 0
idle_timeout = 30
}
}

radiusd.conf (/etc/freeradius/radiusd.conf) — main FreeRADIUS config file.

This LOOONG text file contains all the possible settings, like working directories, freeradius process user/group, library/module includes etc.

In most cases, default settings in radiusd.conf work well and you should not tinker with them without a good reason.

ldap (/etc/freeradius/modules/ldap) — this file contains settings for ldap module and settings to connect to LDAP directory.

For example:

ldap {
server = %ldap_server_fqdn%
port = %ldap_server_port%
identity = “uid=%bind_user%,cn=users,cn=accounts,dc=example,dc=com”
password = %bind_user_password%
basedn = “cn=users,cn=accounts,dc=example,dc=com”
filter = “(uid=%{%{Stripped-User-Name}:-%{User-Name}})”
authtype = ldap
groupname_attribute = cn
groupmembership_filter = “(|(&(objectClass=GroupOfNames)(member=%{Ldap-UserDn}))(&(objectClass=GroupOfUniqueNames)(uniquemember=%{Ldap-UserDn})))”
dictionary_mapping = ${raddbdir}/ldap.attrmap
set_auth_type = yes
auto_header = yes
password_header = “{ssha}”
password_attribute = userPassword
}

Site configuration file in /etc/freeradius/sites-enabled

In sites-enabled you should have 2 config files:

inner-tunnel
%site_name%

inner-tunnel is a virtual server and handles only inner tunnel requests for EAP-TTLS and PEAP types. We won’t be using these protocols, so leave this file at it’s defaults.

%site-name% — in this file you define your virtual host.

If you ever had to configure nginx web server virtual hosts, you’ll see the similarities and similar configuration principals.

This file is heavily commented and has multiple configuration sections.

For LDAP authentication to work, you should checkout Authorize and Authenticate sections.

Authorize

In this section, make sure the mschap option is not commented out.

mschap protocol will be used in authentication requests from LDAP user accounts.

Authenticate

In this section, make sure the LDAP option is not commented out:

Auth-Type LDAP {
ldap
}

ldap is a refrence to ldap module configuration in /etc/freeradius/modules.

ldap.attrmap (/etc/freeradius/ldap.attrmap) — in this file you map LDAP attributes to RADIUS dictionary attributes.

For example:

checkItem Auth-Type radiusAuthType
checkItem User-Password userPassword
checkItem Simultaneous-Use radiusSimultaneousUse
checkItem Called-Station-Id radiusCalledStationId
checkItem Calling-Station-Id radiusCallingStationId
checkItem LM-Password lmPassword
checkItem LM-Password ipaNThash
checkItem NT-Password ipaNThash
checkItem NT-Password ntPassword
checkItem LM-Password sambaLmPassword
checkItem NT-Password sambaNtPassword
checkItem LM-Password dBCSPwd
checkitem Password-With-Header userPassword
checkItem SMB-Account-CTRL-TEXT acctFlags
checkItem Expiration radiusExpiration
checkItem NAS-IP-Address radiusNASIpAddress

Initial FreeRADIUS server configuration is complete.

Part Two: LDAP setup

LDAP setup guide will be based on 389 Directory Server as a part of FreeIPA server.

Of course, you can use a standalone 389 Directory Server, but using FreeIPA is easier and more efficient.

Bind user setup

For FreeRADIUS to be able to communicate with FreeIPA server and retrieve user account information, you’ll need to set up a bind user.

Log in to your FreeIPA server, create a new user for FreeRADIUS, for example: uid=freeradius.bind,cn=users,cn=accounts,dc=example,dc=com, then create new or use existing role/privilege/permission policies, so this user can read attributes from cn=users,cn=accounts,dc=example,dc=com and cn=groups,cn=accounts,dc=example,dc=com containers, and assign those policies to the newly created bind user.

EAP-MSCHAPv2 setup

FreeIPA stores user account passwords in SSHA form, so they can’t be used with FreeRADIUS LDAP module and PAP protocol which supports only clear-text passwords.

For FreeIPA user accounts to be able to authenticate with FreeRADIUS server, in this guide, we’ll use EAP-MSCHAPv2 protocol, but for this to work, we need to generate some NTLM password hashes for FreeIPA user accounts.

To generate additional NTLM password hashes for FreeIPA user accounts, do the following:

log in to your FreeIPA server
install ipa-server-trust-ad package
run ipa-adtrust-install command

ipa-adtrust-install command will create a new attribute for FreeIPA user accounts — ipaNThash.

This attribute will contain FreeIPA user account’s password in NTLM hash.

To generate NTLM hash, each user needs to reset his/hers password.

After NTLM hashes have been generated, you can try to authenticate in FreeRADIUS using FreeIPA user account’s credentials

For testing purposes, I recommend to run FreeRADIUS server in debug mode, like this:

freeradius -X

After server has been started, run this command to test user authentication:

radtest -t mschap %user_name% %user_password% localhost 1812 %nas_password%

If your user does not have ipaNThash attribute, you’ll see something like this:

[mschap] No Cleartext-Password configured. Cannot create LM-Password.
[mschap] No Cleartext-Password configured. Cannot create NT-Password.
[mschap] Told to do MS-CHAPv1 with NT-Password
[mschap] FAILED: No NT/LM-Password. Cannot perform authentication.
[mschap] MS-CHAP-Response is incorrect.
++[mschap] returns reject
Failed to authenticate the user.

But, if your user does have ipaNThash attribute and it contains NTLM password hash, you should see something like this:

[ldap1] performing user authorization for %user_name%
[ldap1] expand: %{Stripped-User-Name} ->
[ldap1] … expanding second conditional
[ldap1] expand: %{User-Name} -> %user_name%
[ldap1] expand: (uid=%{%{Stripped-User-Name}:-%{User-Name}}) -> (uid=%user_name%)
[ldap1] expand: cn=users,cn=accounts,dc=example,dc=com -> cn=users,cn=accounts,dc=example,dc=com
[ldap1] ldap_get_conn: Checking Id: 0
[ldap1] ldap_get_conn: Got Id: 0
[ldap1] attempting LDAP reconnection
[ldap1] (re)connect to %ldap_server_fqdn%:%ldap_port%, authentication 0
[ldap1] setting TLS mode to 1
[ldap1] setting TLS CACert File to %ca_cert%
[ldap1] bind as uid=%bind_user%,cn=users,cn=accounts,dc=example,dc=com/%bind_password% to %ldap_server%:%ldap_port%
[ldap1] waiting for bind result …
[ldap1] Bind was successful
[ldap1] performing search in cn=users,cn=accounts,dc=example,dc=com, with filter (uid=%bind_user%)
[ldap1] Added SSHA1-Password = %ssha_password% == in check items
[ldap1] No default NMAS login sequence
[ldap1] looking for check items in directory…
[ldap1] userPassword -> Password-With-Header == “%ssha_password%”
[ldap1] ipaNThash -> NT-Password == %nt_hash%
[ldap1] ipaNThash -> LM-Password == %lm_hash%
[ldap1] userPassword -> User-Password == %ssha_password%
[ldap1] looking for reply items in directory…
[ldap1] user %user_name% authorized to use remote access
[ldap1] ldap_release_conn: Release Id: 0
+++[ldap1] returns ok
++- group LDAP returns ok
++[expiration] returns noop
++[logintime] returns noop
[pap] Normalizing SSHA1-Password from base64 encoding
[pap] Normalizing SSHA1-Password from base64 encoding
[pap] WARNING: Auth-Type already set. Not setting to PAP
++[pap] returns noop
Found Auth-Type = MSCHAP
# Executing group from file /etc/freeradius/sites-enabled/%site_name%
+- entering group MS-CHAP {…}
[mschap] Found LM-Password
[mschap] Found NT-Password
[mschap] Told to do MS-CHAPv1 with NT-Password
[mschap] adding MS-CHAPv1 MPPE keys
++[mschap] returns ok
Login OK: [%user_name%] (from client localhost port 1812)
# Executing section post-auth from file /etc/freeradius/sites-enabled/%site_name%
+- entering group post-auth {…}
++[exec] returns noop
Sending Access-Accept of id 200 to 127.0.0.1 port 57889
MS-CHAP-MPPE-Keys = %ms-chap-mppe-key%
MS-MPPE-Encryption-Policy = %ms-mppe-encryption-policy%
MS-MPPE-Encryption-Types = %ms-mppe-encryption-types%
Finished request 0.
Going to the next request

So, if radtest command succeeded, you can proceed to testing FreeRADIUS authentication with real devices.

Part Three: LDAP fail-over setup

After you’ve successfully authenticated with a single LDAP server, it’s time to add another LDAP server, so you’ll have some LDAP server fail-over.

In case the primary LDAP server will go offline, FreeRADIUS can query the second LDAP server for authentication credentials.

To add second LDAP server to your authentication setup, do the following:

Go to /etc/freeradius/modules.

Create a copy of the ldap module file named, for example: ldap2.

Rename the original ldap module file to ldap1, so you’ll have 2 module files in /etc/freeradius/modules:

ldap1 and ldap2.

ldap1 file will be used for the first LDAP server and ldap2 file will be used for the second LDAP server.

Open ldap1 file in your favorite text editor and change the beginning of the LDAP server configuration array from this:

ldap {
server = ‘%freeipa_server_fqdn%’
port = %ldap_port%
}

to this:

ldap ldap1 {
server = ‘%freeipa_server_fqdn%’
port = %ldap_port%
}

Do the same changes in ldap2 file, only instead of ldap1, add ldap2.

After you’ve configured both ldap modules, go to /etc/freeradius/sites-enabled.

Open your site configuration file in text editor.

Go to authorize section, find ldap option and comment it out.

Add this configuration directive beneath commented out ldap option:

#ldap
redundant LDAP {
ldap1
ldap2
}

Instead of single ldap module, we’ve enabled FreeRADIUS’s redundant function, which will enable FreeRADIUS to use both ldap1 and ldap2 modules we’ve defined in /etc/freeradius/modules.

Now, go to authenticate section, find ldap configuration section:

Auth-Type LDAP {
ldap
{

and comment it out.

Beneath commented out ldap configuration, add this:

Auth-Type LDAP {
ldap1 {
fail = 1
ok = return
}
ldap2 {
fail = 1
ok = return
}
}

After you’ve updated site configuration, launch FreeRADIUS server in debug mode, and you should see this in server’s output:

ldap1 enabled:

Module: Instantiating module “ldap1” from file /etc/freeradius/modules/ldap1
ldap ldap1 {
server = “%freeipa_fqdn%”
port = %ldap_port%
password = “%bind_password”
identity = “uid=%bind_user%,cn=users,cn=accounts,dc=example,dc=com”
net_timeout = 1
timeout = 4
timelimit = 3
tls_mode = no
start_tls = no
tls_require_cert = “allow”

ldap2 enabled:

Module: Instantiating module “ldap2” from file /etc/freeradius/modules/ldap2
ldap ldap2 {
server = “%freeipa_fqdn%”
port = %ldap_port%
password = “%bind_password”
identity = “uid=%bind_user%,cn=users,cn=accounts,dc=example,dc=com”
net_timeout = 1
timeout = 4
timelimit = 3
tls_mode = no
start_tls = no
tls_require_cert = “allow”

If everything went well, now, when clients will try to authenticate using your RADIUS server, you should see something like this:

++- entering group LDAP {…}
[ldap1] performing user authorization for user.test
[ldap1] expand: %{Stripped-User-Name} ->
[ldap1] … expanding second conditional
[ldap1] expand: %{User-Name} -> user.test
[ldap1] expand: (uid=%{%{Stripped-User-Name}:-%{User-Name}}) -> (uid=user.test)
[ldap1] expand: cn=users,cn=accounts,dc=example,dc=com -> cn=users,cn=accounts,dc=example,dc=com
[ldap1] ldap_get_conn: Checking Id: 0
[ldap1] ldap_get_conn: Got Id: 0
[ldap1] attempting LDAP reconnection
[ldap1] (re)connect to %freeipa_fqdn%:%ldap_port%, authentication 0
[ldap1] setting TLS mode to 1
[ldap1] setting TLS CACert File to %ca_file%
[ldap1] setting TLS Require Cert to demand
[ldap1] bind as uid=%bind_user%,cn=users,cn=accounts,dc=example,dc=com/%bind_password% to %freeipa_fqdn%:%ldap_port%
[ldap1] waiting for bind result …
[ldap1] Bind was successful
[ldap1] performing search in cn=users,cn=accounts,dc=example,dc=com, with filter (uid=%ldap_user%)
[ldap1] Added SSHA1-Password =%user_password_hash% == in check items
[ldap1] No default NMAS login sequence
[ldap1] looking for check items in directory…
[ldap1] userPassword -> Password-With-Header == %user_password_hash% ==”
[ldap1] userPassword -> User-Password == “%user_password_hash%==”
[ldap1] looking for reply items in directory…
[ldap1] user user.test authorized to use remote access

As you can see, FreeIPA user account has successfully authenticated using our RADIUS server using ldap1 module.

If, for some reason, ldap1 module can’t connect to the first LDAP server, FreeRADIUS will switch to ldap2 module in a second or two.

I hope you’ve enjoyed reading this lengthy article, and I hope, it will help you with FreeRADIUS configuration.

See you next time.