Table of Contents


FreeRADIUS with LDAP

Introduction

Basic FreeRADIUS with LDAP

OpenLDAP

Install OpenLDAP

# 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

Configure OpenLDAP

# Configure or reconfigure slapd defaults interactively
sudo dpkg-reconfigure slapd

Check OpenLDAP

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

Create the Organizational Unit (OU)

ou.ldif
dn: ou=people,dc=radiusdesk,dc=com
objectClass: organizationalUnit
ou: people
ldapadd -x -D "cn=admin,dc=radiusdesk,dc=com" -W -f ou.ldif

Create the User Account

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
ldapadd -x -D "cn=admin,dc=radiusdesk,dc=com" -W -f user.ldif
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

FreeRADIUS + LDAP

Enable the FreeRADIUS LDAP Module

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 .

Map FreeRADIUS to OpenLDAP

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}})"
    }
}

Check the Default Virtual Server

#
#  The ldap module reads passwords from the LDAP database.
-ldap

Test it out

sudo systemctl stop freeradius
sudo freeradius -X
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
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

FreeRADIUS with Secure LDAP

Create PKI

Create the CA

mkdir -p ~/pki
cd ~/pki
openssl genpkey -algorithm RSA -out ca.key -pkeyopt rsa_keygen_bits:4096
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

Generate the LDAP server key

openssl genpkey -algorithm RSA -out ldap.key -pkeyopt rsa_keygen_bits:4096

Create an OpenSSL configuration

[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

Create the CSR

openssl req -new -key ldap.key -out ldap.csr -config ldap.cnf

Sign the request

openssl x509 -req -in ldap.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out ldap.crt -days 825 -sha256 -copy_extensions copy

Configure OpenLDAP for ldaps

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
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
sudo ldapmodify -Y EXTERNAL -H ldapi:/// -f tls.ldif
# 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
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))      
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
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)

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

What does update-ca-certificates do?

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

Update FreeRADIUS for ldaps://

sudo su
#Copy the OpenLDAP's CA to FreeRADIUS
cp /etc/ldap/tls/ca /etc/freeradius/3.0/certs/ca.crt
#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.
        #
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