This is not about how to generate a self-signed certificate, this is about how to configure an ldap client to connect securely to a ldap server which has a self-signed certificate.
Recently I was searching a lot how to make this kind of setup work, but it seems nobody is using the keywords of the headline in their HOWTOs, or everyone is not really setting up a really secure connection with self-signed certificates. As such here my try to document this for those which are interested in a secure setup.
How OpenLDAP is checking the certificates normally
OpenLDAP is using the certificate store which is configured for OpenSSL. So any certificate which is signed by one of the CAs in the OpenSSL cert-ctore are trusted.
Secure setup
Most of the time you do not expose an LDAP server to the outside where a certificate from one of the trusted-by-default CAs is needed. A certificate from your internal CA is enough, and in some cases a self-signed certificate is sufficient too.
An easy solution could be to add either the root-certificate of your CA or the self-signed certificate into the trust-store of OpenSSL (not every OS / distribution has this in the same location, you have to check where this is for your OS, for FreeBSD 13+ this is /usr/local/etc/ssl/certs/
, see also certctl(8) there). But this would mean you trust the certitifacate which you put there additionally to the default certificates (modulo any blacklisting you made yourself). Theoretically this means anyone who is able to get hold of a certificate from a public-CA for your LDAP server, could perform a man-in-the-middle attack (you need to consider yourself how feasible this is in your infrastructure setup and how likely this is to happen).
More secure operation
Let’s say you run a service which needs to be able to make TLS sessions to systems which use certificates from public CAs and you want to make sure a connection to the LDAP backend can not use certificates from public CAs.
To tighten the setup in this case, you need to specify that the client which uses OpenLDAP-client libraries is using a different trust-store for the certifcate validation.
For the openldap client utilities there is a global config file for this (on FreeBSD this is /usr/local/etc/openldap/ldap.conf
). For other tools, like PHP, this needs to be done in the per-user config file ~/.ldaprc
. Both file have the same syntax.
With php-ldap you normally run the service either in php-fpm or in an apache-php-module. In both cases the process which runs is configured to run as a non-root user which may or may not have a home directory (in FreeBSD the www user which is typically used for that has no home directory).
HOWTO
- create a home directory
- create a separate trust-store for LDAP
- configure php-ldap / py-ldap to make use of the separate trust-store
Step 1 – create a home directory
Chose a place which is suitable, and create a directory there. It doesn’t need to be in /home, it can be anywhere. The important part is, that it is readable by the user which runs the application which is using php-ldap. It does not need to be writable by this user. In there you need to create the .ldaprc file (again, needs only be readable by the user) with the content from step 3.
Step 2 – create a separate trust-store for LDAP
In FreeBSD the global ldap config is in /usr/local/etc/openldap/ldap.conf
. Theoretically you can put the trust-store for LDAP in any place wou want. In my setup I consider it to belong into /usr/local/etc/openldap/ssl/
. So make a directory – like /usr/local/etc/openldap/ssl
– for the trust-store, and copy the certificate of the LDAP server there.
Attention! Only the public certificate, not the private key! If you only have one file on the server for this, it is the combined key+certificate (if you don’t know or are able to deduct by looking into the file how to get rid of the key… there is a lot of info out there in the WWW which explains it). The directory and the certificate need to be accessible (read for the file, execute for the directory) by any user which shall make use of this. It does not hurt to have it accessible by everyone (you made sure there is not the private-key from the server, right?).
Step 3 – configure php-ldap / py-ldap to make use of the separate trust-store
If you use php-fpm, you need to configure a home directory in the FPM pool configureation section. As already said above, it does not need to be inside /home, but it dpends upon your needs. Here in this example let me use /home. The FPM config line to add is then something like:env[HOME] = /home/php-fpm
You could achieve the same via changing the home directory in the password database, but this would have an effect on all processes run with this user, whereas here it is just for the php-fpm processes (and childs).
If you use apache instead of php-fpm, you need to configure something similar for the corresponding virtual host:SetEnv HOME /home/php-fpm
With this you can now configure /home/php-fpm/.ldaprc
to point to the LDAP trust-store:TLS_CACERT /usr/local/etc/openldap/ssl/ldap_server_cert.pem
TLS_CACERTDIR /usr/local/etc/openldap/ssl
If you use some python based application, you have to do something similar… if all else fails, it needs to be via a real home directory in the password database.
If you want to use the ldap client tools with any user, you need to add those lines to the /usr/local/etc/openldap/ldap.conf
file too (there you can also set the default BASE – e.g. “BASE dc=example,dc=com
” – and URI – e.g. “URI ldaps://ldap.example.com:639
″).
After restarting php-fpm or apache, you should now be able to make really secure connections to the ldap server.
Some important things
- Every time you change the certificate of the LDAP server, you need to update the certifacte in the clients.
- There are two TLS modes for the LDAP server, one is “ldaps”, and one is “ldap+starttls”. If you have your LDAP server running in ldaps-mode (typically on port 639), you do not need to specify in your php-ldap using application to enable TLS (which is doing a starttls after connecting… typically on port 389), but you need to specify “ldaps://servername:639” (assuming it runs on port 639) instead of just “servername” at the place in your application where you are told to enter the server name. For py-ldap I have checked just one application (netdata), and there TLS needs to be enabled, and the server name has to be without “ldaps://” as netdata is prefixing the “ldaps://” itself if tls is enabled.
- Some places in the internet are telling to add “TLS_REQCERT never” into ldap.conf / .ldaprc. Technically this is not needed. Depending on your point of view this can either be good or bad (specifying it saves some CPU cycles on the server and the client, and some transfer time over the network – not specifying it allows to validate the certificated received to be compared to the certifcate being available locally, but I do not know if OpenLDAP is doing this, nor did I spend some time to evaluate if this improves security (if the important parts of the certificate are out-of-sync, the connection will fail)).