The FreeRadius server project (http://www.freeradius.org/) is the implementation of the Remote Authentication Dial-In User Service (RADIUS) protocol used by many corporations and Internet service providers to authenticate users connecting from remote locations. Complete coverage of FreeRadius or RADIUS servers goes beyond the scope this chapter. RFC 2865 explains the details of the protocol. For a more practical look at RADIUS, you should refer to the FreeRadius web site as well as RADIUS, by Jonathon Hassel (O’Reilly).
The FreeRadius server daemon, radiusd , can use an LDAP directory in two different ways. First, it can use LDAP as a data store for RADIUS attribute values. RADIUS attributes are defined by the RADIUS protocol and should not be confused with LDAP attributes.[4] The only similarity between the two types of attributes is that both have names and are used to store values. The FreeRadius administrator defines the mapping between RADIUS attributes and the LDAP attributes used to represent them. We’ll look at the configuration details after we have compiled a working RADIUS server. The second option is to use the directory as an authentication service by binding to the LDAP server on behalf of a user. In this way, radiusd can determine whether to accept or reject incoming connection requests.
In the 0.8 release, the rlm_ldap module used by
radiusd to access a directory is included in a
default installation. No additional flags are required to enable LDAP
support at compile time. Running the basic configure
&& make && /bin/su -c "make install
" is
enough to achieve a working radiusd in most
environments.
Without getting too bogged down in the specifics of the FreeRadius configuration file, radiusd.conf , it is worth explaining the general layout. Configuration options can be described as either existing within the scope of a section bounded by { }s or global. Global parameters define information such as the location of directories necessary to the general operation of radiusd or the number of threads that the main server should spawn. Scoped parameters can be subdivided into module settings and component implementations.
FreeRadius modules are shared libraries defined by the
project’s RLM interface. The
modules
block in radiusd.conf
contains parameters specific to each library. The RLM interface
describes several different components that a module can implement.
The two components of interest to us are
authorize
and
authenticate
.
The authorization component is used by radiusd
to look up information about a user account. The
authorize
section can contain several different
module names. Each module is queried in order for an entry matching
the login name of the user in question until a record is located or
all modules have reported failure. Part of the authorization
component’s responsibility is to describe the
authentication method used to validate this account. The
authenticate
section defines possible
authentication mechanisms. The method actually used for a specific
request is determined by the information returned by the
authorize
section.
Here is the working configuration file for a basic server to authenticate connections against the list of local accounts:
## radiusd.conf: FreeRADIUS server configuration file ## ## Global parameters: directory/logfile locations, etc. ## prefix = /opt/radius exec_prefix = ${prefix} sysconfdir = ${prefix}/etc localstatedir = ${prefix}/var sbindir = ${exec_prefix}/sbin logdir = ${localstatedir}/log/radius raddbdir = ${sysconfdir}/raddb radacctdir = ${logdir}/radacct confdir = ${raddbdir} run_dir = ${localstatedir}/run/radiusd log_file = ${logdir}/radius.log libdir = ${exec_prefix}/lib pidfile = ${run_dir}/radiusd.pid # CLIENTS CONFIGURATION $INCLUDE ${confdir}/clients.conf ## ## MODULE CONFIGURATION ## modules { ## Unix /etc/passwd-style authentication unix { passwd = /etc/passwd shadow = /etc/shadow group = /etc/group radwtmp = ${logdir}/radwtmp } ## Local files. The user's file contains a single entry to default all ## authentication to the local system. ## DEFAULT Auth-Type := System files { usersfile = ${confdir}/users acctusersfile = ${confdir}/acct_users compat = no } } ## ## Authorization: obtain information about the user ## authorize { files } ## ## Authentication: validate the user request ## authenticate { unix }
To test your server, you must make sure that the following entry is defined in radiusd’s clients.conf file to allow connections over the loopback interface:
## Allow connection requests from localhost. client 127.0.0.1 { secret = testing123 shortname = localhost nastype = other }
You can test your configuration by starting radiusd in debug mode. This will produce a large amount of log information printed to standard output.
root# radiusd -X -A < . . . preceding output omitted . . . > Ready to process requests.
Using radtest(1) , you can verify that the local user guest1 with password test1 can be successfully authenticated:
$ radtest guest1 test1 localhost 0 testing123 Sending Access-Request of id 50 to 127.0.0.1:1812 User-Name = "guest1" User-Password = "263 33 372760@3 22X327334343 25265347}" NAS-IP-Address = garion NAS-Port = 0 rad_recv: Access-Accept packet from host 127.0.0.1:1812, id=50, length=20
Now that you have a working RADIUS configuration, it is time to move on and integrate the new server with your directory.
If you want the RADIUS server to utilize
the directory for authentication only, no schema modifications to
your existing LDAP server are necessary. You can
simply use the posixAccount
entry for a user, as
you did with the ProFTPD server.
The first step is to define the parameters for the rlm_ldap module
instance. All of the parameters shown here should be intuitive. The
module will perform an anonymous bind to our
LDAP server and search for a
posixAccount
entry whose uid
attribute matches the username of the connecting user. Once this
entry is found, the library will attempt to bind to the directory as
the user to verify the user’s credentials. All of
the communication takes place after the StartTLS command has been
executed to ensure privacy.
ldap { server = "ldap.plainjoe.org" port = "389" basedn = "ou=people,dc=plainjoe,dc=org" filter = "(&(objectclass=posixAccount)(uid=%{Stripped-User-Name:-%{User-Name}}))" start_tls = yes }
There are many more parameters that can be defined for the rlm_ldap module. A complete list is given, along with descriptions, in Table 8-4.
Parameter |
Default |
Description |
None |
The attribute located below the | |
|
Controls how the | |
None |
Searches base DN. | |
|
Specifies whether the module should compare the check items in the RADIUS request with the check items in the directory. | |
None |
DN of the entry containing the default RADIUS profile. | |
|
Location of the file containing the RADIUS/ LDAP attribute mappings. | |
|
An RFC 2254 search filter. | |
|
Attribute used when searching for a RADIUS groupname. | |
None |
The attribute containing the DN of the group of which the user is a member. | |
(|(&(objectClass= GroupOfNames)(member=% {LdapUserDn}))(& (objectClass= GroupOfUniqueNames) (uniquemember=% {Ldap-UserDn})))
|
The RFC 2254 search filter used to query a group for membership. | |
|
None |
DN and password to use when performing a nonanonymous bind to the directory server for searches. |
|
Number of seconds until the LDAP client library cache expires. A
setting of | |
|
The cache size to pass to the LDAP client libraries. A size of
| |
|
The total number of LDAP connections to maintain for the RADIUS server. | |
|
OpenLDAP debug flags (see
slapd.conf’s
| |
|
The number of seconds to wait for a response from the LDAP server in the event of a network failure. | |
None |
Header (such as | |
None |
The name of the attribute containing the password for a user. | |
|
TCP port to use when contacting the directory server. | |
None |
The attribute containing the DN of the user’s
| |
|
Hostname of the LDAP server. | |
|
If enabled, the module will send a StartTLS command prior to any other LDAPoperations. | |
|
Number of seconds the LDAP server has to perform the search (server-side time limit). | |
|
Number of seconds to wait for a response to the LDAP query. | |
|
If enabled, the module will contact the directory using LDAPS. |
The next step required to put this new module instance into play is
adding a definition to the authenticate
section
that can be used as a value for the Auth-Type
attribute in the users file:
authenticate { authtype LDAP { ldap } }
Now you can change the authentication default in raddb/users to LDAP:
## raddb/users file defined by the files authorize component ## ## Authenticate all users by binding to the LDAP directory. DEFAULT Auth-Type := LDAP
After restarting radiusd (again in debug mode),
test the new configuration using a preexisting LDAP user entry
(uid=kristi
and
userPassword=testpass
):
$ ./radtest kristi testpass localhost 0 testing123 Sending Access-Request of id 147 to 127.0.0.1:1812 User-Name = "kristi" User-Password = "1q325 26 20315p214X310227376 14]F332" NAS-IP-Address = garion NAS-Port = 0 rad_recv: Access-Accept packet from host 127.0.0.1:1812, id=147, length=20
From the client’s point of view, nothing appears to be different. The server, however, yields much more information. You should be able to locate a line where the module binds to the directory on behalf of the user.
auth: type "LDAP"
modcall: entering group authtype
< . . . remainingg output omitted . . . >
rlm_ldap: bind as uid=kristi,ou=people,dc=plainjoe,dc=org/test to
ldap.plainjoe.org:389
rlm_ldap: waiting for bind result ...
rlm_ldap: user kristi authenticated successfully
modcall[authenticate]: module "ldap" returns ok
This is definitely the least intrusive way to integrate FreeRadius with an existing directory and will work with any LDAPv3 server. The next step is to use your directory as a data store for the information currently stored in FreeRadius’s users file. This, however, will require that you learn about some new schema items.
The FreeRadius project provides an LDAP schema file for use with
OpenLDAP 2.x servers. The
RADIUS-LDAPv3.schema
file can be found in the
doc/ directory of the FreeRadius source code
distribution. The schema defines many new attributes used to store
RADIUS attribute values and a single structural object class named
radiusprofile
, shown in Figure 8-5, which is used to represent RADIUS users. To
keep our focus on LDAP and not RADIUS, we will only concern ourselves
with how radiusAuthType
maps the
Auth-Type
RADIUS attribute. Descriptions of the
other attributes can be found in the RADIUS RFC.
First, copy the new schema file to /usr/local/etc/openldap/schema/:
root# cp RADIUS-LDAPv3.schema /usr/local/etc/openldap/schema/
Next, you must include a reference to this file in
slapd.conf. Because a
radiusprofile
object includes the
lmPassword
and ntPassword
attributes from the samba.schema file, it must
be placed after the latter file has been parsed:
## /usr/local/etc/openldap/slapd.conf ## core.schema is required for all servers. include /usr/local/etc/openldap/schema/core.schema ## Included from Chapter 6 include /usr/local/etc/openldap/schema/cosine.schema include /usr/local/etc/openldap/schema/nis.schema ## Included from Chapter 7 include /usr/local/etc/openldap/schema/inetorgperson.schema ## Dependencies for samba.schema: cosine.schema and inetorgperson.schema needed to ## support --with-ldapsam in Samba include /usr/local/etc/openldap/schema/samba.schema ## Support for FreeRadius depends on samba.schema for LM/NT password attributes. include /usr/local/etc/openldap/schema/RADIUS-LDAPv3.schema
The mapping between
LDAP attributes and RADIUS
attributes is stored in a text file named
ldap.attrmap by default. Conventionally, this
file is stored in the raddb/ directory, but both
the name and location are configurable via the rlm_ldap
module’s dictionary_mapping
parameter. Because we will be using the default schema file, the
corresponding attribute dictionary is sufficient. If you decide to
use a custom schema, you may have to modify the dictionary as well.
Your next hurdle is decide where to store the
radiusprofile
entries in the
directory. Your first choice might be to store them with the other
user account information below the people
ou
. The problem is that you cannot add the
radiusprofile
object to your existing
users’ accounts. You can either change the
radiusprofile
definition to an auxiliary object or
choose to store objects of this type somewhere else. Following the
trend of using provided schemas whenever possible, we will create a
new organizational unit to hold FreeRadius users and establish a link
between the posixAccount
and the
radiusprofile
objects.
First, create a new ou=radius
entry below the
services
ou
(created in Chapter 7 for Sendmail):
dn: ou=radius,ou=services,dc=plainjoe,dc=org objectclass: organizationalUnit ou: radius
Next, create a profile for the user kristi. The
difficulty with this is choosing the RDN for the entry. You have
already chosen the uid
attribute as the unique
naming convention for entries below ou=people
.
However, the radiusprofile
object includes only
the cn
attribute. The informational RFC 2377
defines an auxiliary uidObject
class for
situations such as this one. This allows you to include the
uid
attribute as the RDN for new entries
regardless of the structural object class and still maintain your
internal naming conventions. You could just use an
extensibleObject
, but the
uidObject
is a cleaner approach. Here is the
resulting RADIUS user entry:
dn: uid=kristi,ou=radius,ou=services,dc=plainjoe,dc=org objectclass: radiusprofile objectclass: uidObject uid: kristi cn: Kristi Carter radiusAuthType: LDAP
Finally, link the posixAccount
entry for
kristi to her RADIUS information by storing the
DN of the radiusprofile
object in
uid=kristi,ou=people,dc=plainjoe,dc=org
. This time
we will use the extensibleObject
to add the extra
attribute. To make the new attributes easier to see, most of the
existing optional attributes have been omitted from this LDIF
excerpt. Added attributes are shown in bold.
## Existing optional attributes have been omitted from the display. dn: uid=kristi,ou=people,dc=plainjoe,dc=org objectClass: inetOrgPerson objectClass: posixAccount objectClass: sambaAccountobjectclass: extensibleObject
cn: Kristi Carter cn: Kristi W. Carter sn: Carter loginShell: /bin/bash uidNumber: 781 gidNumber: 100 homeDirectory: /home/kristi uid: kristi rid: 2570radiusprofileDN: uid=kristi,ou=radius,ou=services,dc=plainjoe,dc=org
The two module configuration changes to be made are changing the
locations of the dictionary file for LDAP/RADIUS attributes
(dictionary_mapping
) and specifying the LDAP
attribute containing the DN of the user’s RADIUS
information (profile_attribute
):
ldap { server = "ldap.plainjoe.org" port = "389" basedn = "ou=people,dc=plainjoe,dc=org" filter = "(&(objectclass=posixAccount)(uid=%{Stripped-User-Name:-%{User-Name}}))" start_tls = yes profile_attribute = "radiusProfileDn" dictionary_mapping = ${raddbdir}/ldap.attrmap }
You can now remove the local copy of the users file from the RADIUS server and add the ldap module to the authorize block in radiusd.conf. The files module is still listed because it also contains directives affecting local accounting policies.
## ## Authorization: obtain information about the user ## authorize { files ldap }
This example brings up two important points:
Relationships between entries in a directory can be represented by storing the DNs as reference links.
Using auxiliary objects such as the uidObject
to
maintain a standard naming convention can help reduce the management
costs associated with locating related entries.
I’ll leave it up to you to use the radtest tool to verify that the server is working correctly.
[4] A list of RADIUS attributes linked with the corresponding RFCs can be found at http://www.freeradius.org/rfc/attributes.html.