Skip to content

FreeRADIUS Installation and Configuration on Ubuntu Server 24.04 (OpenLDAP)

Based on IAM by LANKA (Lanka Education and Research Network) (learn@learn.ac.lk)

1. Prerequisites

  • 1.1 Minimum System Requirements:
    • 4GB or more for small to medium deployments.
    • Quad-core or higher with a minimum clock speed of 2.0 GHz.
    • At least 50GB of free disk space.
    • Ports allowed (tcp/80, tcp/443, udp/1812, udp/1813)

2. Update your server and install curl

sudo apt update

sudo apt upgrade

sudo apt install curl

3. Install Repo from NetworkRADIUS

To install FreeRADIUS 3.2 on Ubuntu noble 24.04, Add the NetworkRADIUS PGP public key:

install -d -o root -g root -m 0755 /etc/apt/keyrings
curl -s 'https://packages.networkradius.com/pgp/packages%40networkradius.com' | \
    sudo tee /etc/apt/keyrings/packages.networkradius.com.asc > /dev/null

Add an APT preferences file to ensure all freeradius packages are installed from the Network RADIUS repository:

printf 'Package: /freeradius/\nPin: origin "packages.networkradius.com"\nPin-Priority: 999\n' | \
    sudo tee /etc/apt/preferences.d/networkradius > /dev/null

Add the APT sources list:

echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/packages.networkradius.com.asc] http://packages.networkradius.com/freeradius-3.2/ubuntu/noble noble main" | \
    sudo tee /etc/apt/sources.list.d/networkradius.list > /dev/null

Finally, update the APT database and install the packages:

sudo apt update

4. Install Packages

You need to become root by sudo su and proceed.

sudo apt install freeradius 

sudo apt install git libssl-dev devscripts pkg-config libnl-3-dev libnl-genl-3-dev

5. Modify Users for testing

Next, sudo vim /etc/freeradius/users and modify to enable bob and test realm user

#
#bob     Cleartext-Password := "hello"
#       Reply-Message := "Hello, %{User-Name}"
#
eduroamtest         Cleartext-Password := "EduTestP@33"
####

After the user modification following radtests should succeed.

sudo systemctl restart freeradius.service
radtest -t mschap -x eduroamtest  EduTestP@33 127.0.0.1:1812 10000 testing123

6. Build eapol tool for testing

git clone --depth 1 --single-branch https://github.com/FreeRADIUS/freeradius-server.git

cd freeradius-server/scripts/ci/

./eapol_test-build.sh

sudo cp ./eapol_test/eapol_test /usr/local/bin/

You can now test the above radius local user authentication with eapol_test as below. For eapol_test command you need to create a configuration file which describes the network connection properties. Let's create a configuration file.

cd /etc/freeradius

sudo vim peap-mschapv2-local.conf

Add the below code,

network={
        ssid="eduroam"
        key_mgmt=WPA-EAP
        eap=PEAP
        identity="eduroamtest"
#       anonymous_identity="@eduroam.gh"
        password="EduTestP@33"
        phase2="auth=MSCHAPV2"

        #  Uncomment the following to perform server certificate validation.
#       ca_cert="/etc/raddb/certs/ca.der"
}

And execute the eapol_test as below,

eapol_test -c peap-mschapv2-local.conf -p 1812 -s testing123

If the authentication is successful you should recieve at the end,

MPPE keys OK: 1  mismatch: 0
SUCCESS

7. Freeradius Settings

Go to install location and do the changes.

cd /etc/freeradius/
sudo mv mods-config/attr_filter/pre-proxy mods-config/attr_filter/pre-proxy.orig
sudo mv mods-config/attr_filter/post-proxy mods-config/attr_filter/post-proxy.orig

Edit the file pre-proxy with following content:

sudo vim mods-config/attr_filter/pre-proxy
DEFAULT
        User-Name =* ANY,
        EAP-Message =* ANY,
        Message-Authenticator =* ANY,
        NAS-IP-Address =* ANY,
        NAS-Identifier =* ANY,
        State =* ANY,
        Proxy-State =* ANY,
        Calling-Station-Id =* ANY,
        Called-Station-Id =* ANY,
        Operator-Name =* ANY,
        Class =* ANY,
        Chargeable-User-Identity =* ANY

Edit the file post-proxy with following content:

