----- ====== FreeRADIUS with LDAP ====== ===== Introduction ===== * 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 ====== ===== OpenLDAP ===== ==== Install OpenLDAP ==== * 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. * OpenLDAP server is called **slapd**. * The program to extract info from the OpenLDAP directory is called **slapcat**. # 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 ==== * 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. ==== Check OpenLDAP ==== * 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 the Organizational Unit (OU) ==== * Create a file named ou.ldif to set up a dedicated container for your network accounts. 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 the User Account ==== * Create a file named user.ldif. * This template creates a user named testuser with the password mysecretpassword. 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. * ===== FreeRADIUS + LDAP ===== * 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) * If you have installed RADIUSdesk on the host it will have a modified FreeRADIUS config. * You can however activate the default config again with the following steps sudo su service freeradius stop cd /etc mv /etc/freeradius /etc/freeradius.rd cp -r /etc/freeradius.orig /etc/freeradius chown -R freerad:freerad /etc/freeradius ==== Enable the FreeRADIUS LDAP Module ==== * 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 . ==== Map FreeRADIUS to OpenLDAP ==== * 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}})" } } ==== Check the Default Virtual Server ==== * 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 it out ==== * 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 PKI ===== ==== Create the CA ==== * 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. ==== Generate the LDAP server key ==== * Issue the following command: openssl genpkey -algorithm RSA -out ldap.key -pkeyopt rsa_keygen_bits:4096 ==== Create an OpenSSL configuration ==== * 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. ==== Create the CSR ==== * The following command will create the sign request: openssl req -new -key ldap.key -out ldap.csr -config ldap.cnf ==== Sign the request ==== * 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. ===== Configure OpenLDAP for 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:///" * Restart **slapd**. 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 ==== 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 :-) * 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. ==== Update FreeRADIUS for ldaps:// ==== * 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