eduroam SP setup
eduroam, short for “education roaming”, is a global federation of academic institutions providing secure and seamless wireless internet access for students, faculty, and staff.
The goal of this tutorial is to setup a working eduroam Service Provider (SP) instance and optionally an Identity Provider (IdP) instance as well.
An eduroam Service Provider or SP is an instance that offers eduroam as a service, i.e. publishing the eduroam SSID on premises and allowing people to connect. This can be anyone from libraries to coffee shops or train stations.
An eduroam Identity Provider or IdP is an instance that joins local users to the eduroam federation, allowing them to use the eduroam network anywhere they go. Only institutions in education or research may join the federation.
Now that we have a basic understanding of what eduroam is and what it does, let’s get to setting up our own instance.
We assume you have a fresh installation of EL9 (Rocky/Alma/RHEL), with static IP addresses and ports 1812/udp and 1813/udp open.
Bismillah, let us begin !
1. Synchronize the system clock
Clock synchronization is very important when dealing with authentication and authorization requests. It ensure accurate timestamping and facilitates effective log analysis.
Make sure ntpd or chronyd is ruuning & enabled:
systemctl status chronyd
If it’s not running install ntp and start/enable it.
Then check the date for correct time zone:
timedatectl set-timezone Africa/Casablanca
2. Update and reboot
We will update the system to get security patches and reboot to work with the latest available kernel.
dnf -y update
reboot
3. Install freeradius and open firewall
We’ll be working with freeradius for our eduroam instance. So let us install freeradius from the epel-release repository.
FreeRADIUS is a modular, opensource implementation of the RADIUS protocol developed and distributed under the GNU GPLv2 licence.
dnf -y install epel-release
dnf -y install freeradius freeradius-utils
The default config in /etc/raddb/radiusd.conf should be fine, but we can tune to our liking.
Let’s open radius ports on firewalld
firewall-cmd --permanent --add-service=radius
firewall-cmd --reload
4. Configure clients and proxies
In this section we’ll configure the files clients.conf
and proxy.conf
which are both crucial in any RADIUS deployment.
4.1 Configure clients
FreeRADIUS uses the clients.conf
file to establish which devices are authorised to interact with the RADIUS server. For this, it uses a set of IP addresses and corresponding shared secrets to control access to the RADIUS server.
You’ll find that the existing /etc/raddb/clients.conf
is bloated. For simplicity’s sake, we’ll rename it /etc/raddb/clients.conf.og
, and create a new one with the following configuration.
/etc/raddb/clients.conf
#############
### Wireless controller config ###
#client uni_wlc_ipv4 {
# ipaddr = 192.1xx.0.18
# secret = sharedSecretGoesHere
# require_message_authenticator = no
# nas_type = other
# Operator-Name = 1uni.ac.ma
# add_cui = yes
#}
#client uni_wlc_ipv6 {
# ipaddr = 2001:db8::18
# secret = sharedSecretGoesHere
# require_message_authenticator = no
# nas_type = other
# Operator-Name = 1uni.ac.ma
# add_cui = yes
#}
### IPv4 localhost server for tests ###
client localhost_ipv4 {
ipaddr = 127.0.0.1
secret = testing123
require_message_authenticator = no
nas_type = other
limit {
max_connections = 16
lifetime = 0
idle_timeout = 30
}
}
### IPv6 localhost server for tests ###
client localhost_ipv6 {
ipv6addr = ::1
secret = testing123
require_message_authenticator = no
nas_type = other
limit {
max_connections = 16
lifetime = 0
idle_timeout = 30
}
}
### Morocco eTLR servers ###
client eduroam-morocco-flr1-ipv4 {
ipaddr = 196.200.131.17
secret = placeSharedSecr3there
nastype = 'eduroam_flr'
}
client eduroam-morocco-flr1-ipv6 {
ipaddr = 2001:4310:f6:d::17
secret = placeSharedSecr3tHere
nastype = 'eduroam_flr'
}
client eduroam-morocco-flr2-ipv4 {
ipaddr = 196.200.160.18
secret = placeSharedSecretHere
nastype = 'eduroam_flr'
}
client eduroam-morocco-flr2-ipv6 {
ipaddr = 2001:4310:f1::18
secret = placeSharedSecretHere
nastype = 'eduroam_flr'
}
#############
4.2 (Optional) CUI for eduroam
Chargeable-User-Identity (CUI) serves as an alias to the user’s real identity and is an answer to accounting problems caused by the use of anonymous identity in some EAP methods. In eduroam, the primary use of CUI is in incident handling, but it can also enhance local accounting.
Let’s setup CUI for eduroam logging:
dnf --enablerepo=crb install freeradius-sqlite
Create a new file named /etc/raddb/mods-available/eduroam_cui_log
touch /etc/raddb/mods-available/eduroam_cui_log
chmod 640 /etc/raddb/mods-available/eduroam_cui_log
chgrp radiusd /etc/raddb/mods-available/eduroam_cui_log
Then edit it with the following content
/etc/raddb/mods-available/eduroam_cui_log
linelog cui_log {
# filename = syslog
filename = ${logdir}/radius.log
format = ""
reference = "auth_log.%{%{reply:Packet-Type}:-format}"
auth_log {
Access-Accept = "%t : eduroam-auth#ORG=%{request:Realm}#USER=%{User-Name}#CSI=%{%{Calling-Station-Id}:-Unknown Caller Id}#NAS=%{%{Called-Station-Id}:-Unknown Access Point}#CUI=%{%{reply:Chargeable-User-Identity}:-Unknown}#MSG=%{%{EAP-Message}:-No EAP Message}#RESULT=OK#"
Access-Reject = "%t : eduroam-auth#ORG=%{request:Realm}#USER=%{User-Name}#CSI=%{%{Calling-Station-Id}:-Unknown Caller Id}#NAS=%{%{Called-Station-Id}:-Unknown Access Point}#CUI=%{%{reply:Chargeable-User-Identity}:-Unknown}#MSG=%{%{reply:Reply-Message}:-No Failure Reason}#RESULT=FAIL#"
}
}
Create a softlink
cd /etc/raddb/mods-enabled; ln -s ../mods-available/eduroam_cui_log; ln -s ../mods-available/cui
Edit the CUI policy file /etc/raddb/policy.d/cui
/etc/raddb/policy.d/cui
cui_hash_key = "changeme" # --> replace with a random string
# if you use a secondary or backup FreeRADIUS server, use the same cui_hash_key
# this allows you to keep the same CUI log even if the FreeRADIUS server change
cui_require_operator_name = "yes"
Modify the post proxy attribute filter:
/etc/raddb/mods-config/attr_filter/post-proxy
DEFAULT
...
User-Name =* ANY,
Chargeable-User-Identity =* ANY,
...
Edit policy.d/filter, add a filter function ‘cui_filter’. Simple example :
# Filter the Chargeable-User-Identity attribute
cui_filter {
if (&reply:Chargeable-User-Identity =~ /REPLACE-WITH-CUI-TO-MATCH/) {
update request {
&Module-Failure-Message += "Rejected: CUI matching '%{reply:Chargeable-User-Identity}'"
}
reject
}
}
4.3 Configure proxies
The proxy.conf
file in FreeRADIUS manages RADIUS requests proxying between servers. It basically routes authentication and authorization requests across network infrastructure.
Same as before, the existing /etc/raddb/proxy.conf
is quite big. To simplify, we rename it /etc/raddb/proxy.conf.og
, and create a new one with the following configuration.
/etc/raddb/proxy.conf
#############
proxy server {
default_fallback = no
}
home_server localhost {
type = auth
ipaddr = ::1
port = 1812
secret = testing123
response_window = 20
zombie_period = 40
revive_interval = 120
status_check = status-server
check_interval = 30
check_timeout = 4
num_answers_to_alive = 3
max_outstanding = 65536
coa {
irt = 2
mrt = 16
mrc = 5
mrd = 30
}
limit {
max_connections = 16
max_requests = 0
lifetime = 0
idle_timeout = 0
}
}
# Modify with our university's domain
realm uni.ac.ma {
type = radius
authhost = LOCAL
accthost = LOCAL
}
# Modify with our university's domain
realm "~(.*\.)uni\.ac\.ma$" {
type = radius
authhost = LOCAL
accthost = LOCAL
}
realm LOCAL {
type = radius
authhost = LOCAL
accthost = LOCAL
}
realm NULL {
type = radius
authhost = LOCAL
accthost = LOCAL
}
### Morocco eTLR servers ###
home_server eduroam_morocco_flr1_ipv4 {
type = auth+acct
ipaddr = 196.200.131.17
port = 1812
secret = sharedSecretGoesHere
response_window = 20
zombie_period = 40
status_check = status-server
revive_interval = 60
check_interval = 30
num_answers_to_alive = 3
}
home_server eduroam_morocco_flr1_ipv6 {
type = auth+acct
ipaddr = 2001:4310:f6:d::17
port = 1812
secret = sharedSecretGoesHere
response_window = 20
zombie_period = 40
status_check = status-server
revive_interval = 60
check_interval = 30
num_answers_to_alive = 3
}
home_server eduroam_morocco_flr2_ipv4 {
type = auth+acct
ipaddr = 196.200.160.18
port = 1812
secret = sharedSecretGoesHere
response_window = 20
zombie_period = 40
status_check = status-server
revive_interval = 60
check_interval = 30
num_answers_to_alive = 3
}
home_server eduroam_morocco_flr2_ipv6 {
type = auth+acct
ipaddr = 2001:4310:f1::18
port = 1812
secret = sharedSecretGoesHere
response_window = 20
zombie_period = 40
status_check = status-server
revive_interval = 60
check_interval = 30
num_answers_to_alive = 3
}
home_server_pool eduroam_morocco_pool {
type = keyed-balance
home_server = eduroam_morocco_flr2_ipv4
home_server = eduroam_morocco_flr1_ipv4
}
### Catch all non local users and route the requests to Morocco's eFLR servers
realm "~.+$" {
auth_pool = eduroam_morocco_pool
nostrip
}
#############
Let’s not forget to modify the file configuration with our own parameters, especially the university/institute’s domain and shared secret.
We’ll need to configure the shared secrets in this file with the National Roaming Operator (NRO).
5. Configure the sites eduroam and eduroam-inner-tunnel
Now let’s configure the eduroam site and inner tunnel. Delete all the files in sites-enabled folder. Create two files called eduroam and eduroam-inner-tunnel and paste the contents from below.
5.1 Configure eduroam
unlink /etc/raddb/sites-enabled/default
vi /etc/raddb/sites-available/eduroam
/etc/raddb/sites-available/eduroam
##############
# The domain users will add to their username to have their credentials
# routed to our institution. We will also need to register this
# and our RADIUS server addresses with the NRO.
operator_name = "univ.ma"
# The VLAN to assign eduroam visitors
eduroam_default_guest_vlan = "17"
# The VLAN to assign our students/staff
eduroam_default_local_vlan = "14"
server eduroam {
listen {
type = auth
ipaddr = *
port = 0
limit {
max_connections = 16
lifetime = 0
idle_timeout = 30
}
}
listen {
type = acct
ipaddr = *
port = 0
limit {
}
}
listen {
type = auth
ipv6addr = ::
port = 0
limit {
max_connections = 16
lifetime = 0
idle_timeout = 30
}
}
listen {
type = acct
ipv6addr = ::
port = 0
limit {
}
}
authorize {
update request {
Operator-Name := "1univ.ma"
# The "1" above is important, do not change.
}
cui
preprocess
filter_username
auth_log
suffix
eap {
ok = return
updated = return
}
files
#ldap
pap
}
pre-proxy {
attr_filter.pre-proxy
#linelog_send_proxy_request
cui
}
post-proxy {
attr_filter.post-proxy
#linelog_recv_proxy_response
}
authenticate {
Auth-Type PAP {
pap
#ldap
}
Auth-Type CHAP {
chap
#ldap
}
Auth-Type MS-CHAP {
mschap
#ldap
}
#Auth-Type LDAP {
#ldap
#}
eap
}
post-auth {
cui
cui_log
update reply {
Tunnel-Type := VLAN
Tunnel-Medium-Type := IEEE-802
}
if (&control:Proxy-To-Realm) {
update reply {
Tunnel-Private-Group-ID = ${eduroam_default_guest_vlan}
}
}
else {
update reply {
Tunnel-Private-Group-ID = ${eduroam_default_local_vlan}
}
}
# We're sending a response to one of OUR network devices for one of
# OUR users so provide it with the real user-identity.
if (&session-state:Stripped-User-Name) {
update reply {
User-Name := "%{session-state:Stripped-User-Name}@%{Stripped-User-Domain}"
}
}
#linelog_send_accept
Post-Auth-Type REJECT {
attr_filter.access_reject
#linelog_send_reject
}
}
}
##############
Once we’ve created the file, let us fix eduroam site file permissions and add a soft link to it from the /etc/raddb/sites-enabled/
.
chgrp radiusd /etc/raddb/sites-available/eduroam
chmod 640 /etc/raddb/sites-available/eduroam
chcon system_u:object_r:radiusd_etc_t:s0 /etc/raddb/sites-available/eduroam
cd /etc/raddb/sites-enabled/
ln -s ../sites-available/eduroam eduroam
chgrp -ch radiusd eduroam
5.2 Configure eduroam-inner-tunnel
We’ll do the same thing for eduroam-inner-tunnel:
- Unlink the default /etc/raddb/sites-enabled/inner-tunnel
.
- Create the new /etc/raddb/sites-available/eduroam-inner-tunnel
with contents from below.
- Then create a soft link in /etc/raddb/sites-enabled/
.
/etc/raddb/sites-available/eduroam-inner-tunnel
###############
server eduroam-inner-tunnel {
listen {
type = auth
ipaddr = 127.0.0.1
port = 18120 # Used for testing only. Requests proxied internally.
}
authorize {
chap
mschap
suffix
update control {
Proxy-To-Realm := LOCAL
}
eap {
ok = return
}
files
expiration
logintime
pap
}
authenticate {
Auth-Type PAP {
pap
}
Auth-Type CHAP {
chap
}
Auth-Type MS-CHAP {
mschap
}
#Auth-Type LDAP {
#ldap
#}
eap
}
session {
}
post-auth {
reply_log
Post-Auth-Type REJECT {
attr_filter.access_reject
reply_log
update outer.session-state {
&Module-Failure-Message := &request:Module-Failure-Message
}
}
}
pre-proxy {
}
post-proxy {
eap
}
}
#############
6. Configure certificates for the eap module
If you already have a certificate and key, copy them to /etc/raddb/certs/
.
Alternatively you can create a self-signed certificate and key using openssl by following this guide. (NB: the key file should have 600 in permissions)
Recommended certificate properties: https://wiki.geant.org/pages/viewpage.action?pageId=121346259#Howtodeployeduroamonsiteoroncampus(ADVANCED)-Consideration2:Recommendedcertificateproperties
Let’s rename /etc/raddb/mods-available/eap
into /etc/raddb/mods-available/eap.og
and then put the content below into the file.
/etc/raddb/mods-enabled/eap
##############
eap {
default_eap_type = ttls
timer_expire = 60
ignore_unknown_eap_types = no
cisco_accounting_username_bug = no
max_sessions = ${max_requests}
md5 {
}
leap {
}
gtc {
auth_type = PAP
}
tls-config tls-common {
certificate_file = ${certdir}/eduroam.univ.ma.pem
private_key_file = ${certdir}/eduroam.univ.ma.key
# private_key_password = whatever
ca_file = ${cadir}/univCACert.pem
# If our AP drops packets towards the client, try reducing this.
fragment_size = 1024
# When issuing client certificates embed the OCSP URL in the
# certificate if you want to be able to revoke them later.
ocsp {
enable = yes
override_cert_url = no
use_nonce = yes
}
}
tls {
tls = tls-common
}
ttls {
tls = tls-common
default_eap_type = gtc
copy_request_to_tunnel = yes
use_tunneled_reply = yes
virtual_server = "eduroam-inner-tunnel"
}
peap {
tls = tls-common
default_eap_type = mschapv2
copy_request_to_tunnel = no
use_tunneled_reply = no
virtual_server = "eduroam-inner-tunnel"
}
mschapv2 {
}
}
##############
7. Start the RADIUS server
To test if our configuration is correct, let’s first start the server in debug mode.
radiusd -X
If all is OK, now let’s start the FreeRADIUS daemon.
systemctl start radiusd
And then let’s enable our radius daemon, so that it starts automatically everytime the system reboots.
systemctl enable radiusd
8. (For IdPs only) Configure an authentication method
We are currently supporting 2 ways of configuring user authentication with freeradius:
- For openldap, go to eduroam IdP with OpenLDAP
- For active directory, go to eduroam IdP with Active Directory
9. Test the RADIUS server
Now that we’re done configuring our RADIUS eduroam server, let’s do some tests !
You can run radius X
to troubleshoot any possible issues.
[root@eduroam ~]# radtest user@uni.ac.ma userPasswd localhost 1812 testing123
Sent Access-Request Id 77 from 0.0.0.0:37126 to 127.0.0.1:1812 length 77
User-Name = "user@uni.ac.ma"
User-Password = "userPasswd"
NAS-IP-Address = 196.200.160.149
NAS-Port = 1812
Message-Authenticator = 0x00
Cleartext-Password = "userPasswd"
Received Access-Accept Id 77 from 127.0.0.1:1812 to 127.0.0.1:37126 length 36
Tunnel-Type:0 = VLAN
Tunnel-Medium-Type:0 = IEEE-802
Tunnel-Private-Group-Id:0 = "14"