sudo vim mods-config/attr_filter/post-proxy
DEFAULT
        Framed-IP-Address == 255.255.255.254,
        Framed-IP-Netmask == 255.255.255.255,
        Framed-MTU >= 576,
        Framed-Filter-ID =* ANY,
        Reply-Message =* ANY,
        Proxy-State =* ANY,
        EAP-Message =* ANY,
        Message-Authenticator =* ANY,
        MS-MPPE-Recv-Key =* ANY,
        MS-MPPE-Send-Key =* ANY,
        MS-CHAP-MPPE-Keys =* ANY,
        State =* ANY,
        Session-Timeout <= 28800,
        Idle-Timeout <= 600,
        Calling-Station-Id =* ANY,
        Operator-Name =* ANY,
        Port-Limit <= 2,
        User-Name =* ANY,
        Class =* ANY,
        Chargeable-User-Identity =* ANY

Backup the eap module configuration file as follows,

sudo mv mods-available/eap mods-available/eap.orig

sudo vim mods-available/eap

Now modify the configuration file to make the below changes. Don't delete any additional configurations not show below. Also some of the below configurations also might be the same as them in your configuration file, hence need to change the selected parts only.

eap {
                default_eap_type = peap     # change to your organisation's preferred eap type (tls, ttls, peap, mschapv2)
                timer_expire     = 60
                ignore_unknown_eap_types = no
                cisco_accounting_username_bug = no

                tls-config tls-eduroam {
                       #private_key_password = whatever
                        private_key_file = ${certdir}/server.pem
                        certificate_file = ${certdir}/server.pem
                        ca_file = ${cadir}/ca.pem
                        #dh_file = ${certdir}/dh
                        random_file = /dev/urandom
                        fragment_size = 1024
                        include_length = yes
                        check_crl = no
                        cipher_list = "DEFAULT"
                }

                tls {
                        tls = tls-eduroam
                }

                ttls {
                        tls = tls-eduroam
                        default_eap_type = mschapv2
                        copy_request_to_tunnel = yes
                        use_tunneled_reply = yes
                        virtual_server = "eduroam-inner-tunnel"
                }

                peap {
                        tls = tls-eduroam
                        default_eap_type = mschapv2
                        copy_request_to_tunnel = yes
                        use_tunneled_reply = yes
                        virtual_server = "eduroam-inner-tunnel"
                }

                mschapv2 {
                #       send_error = yes
                }

        }

You need to modify the linelog module as follows too,

sudo vim mods-enabled/linelog

Modify the following lines containing Access-Accept and Access-Reject

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}#NAS-IP=%{%{NAS-IP-Address}:-Unknown}#OPERATOR=%{%{Operator-Name}:-Unknown}#CUI=%{%{reply:Chargeable-User-Identity}:-Unknown}#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}#NAS-IP=%{%{NAS-IP-Address}:-Unknown}#OPERATOR=%{%{Operator-Name}:-Unknown}#CUI=%{%{reply:Chargeable-User-Identity}:-Unknown}#MSG=%{%{reply:Reply-Message}:-No Failure Reason}#RESULT=FAIL#"

Chargeable-User-Identity (CUI) is a non-human readable (“opaque”) cryptographic hash that is targeted to the service provider. Each service provider therefore receives a different opaque value for the same user. This allows service providers to recognize a user as one that they have seen before, without knowing who the user is; while preventing service providers from colluding to track users. This enables legitimate purposes, such as blocking malfunctioning devices and generating accurate usage statistics. The CUI value is computed as a SHA1 hash of concatenated (inner) User-Name, optional Operator-Name and a local salt value. This salt is random string and we have to set this salt in the cui_hash_key attribute.

Run the command below to generate a 16-byte (128-bit) salt in base64

openssl rand -base64 16

Modify the cui policy as follows,

sudo vim policy.d/cui
cui_hash_key = "SOMELONGCHARACTERstring"
cui_require_operator_name = "yes"

8. Creating Certificates

Certificates can be obtained using a service like LetsEncrypt or Commercial provider. We can also create certificates using a private CA.You need to only follow a one method.

Method 1: Create certificates using LetsEncrypt

Install the certbot utility from LetsEncrypt. On Debian it can be installed from the default repositories:

apt-get install certbot

Add a group for the certificate and key files, and then add the FreeRADIUS user to this group so that FreeRADIUS can read the certificate files:

addgroup certs
adduser freerad certs

