FreeRADIUS with LDAP

  • FreeRADIUS includes support for LDAP integration.
  • To ensure maximum benefit of RADIUSdesk we will use a hybrid setup for LDAP integration.
  • The next section will be used as a basic introduction to FreeRADIUS and LDAP

Basic FreeRADIUS with LDAP

  • We will install OpenLDAP and do a basic setup that can be used by FreeRADIUS.
  • When you install OpenLDAP (slapd) the package installer will ask you to provide a password.
  • Provide one but keep in mind that we will reconfigure everything after the install.
# Update repository lists
sudo apt update
 
# Install OpenLDAP and standard utility tools
sudo apt install -y slapd ldap-utils
 
# Install the FreeRADIUS LDAP module (rlm_ldap) 
sudo apt install -y freeradius-ldap
  • After the slapd package installed we can run the following command to reconfigure slapd and provide a basic setup.
# Configure or reconfigure slapd defaults interactively
sudo dpkg-reconfigure slapd
  • Omit OpenLDAP server configuration? No.
  • DNS domain name: Enter your domain (e.g., radiusdesk.com creating dc=radiusdesk,dc=com).
  • Organization name: Enter your organization name (We specified radiusdesk).
  • Administrator password: We used a simple password since we're testing.
  • After you answered these questions slapd should be reconfigured with those items.
  • You can confirm it by issuing the following command
sudo slapcat
dn: dc=radiusdesk,dc=com
objectClass: top
objectClass: dcObject
objectClass: organization
o: radiusdesk
dc: radiusdesk
structuralObjectClass: organization
entryUUID: 8c68dc16-1a3f-1041-9add-250d07e316fc
creatorsName: cn=admin,dc=radiusdesk,dc=com
createTimestamp: 20260722173600Z
entryCSN: 20260722173600.213629Z#000000#000#000000
modifiersName: cn=admin,dc=radiusdesk,dc=com
modifyTimestamp: 20260722173600Z
  • This confirms the basic setup of slapd.
  • Next we will add an Organizational Unit (OU) under which we can add our users. (Remember LDAP is a directory/hierarchy)
  • Create a file named ou.ldif to set up a dedicated container for your network accounts.
ou.ldif
dn: ou=people,dc=radiusdesk,dc=com
objectClass: organizationalUnit
ou: people
  • Apply this file to your OpenLDAP directory:
ldapadd -x -D "cn=admin,dc=radiusdesk,dc=com" -W -f ou.ldif
  • Create a file named user.ldif.
  • This template creates a user named testuser with the password mysecretpassword.
user.ldif
dn: uid=testuser,ou=people,dc=radiusdesk,dc=com
objectClass: top
objectClass: account
objectClass: simpleSecurityObject
uid: testuser
userPassword: mysecretpassword
description: RADIUS Test User
  • Apply this file to your OpenLDAP directory:
ldapadd -x -D "cn=admin,dc=radiusdesk,dc=com" -W -f user.ldif
  • If you run slapcat again these items should be in the reply.
sudo slapcat
dn: dc=radiusdesk,dc=com
objectClass: top
objectClass: dcObject
objectClass: organization
o: radiusdesk
dc: radiusdesk
structuralObjectClass: organization
entryUUID: 8c68dc16-1a3f-1041-9add-250d07e316fc
creatorsName: cn=admin,dc=radiusdesk,dc=com
createTimestamp: 20260722173600Z
entryCSN: 20260722173600.213629Z#000000#000#000000
modifiersName: cn=admin,dc=radiusdesk,dc=com
modifyTimestamp: 20260722173600Z
 
dn: ou=people,dc=radiusdesk,dc=com
objectClass: organizationalUnit
ou: people
structuralObjectClass: organizationalUnit
entryUUID: a6c420c6-1a48-1041-895a-41f787ad35f7
creatorsName: cn=admin,dc=radiusdesk,dc=com
createTimestamp: 20260722184109Z
entryCSN: 20260722184109.903088Z#000000#000#000000
modifiersName: cn=admin,dc=radiusdesk,dc=com
modifyTimestamp: 20260722184109Z
 
dn: uid=testuser,ou=people,dc=radiusdesk,dc=com
objectClass: top
objectClass: account
objectClass: simpleSecurityObject
uid: testuser
userPassword:: bXlzZWNyZXRwYXNzd29yZA==
description: RADIUS Test User
structuralObjectClass: account
entryUUID: f0928490-1a48-1041-895b-41f787ad35f7
creatorsName: cn=admin,dc=radiusdesk,dc=com
createTimestamp: 20260722184313Z
entryCSN: 20260722184313.729405Z#000000#000#000000
modifiersName: cn=admin,dc=radiusdesk,dc=com
modifyTimestamp: 20260722184313Z
  • Now that we have a working LDAP setup we can configure FreeRADIUS to work with this LDAP server.
  • We assume the following:
    • You have installed the OpenLDAP server on the same host that FreeRADIUS is installed on.
    • You have a clean install of FreeRADIUS. (default config)
  • FreeRADIUS keeps available modules in one directory and active ones in another.
  • Enable the LDAP module by creating a symbolic link.
