Encrypt

Clients expect to see a certificate when connecting. The default Debian Snakeoil certificate causes some clients to show a red warning message (as it should).

Back in the bad old days, you had to purchase a ‘Unified Communication’ cert at a premium. But now you can use the free Let’s Encrypt project. It includes the EKU (Extended Key Usage) attribute “Server Authentication” which is required.

It can secure the inter-server communication channel as well.

Get the Certificate

There are a couple ways to prove yourself to Let’s Encrypt, but the simplest is let them connect to you at the DNS name you are requesting.

Configure a Reverse Proxy

Create the public DNS CNAME “wifi.example.org” for your hopefully-already-existing reverse proxy server. Forward port 80 to your RADIUS server for later use.

# HAProxy Example
frontend http
    bind *:80
    mode http

    acl wifi hdr(host) wifi.example.org
    use_backend wifi if wifi

backend wifi
    mode http
    server wifi radius.private.lan:80


# Caddy Example
http://wifi.example.org {
        import logging
        reverse_proxy * radius.private.lan
}

Install Certbot

The EFF makes a handy utility called certbot that will do the hard work for us. Though one hesitates to allow the internet in, certbot only listens for a few seconds every two months.

apt install certbot

certbot certonly --standalone -d wifi.example.org

ls /etc/letsencrypt/live/wifi.example.org/

Set Permissions

The cert files start as root-only but any changes are preserved.

# Create a certs group (if it doesn't already exist) and add the processes to it.
sudo addgroup certs
sudo chgrp -R certs /etc/letsencrypt/live /etc/letsencrypt/archive
sudo chmod 750 /etc/letsencrypt/live /etc/letsencrypt/archive

Configure FreeRADIUS

Add Group to FreeRADIUS

Grant the freerad process access to the certs

sudo adduser freerad certs

Change the EAP config

vi /etc/freeradius/3.0/mods-available/eap
...
...
        tls-config tls-common {
                private_key_file = /etc/letsencrypt/live/wifi.example.org/privkey.pem
                #private_key_file = /etc/ssl/private/ssl-cert-snakeoil.key
                ...
                ...
                certificate_file = /etc/letsencrypt/live/wifi.example.org/fullchain.pem
                #certificate_file = /etc/ssl/certs/ssl-cert-snakeoil.pem
systemctl restart freeradius

Test with a client

The best way to test is to grab an IOS device or Apple Laptop. These will display the Cert when you connect.

Add a RADIUS Trigger

FreeRADIUS won’t know when a cert gets updated, so you’ll need to trigger a restart (a reload isn’t enough) with a certbot hook.

sudo touch /etc/letsencrypt/renewal-hooks/post/radius
sudo chmod +x /etc/letsencrypt/renewal-hooks/post/radius
sudo vi /etc/letsencrypt/renewal-hooks/post/radius
#!/bin/sh
logger certbot renewel triggered restart of freerad
systemctl restart freeradius.service

Configure LDAP

In this example everything is running on one host. But if the data was traversing the network you’d encrypt it. The modern way is to enable and enforce TLS using the certificate we just got. In fact, RFC4513 says servers should disallow the use of passwords when TLS is not in use, so let’s get on this.

Configure SLAPD

First, let’s grant OpenLDAP access to the key.

sudo adduser openldap certs

Then, tell slapd about the cert by creating a configuration file in LDIF format and loading it.

vi cert.ldif
dn: cn=config
changetype: modify
replace: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/letsencrypt/live/wifi.example.org/privkey.pem
-
replace: olcTLSCertificateFile
olcTLSCertificateFile: /etc/letsencrypt/live/wifi.example.org/cert.pem
ldapmodify -Y EXTERNAL -H ldapi:/// -f cert.ldif

Test TLS is working with the ZZ option. (A single Z says means continue if negotiation fails, ZZ means stop if failed). Also use LDAPTLS_REQCERT=never. This allows us to verify that a cert is in place (ZZ) but don’t bother testing the validity (never).

# Use 'LDAPTLS_REQCERT=never' to trust any cert we get, but don't check the chain root yet
LDAPTLS_REQCERT=never ldapsearch -x -LLL -W -ZZ -D cn=admin,dc=example,dc=org -b dc=example,dc=org

There’s also the gold standard, openssl.

openssl s_client -starttls ldap -connect localhost:389

# Or a one-liner to decode as well
echo | openssl s_client -starttls ldap -connect localhost:389 | openssl x509 -text

Add a Hook

Just like before, we need to tel slapd about the new cert.

sudo touch /etc/letsencrypt/renewal-hooks/post/ldap
sudo chmod +x /etc/letsencrypt/renewal-hooks/post/ldap
sudo vi /etc/letsencrypt/renewal-hooks/post/ldap
#!/bin/bash
logger certbot renewel triggered restart of openldap
systemctl restart slapd.service

Join Them Together

RADIUS TLS

We can now tell RADIUS to use TLS with LDAP. Use the ‘require_cert’ attribute to ignore the trust chain until later.

vi /etc/freeradius/3.0/mods-available/ldap
        tls {
                ...
                ...
               start_tls = yes
               ...
               ...
               require_cert    = 'never'
systemctl restart freeradius
# You can start in dubug mode like before to troubleshoot if needed

Next Steps

You may have noticed that are using certs now, but the processes themselves don’t care if they are valid. Just that we have one. We also haven’t done much with LDAP security. Let’s do that next.

secure


Last modified July 31, 2025: nac additions (3334349)