Configure certbot

We will use a script to restart FreeRADIUS whenever certbot renews the certificate. This script will also ensure that the permission are always set correctly on the certificate files, so that FreeRADIUS is able to read them.

Create a file /usr/local/sbin/certbot-post-hook with the following contents:

#!/bin/sh

#  Ensure certificates are in the correct group
chgrp -R certs /etc/letsencrypt/live /etc/letsencrypt/archive
chmod 750 /etc/letsencrypt/live /etc/letsencrypt/archive

#  Restart FreeRADIUS
service freeradius restart
chmod +x /usr/local/sbin/certbot-post-hook

Generate the Certificate

The system is now ready to request a certificate from LetsEncrypt. We will be using the example FQDN of radius.example.com here; replace this name with whatever name you have in your DNS, and which points to the public IP address of the RADIUS server.

nano mods-available/eap
private_key_file = /etc/letsencrypt/live/SERVER_FQDN/privkey.pem
certificate_file = /etc/letsencrypt/live/SERVER_FQDN/fullchain.pem
certbot certonly \
        --standalone \
        --cert-name radius.example.com \
        -d radius.example.com
bash /usr/local/sbin/certbot-post-hook

Method 2: Create Certificates Using Private CA

By default, the FreeRADIUS installation generates certificates that are only valid for 60 days. To generate new certificates, follow these steps:

find /etc/freeradius/certs -type f ! -name "Makefile" ! -name "bootstrap" ! -name "ca.cnf" ! -name "client.cnf" ! -name "inner-server.cnf" ! -name "server.cnf" ! -name "xpextensions" ! -name "index.txt" -delete

The above command deletes everything in the cert folder except the following files:

  - Makefile
  - bootstrap
  - ca.cnf
  - client.cnf
  - inner-server.cnf
  - server.cnf
  - xpextensions
cd /etc/freeradius/certs/

Populate the ca.cnf with the following updates:

[CA_default]
# ...
default_days = 3650
default_crl_days = 3650
# ...

[req]
# ...
input_password = private-key-password-set-in-step-7-in-eap-configuration
output_password = private-key-password-set-in-step-7-in-eap-configuration
x509_extensions = v3_ca
# ...

[certificate_authority]
countryName             = GH
stateOrProvinceName     = Central
localityName            = Somewhere
organizationName        = Univerity of ABC
emailAddress            = admin@YOUR_DOMAIN
commonName              = "Univerity of ABC Certificate Authority"

[v3_ca]
#....
crlDistributionPoints   = URI:http://server-fqdn/domain_ca.crl

Populate the client.cnf with the following updates:

[CA_default]
# ...
default_days = 3650
default_crl_days = 3650
# ...

[req]
# ...
input_password = private-key-password-set-in-step-7-in-eap-configuration
output_password = private-key-password-set-in-step-7-in-eap-configuration
# ...

[client]
countryName             = GH
stateOrProvinceName     = Central
localityName            = Somewhere
organizationName        = University of ABC
emailAddress            = wifiuser@abc.edu.gh
commonName              = "Wifi User Client Certificate"

Populate the server.cnf with the following updates:

[CA_default]
# ...
default_days = 3650
default_crl_days = 3650
# ...

[req]
# ...
input_password = private-key-password-set-in-step-7-in-eap-configuration
output_password = private-key-password-set-in-step-7-in-eap-configuration
# ...

[server]
countryName             = GH
stateOrProvinceName     = Central
localityName            = Somewhere
organizationName        = University of ABC
emailAddress            = radius@abc.edu.gh
commonName              = "radius.abc.edu.gh"

[alt_names]
DNS.1 = server-fqdn
otherName.0 = 1.3.6.1.5.5.7.8.8;FORMAT:UTF8,UTF8:*.YOUR-DOMAIN.edu.gh

Remove exisiting certificate generation entry in index.txt to allow the generation of new certificates

Generate certificates by running the make command

make
chown freerad:freerad *

9. Sites Configurations

Create virtual server for eduroam as

cd /etc/freeradius/
sudo vim sites-available/eduroam
######################################################################
#
# Virtual Server Eduroam
#
######################################################################