sudo su
# Navigate to the enabled modules directory
cd /etc/freeradius/3.0/mods-enabled/
 
# Link the LDAP configuration module
sudo ln -s ../mods-available/ldap .
  • Edit the module configuration file to point FreeRADIUS to your local OpenLDAP instance.
  • Edit /etc/freeradius/3.0/mods-available/ldap and update these core lines:
ldap {
    # LDAP server connection parameters
    server = "localhost"
    port = 389
 
    # Identity used to search the directory
    identity = "cn=admin,dc=radiusdesk,dc=com"
    password = testing123
 
    # Base DN where user lookups will begin
    base_dn = "dc=radiusdesk,dc=com"
 
    # User lookup mapping filter
    update {
        control:Password-With-Header += 'userPassword'
    }
 
    user {
        base_dn = "ou=people,dc=radiusdesk,dc=com"
        filter = "(uid=%{%{Stripped-User-Name}:-%{User-Name}})"
    }
}
  • You must instruct FreeRADIUS to check the LDAP backend during incoming connection requests.
  • Open /etc/freeradius/3.0/sites-enabled/default and confirm the following is in the authorize block
#
#  The ldap module reads passwords from the LDAP database.
-ldap
  • The - prefix to ldap means it will silently be ignored when the LDAP module is not enabled.
  • Test the RADIUS authentication to confirm if it is working as intended
  • Start FreeRADIUS in debug mode
sudo systemctl stop freeradius
sudo freeradius -X
  • Use the radtest program to do a test authentication.
radtest testuser mysecretpassword localhost 1812 testing123
#This is the return on my setup
Sent Access-Request Id 249 from 0.0.0.0:56819 to 127.0.0.1:1812 length 78
	User-Name = "testuser"
	User-Password = "mysecretpassword"
	NAS-IP-Address = 127.0.1.1
	NAS-Port = 1812
	Message-Authenticator = 0x00
	Cleartext-Password = "mysecretpassword"
Received Access-Accept Id 249 from 127.0.0.1:1812 to 127.0.0.1:56819 length 38
	Message-Authenticator = 0x216fe46614354e897d645ce27ec9e0d8
  • Lets look at the LDAP module's debug output in FreeRADIUS
rlm_ldap (ldap): Reserved connection (1)
(2) ldap: EXPAND (uid=%{%{Stripped-User-Name}:-%{User-Name}})
(2) ldap:    --> (uid=testuser)
(2) ldap: Performing search in "ou=people,dc=radiusdesk,dc=com" with filter "(uid=testuser)", scope "sub"
(2) ldap: Waiting for search result...
(2) ldap: User object found at DN "uid=testuser,ou=people,dc=radiusdesk,dc=com"
(2) ldap: Processing user attributes
(2) ldap: control:Password-With-Header += 'mysecretpassword'
rlm_ldap (ldap): Released connection (1)
Need 1 more connections to reach min connections (3)
Need more connections to reach 10 spares
rlm_ldap (ldap): Opening additional connection (7), 1 of 30 pending slots used
rlm_ldap (ldap): Connecting to ldap://localhost:389
rlm_ldap (ldap): Waiting for bind result...
rlm_ldap (ldap): Bind successful
(2)     [ldap] = updated
(2)     [expiration] = noop
(2)     [logintime] = noop
(2) pap: No {...} in Password-With-Header, re-writing to Cleartext-Password
(2) pap: Removing &control:Password-With-Header
(2)     [pap] = updated
(2)   } # authorize = updated
(2) Found Auth-Type = PAP
  • Based on the debug output we can see the following happens when the ldap module is angaged
    • The LDAP module does a search for the user.
    • When the LDAP server replies with a result the LDAP module looks for the userPassword LDAP reply attribute.
    • This is the clear-text password of the user and not all LDAP servers provides it.
    • OpenLDAP does provide it (control:Password-With-Header += 'mysecretpassword')
    • It then tries an LDAP bind as the user to confirm the password is working.
    • If this worked it sets the Cleartext-Password so we can do PAP authentication. (Auth-Type = PAP)
  • Not all LDAP authentications works like this since not all LDAP servers will return the user's password in cleartext.
  • We will cover those later in the document.

FreeRADIUS with Secure LDAP

  • When you plan to use any external LDAP server with FreeRADIUS they will typically be forcing you to connect securely via SSL/TLS on port 636.
  • In this section we will:
    • Create a PKI. This includes creating a CA with cert and key for slapd (OpenLDAP)
    • Configure OpenLDAP to also support ldaps.
    • Configure FreeRADIUS to communicate via ldaps with slapd (OpenLDAP).
  • Create a working directory
