Setting up Let’s Encrypt certificates for the 389-ds LDAP server

In the past months I’ve set up LDAP at home, to avoid having different user accounts for the services that I run on my home hardware. Rather than the venerable OpenLDAP I settled for 389 Directory Server, commercially known as Red Hat Directory Server, mainly because I was more familiar with it. Rather than describing how to set that up (Red Hat’s own documentation is excellent on that regard), this post will focus on the steps required to enable encryption using Let’s Encrypt certificates.

The problem

Even the LDAP server (they’re actually two, but for the purpose of this post it does not matter) was just operating in my LAN, I wanted to reduce the amount of information going around in clear, including LDAP queries. That meant setting up encryption, of course.

The problem was that Red Hat’s docs only cover the “traditional” way of obtaining certificates, that is obtaining a Certificate Authority (CA) certificate, request a server certificate, obtain it and set it up in the server. There is (obviously) no mention of Let’s Encrypt anywhere. I’ve found some guides, but they were either too complicated (lots of fuzzing around with certutil) or they weren’t clear for some steps. Hence, this post.

NOTE: I’ve focused on 389-ds version 2.0 and above, which has a different CLI set of commands than the venerable 1.3 series. All of the steps shown here can also be carried out via the Cockpit Web interface as well, if your distribution carries it (spoiler: openSUSE doesn’t).

Importing the CA

This is arguably one of the most important steps of the process. 389-ds needs also to store the CA for your certificates. As you may (or may not) know, Let’s Encrypt’s CA certificates are two:

  • The actual “root” certificate, by Internet Security Research Group (ISRG);
  • An intermediate certificate, signed by the above root, called “R3”.

Let’s Encrypt certificates, like the one powering this website, are signed by R3. But since you have to follow a “chain of trust”, to validate the certificate you follow these steps (excuse me, security people; this is probably a bad semplification):

  1. Check the actual certificate (e.g. the one on dennogumi.org)
  2. The certificate is signed by “R3”, so move up the chain and check the R3 certificate
  3. The R3 certificate is signed by the ISRG root certificate, so move up the chain and check the ISRG root
  4. The ISRG certificate is trusted by the OS / application using it, so everything stops there.

If any of the steps fails, the whole validation fails.

This long winding explanation is to tell you that 389-ds needs the whole certificate chain for its CA (so ISRG root + R3) in order to properly validate the Let’s Encrypt certificate you’ll use. If you don’t do that, chances are that some software which uses the system CA will work (for example ldapsearch) but others, like SSSD will fail with “Unknown CA” errors (buried deep into debug logs, so at the practical level they’ll just fail and you won’t know why).

Let’s get to business. Access the Chain of Trust page for Let’s Encrypt and download the relevant certificates. I’m not sure if 389-ds supports ECDSA certificates, so I downloaded the RSA ones: ISRG Root X1 and Let’s Encrypt R3 (both in PEM format). Put them somewhere in your server. Then, as root, import the two CA certificates into 389-ds (substitute LDAP_ADDRESS with the LDAP URI of your server):

# ISRG Root

dsconf -v -D "cn=Directory Manager"  LDAP_ADDRESS security ca-certificate 
    add --file /path/to/certificate --name "ISRG"

# Let's Encrypt R3

dsconf -v -D "cn=Directory Manager"  LDAP_ADDRESS security ca-certificate 
    add --file /path/to/certificate --name "R3"

NOTE: This step may not be necessary if you use Let’s Encrypt’s “full chain” certificates, but I did not test that.

Importing the certificates

Then, you have to import a Let’s Encrypt certificate, which means you have to obtain one. There are hundreds of guides and clients that can do the job nicely, so I won’t cover that part. If you use certbot, Let’s Encrypt’s official client, you will have the certificate and the private key for it in /etc/letsencrypt/live/YOURDOMAIN/fullchain.pem and /etc/letsencrypt/live/YOURDOMAIN/privkey.pem.

You need to import the private key first (substitute LDAP_ADDRESS and DOMAIN with the LDAP URI of your server and the Let’s Encrypt domain, respectively):

dsctl LDAP_ADDRESS tls import-server-key-cert 
    /etc/letsencrypt/live/DOMAIN/fullchain.pem 
        /etc/letsencrypt/live/DOMAIN/privkey.pem

Note that you pass the certificate as well as the key to import it (in doubt, check’s Red Hat documentation).

Once the key is done, it is time to import the actual certificate:

dsconf -v -D "cn=Directory Manager" LDAP_ADDRESS security certificate add 
    --file /etc/letsencrypt/live/DOMAIN/fullchain.pem 
        --primary-cert 
        --name "LE"

--primary-cert sets the certificate as the server’s primary certificate.

Then, we switch on TLS in the server:

dsconf -v -D "cn=Directory Manager" LDAP_ADDRESS config replace 
	nsslapd-securePort=636 nsslapd-security=on

And finally, we restart our instance (replace INSTANCE with your configured instance name):

systemctl restart dirsrv@INSTANCE

Testing everything out

You can use ldapsearch to check whether the SSL connection is OK (I’ve used Directory Manager, but you can use any user you want):


# STARTTLS

ldapsearch -H ldap://your.ldap.hostname -W -x -D "cn=Directory Manager" -ZZ "search filter here"

# TLS

ldapsearch -H ldaps://your.ldap.hostname -W -x -D "cn=Directory Manager" "search filter here"

If everything is OK, you should get a result: otherwise, you’ll get an error like “Can’t contact the LDAP server”.

Alternatively, you can use openssl:

openssl s_client -connect your.ldap.hostname:636
[...]
---
SSL handshake has read 5188 bytes and written 438 bytes
Verification: OK

Don’t forget to adjust your applications to connect via ldaps rather than ldap after everything is done.

Renewing the certificates

To renew the certificates you repeat the steps outlined above for the certificates (without the CA part, of course). Make sure you always import your private key: if there is a mismatch between it and the certificate, 389-ds will refuse to start.

If you use certbot you can use a post-renewal hook to trigger the import of the certificate into 389-ds. This is what I’ve been using: bear in mind it’s customized to my setup and does a few more things than needed. Also it does only import the certificate, and not the full chain.

I messed up and 389-ds won’t start! What do I do?

You can disable encryption by editing /etc/dirsrv/slapd-INSTANCE/dse.ldif and changing nsslapd-security to off, then start 389-ds again. Then you can review everything and see what went wrong. But if you can, I recommend the Cockpit Web UI: it makes the first-time setup a breeze.

Wrap up

Importing the certificates is surprisingly simple, but my Internet searches have been frustrating because at least half of what I found was either not applicable, incomplete, or did not work. I hope this small tutorial can be useful for those who want a bit more security in their LDAP setup.

OpenSUSE Planet