server eduroam {

listen {
    type = auth
    ipaddr = *
    port = 0
    limit {
          max_connections = 16
          lifetime = 0
          idle_timeout = 30
    }
}

listen {
    ipaddr = *
    port = 0
    type = acct
    limit {
    }
}


listen {
    type = auth
    ipv6addr = ::
    port = 0
    limit {
          max_connections = 16
          lifetime = 0
          idle_timeout = 30
    }
}

listen {
    ipv6addr = ::
    port = 0
    type = acct
    limit {

    }
}


authorize {

    preprocess
    filter_username
        if (("%{client:shortname}" != "FLR1")||("%{client:shortname}" != "FLR2")) {
                   update request {
                           Operator-Name := "1YOUR_DOMAIN"
                            # the literal number "1" above is an important prefix! Do not change it!
         }
        }
    operator-name
    cui
    auth_log
    suffix
    eap {
        ok = return
    }
    files

    -ldap

}



authenticate {

    eap


}


preacct {
    suffix

}


accounting {

}

session {
}


post-auth {

    update {
        &reply: += &session-state:
    }

    reply_log
    linelog
    remove_reply_message_if_eap
    Post-Auth-Type REJECT {
        reply_log
        linelog
    }
}


pre-proxy {

        # if you want detailed logging
    cui
    pre_proxy_log  # logs the packet to the file system again. Attributes that have been added on during inspection are now visible

        if("%{Packet-Type}" != "Accounting-Request") {

              attr_filter.pre-proxy   # removes unnecessary attributes off of the request before sending the request upstream

                }
}

post-proxy {
                # if you want detailed logging

                post_proxy_log              # logs the rply packet to the file system - as received by upstream

                attr_filter.post-proxy      # strips unwanted attributes off of the reply, prior to sending it back to the Access Points (VLAN attributes in particular)


}
}

Create virtual server for eduroam-inner-tunnel.

sudo vim sites-available/eduroam-inner-tunnel
######################################################################
#
# Virtual Server Eduroam-Inner-Tunnel
#
######################################################################
server eduroam-inner-tunnel {

listen {
       ipaddr = 127.0.0.1
       port = 18120
       type = auth
}

authorize {
    auth_log
    suffix
    update control {
            &Proxy-To-Realm := LOCAL
    }
    eap {
        ok = return
    }
    files
    -ldap
    mschap
    pap
}

authenticate {

    Auth-Type PAP {
        pap
    }

    Auth-Type MS-CHAP {
        mschap
    }

    eap
}


session {
    radutmp

}



post-auth {
    cui-inner
    reply_log
    Post-Auth-Type REJECT {
        reply_log
        attr_filter.access_reject
        update outer.session-state {
            &Module-Failure-Message := &request:Module-Failure-Message
        }
    }
}


pre-proxy {

}


post-proxy {
    eap
}
}

Create vim sites-available/blackhole for blackholing

server blackhole {
    authorize {
        reject
    }
}

Next you need to enable the created virtual-server sites above and also remove the unwanted,

cd sites-enabled
rm default
rm inner-tunnel
ln -s ../sites-available/eduroam-inner-tunnel eduroam-inner-tunnel
ln -s ../sites-available/eduroam eduroam
ln -s ../sites-available/blackhole blackhole

Then modify proxy.conf

cd /etc/freeradius
sudo mv proxy.conf proxy.conf.orig
sudo vim proxy.conf
proxy server {
        default_fallback        = no
}

# Add your country's FLR details for the home_server {} attribute as shown below. port and status_check will not change.
# Add as many definitions as there are FLRs

home_server FLR2 {
        ipaddr                  = 169.239.249.51
        port                    = 1812
        secret                  = secretthatmustbesharedwithGARNET
        status_check            = status-server
}
home_server FLR1 {
        ipaddr                  = 197.255.124.73
        port                    = 1812
        secret                  = secretthatmustbesharedwithGARNET
        status_check            = status-server
}
realm LOCAL {
        #  If we do not specify a server pool, the realm is LOCAL, and
        #  requests are not proxied to it.
}

# eduroam home_server_pool attribute links from the home_server attribute. ensure home_server in home_server_pool matches home_server above
home_server_pool EDUROAM {
        type                    = fail-over
        home_server             = FLR2
        home_server             = FLR1
}


# Your IdP realm
realm YOUR-DOMAIN.edu.gh {
        nostrip #uncomment to remove striping of realm from username
}

# Catchall for unhandled realms
# redirect them to a blackhole server
#
home_server blackhole {
    virtual_server = blackhole
}