mkdir -p ~/pki
cd ~/pki
  • Generate the CA key
openssl genpkey -algorithm RSA -out ca.key -pkeyopt rsa_keygen_bits:4096
  • Create the CA certificate
openssl req -new -x509 -days 3650 -key ca.key -sha256 -out ca.crt
#### This is what we filled in ####
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:ZA
State or Province Name (full name) [Some-State]:Gauteng
Locality Name (eg, city) []:Johannesburg
Organization Name (eg, company) [Internet Widgits Pty Ltd]:RADIUSdesk.com
Organizational Unit Name (eg, section) []:PKI
Common Name (e.g. server FQDN or YOUR name) []:RADIUSdesk Internal Root CA
Email Address []:dirk@gmail.com
  • Keep ca.key secret.
  • Issue the following command:
openssl genpkey -algorithm RSA -out ldap.key -pkeyopt rsa_keygen_bits:4096
  • Create ldap.conf. Use the following as reference.
[req]
default_bits = 4096
prompt = no
distinguished_name = dn
req_extensions = req_ext

[dn]
CN = ldap.radiusdesk.com

[req_ext]
subjectAltName = @alt_names

[alt_names]
DNS.1 = ldap.radiusdesk.com
DNS.2 = ldap
IP.1  = 127.0.0.1
  • Please Note: The subjectAltName (SAN) is important. Modern TLS ignores the Common Name.
  • The following command will create the sign request:
openssl req -new -key ldap.key -out ldap.csr -config ldap.cnf
  • The following command will use the sign request to create a certificate:
openssl x509 -req -in ldap.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out ldap.crt -days 825 -sha256 -copy_extensions copy
  • Now you have the following files:
    • ca.crt - The CA public file
    • ca.key - The CA private file
    • ldap.key - The LDAP private file
    • ldap.crt - The LDAP public file
  • Everything is now in place in terms of our PKI for us to configure slapd (OpenLDAP) to support ldaps.
  • Issue the following commands
