RADIUS has been around a while (RFC’s dating back to the 90’s) and it’s uses are very broad.
<Disclaimer> This is not a verbose RADIUS tutorial, rather bare-minimum to get WPA2/802.1X/PEAP working. FR is infinitely flexible, with many ways to reach the same goal. Ideally, you should read the comments in the radius configs & look over http://wiki.freeradius.org/Home, http://deployingradius.com/ etc.
If you'd rather set up RADIUS 2.x, which is likely available in your distro's repository, most of the information here is still valid. You'll just need to modify a different config file. Personally, I prefer the way v3 is laid out, and the debugs are easier to read (color coded). Technically "production" networks should still be running FreeRADIUS v2.x at the time of this writing.</Disclaimer>
I’ll break it down into steps:
1. Set up basic FreeRADIUS3 on Debian server (Debian ver. 7) using local authentication, and add your first NAS and test.
2. Configure RADIUS server and “test” SSID (utilizing WPA2-Enterprise) to WLC and test.
3. Replace the default certificate with your own [and test].
4. Add LDAP directory as authentication db [and...]
5. Use different LDAP directories based on interface, SSID etc. using Tunnel-Private-Group-Id
Step 1. - Building FreeRADIUS package for installation on Debian
You can build from source, use your package manager, find a PPA etc. Here is how I built it using dpkg.
First, some dependencies:
apt-get install dpkg-dev quilt libreadline-dev libtalloc-dev libyubikey-dev libsqlite3-dev ssl-cert
apt-get build-dep freeradius
Grab the latest FreeRADIUS build
wget ftp://ftp.freeradius.org/pub/freeradius/freeradius-server-3.0.0.tar.gz
Extract & build:
tar xvf freeradius-server-3.0.0.tar.gz
cd into the freeradius directory, then:
dpkg-buildpackage -b -uc
Depending on which modules you want to use, install the packages in the parent directory with:
dpkg -i *freeradius*_3.0.0+*_*.deb
You now have an installed freeradius3 server.
config files are located in /etc/freeradius
Usage: /etc/init.d/freeradius start|stop|restart|force-reload|reload|configtest|debug
Similiar to Nginx and Apache, you have mods-available/mods-enabled, sites-available/sites-enabled etc. To activate a module or “site” you can copy from ‘available’ to ‘enabled’ or use a symlink.
You should test basic functionality by enabling a local user account and running freeradius in debug mode:
vi /etc/freeradius/users
uncomment the follwing line line:
steve Cleartext-Password := "testing"
freeradius -X
From the freeradius server run:
radtest steve testing 127.0.0.1:1812 0 testing123
To test 'inner-tunnel' config:
radtest steve testing 127.0.0.1:18120 0 testing123
To test using mschap:
radtest -t mschap steve testing 127.0.0.1:18120 0 testing123
All of those should yeild an "Access-Accept" packet, like:
rad_recv: Access-Accept packet from host 127.0.0.1 port 18120, id=57, length=84
Add the WLC as a NAS client:
vi /etc/freeradius/clients.conf
Towards the bottom, in the clients section...
client <ip_of_your_WLC> {
secret = testing123
shortname = WLC-11
}
secret = testing123
shortname = WLC-11
}
Production environment should change the secret accordingly.
It's a good habit to re-run "freeradius -X" after each modification to config files. It will allow you to easily see when you've messed something up.
It's a good habit to re-run "freeradius -X" after each modification to config files. It will allow you to easily see when you've messed something up.
Step 2. - Configure RADIUS server and “test” SSID
In this case I'm using an ap-group so I only advertise the SSID on the testing AP. Configured on AirOS WLC:
Add the RADIUS server:
config radius auth add 6 <server_ip> 1812 ascii testing123
Since this will be for network users only (not mgmnt users):
config radius auth management 6 disable
Configure the test SSID:
config wlan create 5 testing testing
config wlan security wpa wpa2 enable 5
config wlan radius_server auth add 5 6
config wlan enable 5
Add the apgroup:
config wlan apgroup add testing
config wlan apgroup interface-mapping add testing 5 <dynamic-interface-name on wlc>
config ap group-name testing <ap_name>
Now, with freeradius running in debug mode (freeradius -X), you should be able to connect to the “testing” SSID (accepting the test default certificate), using "steve/testing" credentials. You can follow the PEAP process by looking at the debug, from establishing TLS (outer tunnel) through the eap_mschapv2 challenge eventually getting "Sending Access-Accept".
I’m assuming your connecting with an OS that can “auto-configure” it’s dot1X supplicant for PEAP. (autoconfiguration = bad idea for security, but that’s another story).
Step 3. - Replace the default certificate with your own
Firstly, copy the certificate files (key file & certificate file) to the /etc/freeradius/certs/ directory.
Depending on the type of certificate you obtain, you will need to modify a couple parameters here. The comments explain it thoroughly enough. For my install I modify the following parameters, to use my obtained certificate:
vi mods-enabled/eap
private_key_password = <cert-key-password>
private_key_file = ${certdir}/<certificate.key_file>
certificate_file = ${certdir}/<certificate.pem_file>
Although not strictly necessary, as the server will figure out the eap-type on it's own, I set the default eap type to peap with:
default_eap_type = peap
Comment out EAP types not neccessary, resulting in an uncommented eap file resembling the following:
default_eap_type = peap
timer_expire = 60
ignore_unknown_eap_types = no
cisco_accounting_username_bug = no
max_sessions = ${max_requests}
# Supported EAP-types
tls-config tls-common {
private_key_password = MrGtdTw*
private_key_file = ${certdir}/<private_key>.key
certificate_file = ${certdir}/<cert>.pem
dh_file = ${certdir}/dh
ca_path = ${cadir}
cipher_list = "DEFAULT"
ecdh_curve = "prime256v1"
}
peap {
tls = tls-common
default_eap_type = mschapv2
copy_request_to_tunnel = yes
use_tunneled_reply = no
virtual_server = "inner-tunnel"
}
mschapv2 {
# send_error = no
}
}
It’s a good idea to test again, before moving on. (freeradius -X, connect to SSID etc. etc.). If you screwed something up, the debug output will tell you.
Step 4. - Add LDAP directory as authentication db
In this case I'm using OpenLDAP
similar to apache, you enable modules (like ldap) by making a symlink in mods-enabled, to mods-available. To enable the LDAP module, do the following:
cd /etc/freeradius/mods-enabled/
ln -s ../mods-available/ldap ldap
Alternatively, if you'd like to not modify the default config file, you can copy it over to mods-enabled instead:
cp /etc/freeradius/mods-available/ldap /etc/freeradius/mods-enabled/
vi /etc/freeradius/mods-enabled/ldap
- change the "server= "ldap.example.org"" to the hostname of your ldap server. Separate by commas for adding multiple servers (redundancy).
- Add services account, used for binding to the LDAP server to perform lookups:
password = "<account_password>"
- set the base_dn:
base_dn = "ou=people,dc=organization,dc=edu"
- depending on the format of your directories password, you may need to change the LDAP to RADIUS field mapping, in the “update {“ stanza, like:
control:NT-Password := '<ldap_ntpassword>’'
Note, in previous version of FreeRADIUS, this was referred to as "checkItem" in the 'ldap.attrmap' config file.
- change pool options (like "max" connections). You'll need to play with this a bit to find the sweet spot, using a tool like radperf will help.
max=65
lifetime = 20
idle_timeout = 20
Note, if your usernames contain spaces, you'll need to modify the 'filter_username' stanza in the following file:
vi /etc/freeradius/policy.d/filter
comment out the "if (User-Name =~ / /)" section.
Now run your tests again. freeradius -X, etc.
Note, production configurations should use LDAPS (port 636).
Step 5. - Use different LDAP directories based on interface, SSID etc. using RADIUS Tunnel-Private-Group-Id
There are multiple ways of accomplishing setting up a ‘different’ authentication directory, based on the SSID a user connects to. This method will call on the VLAN (dynamic-interface) that the WLAN is tied to on the WLC.
This is useful in a situation where you have users in one location stored in one departments LDAP directory, but users in a different location are using a different LDAP directory. You want the same SSID in both locations.
Copy the mods-enabled/ldap file to a new file, we’ll call it ldap-2:
cp mods-enabled/ldap mods-enabled/ldap-2
cp mods-enabled/ldap mods-enabled/ldap-2
Modify the ‘ldap-2’ file to point to a different directory server, in the same way you did in step 4. You will also need to change the heading of the 'ldap' stanza to read "ldap ldap-2". For example:
ldap ldap-2 {
#
# Note that this needs to match the name....
...if you don't do this, freeradius -X will fail with "Failed to find "ldap2" in the "modules" section."
A side note about VSA's. If you want FreeRADIUS to utilize Vendor Specific Attributes (VSA) in Access-Accept packets, add the following ( from http://cisco.com/en/US/products/ps6307/products_tech_note09186a0080870334.shtml )
Create the following file in the /etc/freeradius directory and copy these contents into it:
vi dictionary.airespace
# -*- text -*-
#
# As found on the net.
#
# $Id: dictionary.airespace,v 1.3.2.1 2005/11/30 22:17:19 aland Exp $
#
VENDOR Airespace 14179
BEGIN-VENDOR Airespace
ATTRIBUTE Airespace-Wlan-Id 1 integer
ATTRIBUTE Airespace-QOS-Level 2 integer
ATTRIBUTE Airespace-DSCP 3 integer
ATTRIBUTE Airespace-8021p-Tag 4 integer
ATTRIBUTE Airespace-Interface-Name 5 integer
ATTRIBUTE Airespace-ACL-Name 6 string
VALUE Airespace-QOS-Level Bronze 3
VALUE Airespace-QOS-Level Silver 0
VALUE Airespace-QOS-Level Gold 1
VALUE Airespace-QOS-Level Platinum 2
END-VENDOR Airespace
vi /etc/freeradius/dictionary
$INCLUDE dictionary.airespace
Back to our sites-enabled/default config, to put in the logic of checking the “interface” and taking appropriate action, which in this case is “use a different ldap server, if the Access-Request packet includes a certain interface”.
vi sites-enabled/default
In the “authorize” stanza, we want to set up an if/elsif statement. Each one will load the same ‘modules’, except for the ldap. You should read all the comments and figure out which ones you want to load, but a mostly default config, except for this change should look like this:
authorize {
#
#check for interface the request is coming in on. If 3859 use ‘ldap-2’
if ( Tunnel-Private-Group-Id == "3859" ) {
filter_username
preprocess
suffix
eap {
ok = return
}
files
ldap-2
}
#if not comfing from interface 3859 use ‘ldap’ server
elsif ( Tunnel-Private-Group-Id != "3859" ) {
filter_username
preprocess
suffix
eap {
ok = return
}
files
ldap
}
}
The “authorize” stanza on the sites-enabled/inner-tunnel should resemble the following
You should now have a working RADIUS server & fully functioning WPA2-Enterprise SSID.
The “authorize” stanza on the sites-enabled/inner-tunnel should resemble the following
authorize {
#
#check for interface the request is coming in on. If 3859 use ‘ldap-2’
if ( Tunnel-Private-Group-Id == "3859" ) {
filter_username
preprocess
suffix
eap {
ok = return
}
files
ldap-2
pap
pap
}
#if not comfing from interface 3859 use ‘ldap’ server
elsif ( Tunnel-Private-Group-Id != "3859" ) {
filter_username
preprocess
suffix
eap {
ok = return
}
files
ldap
pap
pap
}
}
Notice the match statements. Again, the logic here is “if interface is 3859 use the ‘ldap-2’ LDAP Directory, but if interface is not equal to 3859 use the ‘ldap’ LDAP Directory”. Examples include SSID (which comes across as “Called-Station-SSID”).
If you run in debug mode (freeradius -X) you will see the following when sending a test Radius packet (using radclient utility):
From client:
echo "User-Name=<username>,User-Password=<password>,Tunnel-Private-Group-Id = 3859" | radclient 10.2.7.79:1812 auth testing123
Server:
(1) # Executing section authorize from file /usr/local/etc/raddb/sites-enabled/default
(1) authorize {
(1) ? if ( Tunnel-Private-Group-Id == "3859" )
(1) expand: "3859" -> '3859'
(1) ? if ( Tunnel-Private-Group-Id == "3859" ) -> TRUE
...and if coming from a different interface (3860) you’d see something like this:
(57) # Executing section authorize from file /usr/local/etc/raddb/sites-enabled/default
(57) authorize {
(57) ? if ( Tunnel-Private-Group-Id == "3859" )
(57) expand: "3859" -> '3859'
(57) ? if ( Tunnel-Private-Group-Id == "3859" ) -> FALSE
(57) ? elsif ( Tunnel-Private-Group-Id != "3859" )
(57) expand: "3859" -> '3859'
(57) ? elsif ( Tunnel-Private-Group-Id != "3859" ) -> TRUE
(57) elsif ( Tunnel-Private-Group-Id != "3859" ) {
Now you need to tell FreeRADIUS to copy the VSA part of the Access-Request from the ‘outer-tunnel’ to the ‘inner-tunnel’; which in our case means, include the “Tunnel-Private-Group-Id” part of the radius packet when proxying the request over to the inner-tunnel for doing PEAP. If we don’t do this, the inner-tunnel virtual server will not be able to act on the ‘vlan/dynamic interface’ we are connected to (based on our if/elsif statements) since it won’t ever see that value in the Request.
The configuration file we need to modify is eap (Freeradius 2.x called this eap.conf)
vi mods-enabled/eap
Under the “peap” stanza, look for “copy_request_to_tunnel = no” an change that to:
copy_request_to_tunnel = yes
You should now have a working RADIUS server & fully functioning WPA2-Enterprise SSID.
Other configuration modifications:
I find it useful to change the logging to standard syslog
vi radiusd.conf
in the 'log' stanza:
destination = syslog
I also like to log succesfull logins with:
auth = yes
Depending on the number of WLC's you have utilizing this server, you should change the "max_requests" as well:
max_requests = 2560 (10 * # of clients)
Useful tools for both performace testing and troubleshooting RADIUS:
radtest & radclient -- for issuing RADIUS requests from a pc, rather than going through the 802.1X process on a client for each test
radperf -- performance testing the RADIUS server and/or identity store