home_server_pool blackhole_pool {
    home_server = blackhole
    name = blackhole
}

realm  wlan.mnc000.mcc413.3gppnetwork.org{
    auth_pool = blackhole_pool
}

realm  wlan.mnc001.mcc413.3gppnetwork.org{
    auth_pool = blackhole_pool
}

realm  wlan.mnc002.mcc413.3gppnetwork.org{
    auth_pool = blackhole_pool
}

realm  wlan.mnc003.mcc413.3gppnetwork.org{
auth_pool = blackhole_pool
}

realm  wlan.mnc004.mcc413.3gppnetwork.org{
    auth_pool = blackhole_pool
}

realm  wlan.mnc005.mcc413.3gppnetwork.org{
    auth_pool = blackhole_pool
}

realm  wlan.mnc006.mcc413.3gppnetwork.org{
auth_pool = blackhole_pool
}

realm  wlan.mnc007.mcc413.3gppnetwork.org{
    auth_pool = blackhole_pool
}

realm  wlan.mnc008.mcc413.3gppnetwork.org{
    auth_pool = blackhole_pool
}

realm  wlan.mnc009.mcc413.3gppnetwork.org{
auth_pool = blackhole_pool
}

###########################################
# Proxy the rest
realm "~.+$" {
        pool                    = EDUROAM
        nostrip
}

Modify Clients

vim clients.conf

Add following to the tail

client Private-Network-1 {
        ipaddr          = 10.0.0.0/8
        secret          = verypowerfullsecret
        nastype         = other
}

client Private-Network-2 {
        ipaddr          = 172.16.0.0/12
        secret          = verypowerfullsecret
        nastype         = other
}

client Private-Network-3 {
        ipaddr          = 192.168.0.0/16
        secret          = verypowerfullsecret
        nastype         = other
}

client FLR2 {
        ipaddr          = 169.239.249.51
        secret          = secretthatmustbesharedwithGARNET
        nas_type         = other
    Operator-Name = 1YOUR_DOMAIN
    add_cui = yes
    virtual_server = eduroam
}

client FLR1 {
        ipaddr          = 197.255.124.73
        secret          = secretthatmustbesharedwithGARNET
        nas_type         = other
    Operator-Name = 1YOUR_DOMAIN
    add_cui = yes
    virtual_server = eduroam
}

Now restart the radius server.

sudo systemctl restart freeradius.service

After the restart, following tests should succeed.

eapol_test -c peap-mschapv2-local.conf -p 1812 -s testing123

You may also test some of the test roaming accounts provided by your National Upstream Radius Partner.

10. Enabling LDAP users

Install Freeradius LDAP module

sudo apt-get install freeradius-ldap

Configure LDAP parameters

sudo vim /etc/freeradius/mods-available/ldap

Copy ca_certs.pem from /etc/ldap/ located on the OpenLDAP server and upload to Freeradius Server. Configure LDAP parameters in ldap located in /etc/freeradius/mods-available/ and modify the appropriate lines:

server = 'LDAP_SERVER_FQDN'
identity = 'cn=admin,dc=abc,dc=edu,dc=gh' #bind User
password = 'YOUR_LDAP_PASSWORD'
base_dn = 'ou=people,dc=abc,dc=edu,dc=gh'
edir_autz = yes

#Under the tls section, Update the following
start_tls = yes
ca_file = "/etc/freeradius/ca_certs.pem"
require_cert = "demand"

Enable LDAP Module & Restart Freeradius

ln -s /etc/freeradius/mods-available/ldap /etc/freeradius/mods-enabled/ldap
service freeradius restart

Network configuration file for the ldap connectivity may look like below.

network={
        ssid="eduroam"
        key_mgmt=WPA-EAP
        eap=PEAP
        identity="user@YOUR_DOMAIN"
#       anonymous_identity="@eduroam.gh"
        password="USER-PASSWORD"
        phase2="auth=MSCHAPV2"
        #  Uncomment the following to perform server certificate validation.
#       ca_cert="/etc/raddb/certs/ca.der"
}

Test ldap user authentication:

sudo eapol_test -c peap-mschapv2-ldap.conf -p 1812 -s testing123

11. Troubleshoot

Log Path: /var/logs/freeradius/

Debug mode: * In a new console, stop freeradius service service freeradius stop * Start in debug mode freeradius -X * To stop debug mode, use CTRL+c