sudo mkdir /etc/ldap/tls
#Copy the following files over
sudo cp ca.crt ldap.key ldap.crt /etc/ldap/tls
#Change their permissions
sudo chown openldap:openldap /etc/ldap/tls/*
sudo chmod 600 /etc/ldap/tls/ldap.key
sudo chmod 644 /etc/ldap/tls/*.crt
  • Configure slapd to use those files.
  • Create tls.ldif with the following contents
dn: cn=config
changetype: modify
replace: olcTLSCertificateFile
olcTLSCertificateFile: /etc/ldap/tls/ldap.crt
-
replace: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/ldap/tls/ldap.key
-
replace: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/ldap/tls/ca.crt
  • Apply it to the directory with the following commnand:
sudo ldapmodify -Y EXTERNAL -H ldapi:/// -f tls.ldif
  • Enable LDAPS by editing /etc/default/slapd.
  • Look for the following section and change accordingly:
# slapd normally serves ldap only on all TCP-ports 389. slapd can also
# service requests on TCP-port 636 (ldaps) and requests via unix
# sockets.
# Example usage:
# SLAPD_SERVICES="ldap://127.0.0.1:389/ ldaps:/// ldapi:///"
#SLAPD_SERVICES="ldap:/// ldapi:///"
SLAPD_SERVICES="ldap:/// ldapi:/// ldaps:///"
</code bash>
  * Restart **slapd**.
<code>
sudo systemctl restart slapd
  • Confirm that it is now listening on port 636 also:
sudo ss -lnpt | grep 636
LISTEN 0      2048         0.0.0.0:636        0.0.0.0:*    users:(("slapd",pid=698775,fd=11))                       
LISTEN 0      2048            [::]:636           [::]:*    users:(("slapd",pid=698775,fd=12))      
  • To make the cert's FQDN work smooth add it to the /etc/hosts file (ldap.radiusdesk.com in our case).
127.0.0.1 localhost
127.0.1.1 xubuntu-24-4
127.0.0.1 ldap.radiusdesk.com

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
  • Test the certificate with the following command:
openssl s_client -connect ldap.radiusdesk.com:636 -CAfile ca.crt

#IT should end with something like this:
---
SSL handshake has read 3905 bytes and written 401 bytes
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 4096 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
  • You can now test with the ldapsearch utility.

Important is to specify the CA cert (LDAPTLS_CACERT) that ldapsearch needs to use else if will fail.

LDAPTLS_CACERT=/etc/ldap/tls/ca.crt ldapsearch -H ldaps://ldap.radiusdesk.com -x -D "cn=admin,dc=radiusdesk,dc=com" -w testing123 -b dc=radiusdesk,dc=com
  • If you want this CA to be part of the list of trusted CA's on the system you can do the following:
sudo cp /etc/ldap/tls/ca.crt /usr/local/share/ca-certificates/radiusdesk-ca.crt
sudo update-ca-certificates
#This is the output on our system
Updating certificates in /etc/ssl/certs...
rehash: warning: skipping ca-certificates.crt,it does not contain exactly one certificate or CRL
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
Processing triggers for ca-certificates-java (20240118) ...
Adding debian:radiusdesk-ca.pem
done.
done.
#Now we can just use plain ldapsearch
ldapsearch -H ldaps://ldap.radiusdesk.com -x -D "cn=admin,dc=radiusdesk,dc=com" -w testing123 -b dc=radiusdesk,dc=com

I like to know how things work. If you also like to know how things work the following insert is for you :-)

  • The primary master CA file that is updated and can be used on Ubuntu is /etc/ssl/certs/ca-certificates.crt
  • This command rebuilds the system's trusted certificate store by consolidating certificates from a few key locations:
    • System Certificates: The command reads the configuration file /etc/ca-certificates.conf to determine which certificates from /usr/share/ca-certificates/ should be trusted.
    • Local Certificates: It automatically trusts all .crt files found in /usr/local/share/ca-certificates/. This is the place where we add our internal CA certificate.
  • Output: It then generates the single, concatenated bundle file at /etc/ssl/certs/ca-certificates.crt. This file is the master list used by many applications (like OpenSSL) to verify SSL/TLS connections.
  • Do the following steps.
sudo su
#Copy the OpenLDAP's CA to FreeRADIUS
cp /etc/ldap/tls/ca /etc/freeradius/3.0/certs/ca.crt
  • Edit /etc/freeradius/3.0/mods-enabled/ldap
#Look for these items...
#server = 'localhost'
#---This has to match the cert's  ubjectAltName (SAN)--- 
server = 'ldap.radiusdesk.com'
#       server = 'ldap.rrdns.example.org'
#       server = 'ldap.rrdns.example.org'
 
#  Port to connect on, defaults to 389, will be ignored for LDAP URIs.
##port = 389
#--We specify the ldaps port--
port = 636
 
#--- Then loop for the tls section
tls {
        # Set this to 'yes' to use TLS encrypted connections
        # to the LDAP database by using the StartTLS extended
        # operation.
        #
        # The StartTLS operation is supposed to be
        # used with normal ldap connections instead of
        # using ldaps (port 636) connections
 
        #--- Disable start_tls since we are forcing ssl/tls ---
 
        #start_tls = yes
 
#               ca_file = ${certdir}/cacert.pem
 
       #--- The CA file from OpenLDAP--
 
        ca_file = /etc/freeradius/3.0/certs/ca.crt
 
#               ca_path = ${certdir}
#               certificate_file = /path/to/radius.crt
#               private_key_file = /path/to/radius.key
#               random_file = /dev/urandom
 
        #  Certificate Verification requirements.  Can be:
        #    'never' (do not even bother trying)
        #    'allow' (try, but don't fail if the certificate
        #               cannot be verified)
        #    'demand' (fail if the certificate does not verify)
        #    'hard'  (similar to 'demand' but fails if TLS
        #             cannot negotiate)
        #
        #  The default is libldap's default, which varies based
        #  on the contents of ldap.conf.
 
        #---Enforce FreeRADIUS to check the validity of the certificate ---
 
        require_cert    = 'demand'
 
        #
        #  Check the CRL, as with the EAP module.
        #
  • After you made these changes, restart FreeRADIUS in debug mode and to a test authentication to confirm that it is now using ldaps (port 636)
  • See the results from out setup below:
rlm_ldap (ldap): Reserved connection (3)
(3) ldap: EXPAND (uid=%{%{Stripped-User-Name}:-%{User-Name}})
(3) ldap:    --> (uid=testuser)
(3) ldap: Performing search in "ou=people,dc=radiusdesk,dc=com" with filter "(uid=testuser)", scope "sub"
(3) ldap: Waiting for search result...
(3) ldap: User object found at DN "uid=testuser,ou=people,dc=radiusdesk,dc=com"
(3) ldap: Processing user attributes
(3) ldap: control:Password-With-Header += 'mysecretpassword'
rlm_ldap (ldap): Released connection (3)
Need more connections to reach 10 spares
rlm_ldap (ldap): Opening additional connection (7), 1 of 25 pending slots used
rlm_ldap (ldap): Connecting to ldap://ldap.radiusdesk.com:636
rlm_ldap (ldap): Waiting for bind result...
rlm_ldap (ldap): Bind successful
  • technical/fr-ldap.txt
  • Last modified: 2026/07/26 18:39
  • by system