Self-signed cer­tifi­cates and LDAPS (OpenL­DAP) in PHP (or python)

This is not about how to gen­er­ate a self-signed cer­tifi­cate, this is about how to con­fig­ure an ldap client to con­nect secure­ly to a ldap serv­er which has a self-signed certificate.

Recent­ly I was search­ing a lot how to make this kind of set­up work, but it seems nobody is using the key­words of the head­line in their HOW­TOs, or every­one is not real­ly set­ting up a real­ly secure con­nec­tion with self-signed cer­tifi­cates. As such here my try to doc­u­ment this for those which are inter­est­ed in a secure setup.

How OpenL­DAP is check­ing the cer­tifi­cates normally

OpenL­DAP is using the cer­tifi­cate store which is con­fig­ured for OpenSSL. So any cer­tifi­cate which is signed by one of the CAs in the OpenSSL cert-ctore are trusted.

Secure set­up

Most of the time you do not expose an LDAP serv­er to the out­side where a cer­tifi­cate from one of the trusted-by-default CAs is need­ed. A cer­tifi­cate from your inter­nal CA is enough, and in some cas­es a self-signed cer­tifi­cate is suf­fi­cient too.

An easy solu­tion could be to add either the root-certificate of your CA or the self-signed cer­tifi­cate into the trust-store of OpenSSL (not every OS / dis­tri­b­u­tion has this in the same loca­tion, you have to check where this is for your OS, for FreeB­SD 13+ this is /usr/local/etc/ssl/certs/, see also certctl(8) there). But this would mean you trust the cer­ti­ti­fa­cate which you put there addi­tion­al­ly to the default cer­tifi­cates (mod­u­lo any black­list­ing you made your­self). The­o­ret­i­cal­ly this means any­one who is able to get hold of a cer­tifi­cate from a public-CA for your LDAP serv­er, could per­form a man-in-the-middle attack (you need to con­sid­er your­self how fea­si­ble this is in your infra­struc­ture set­up and how like­ly this is to happen).

More secure operation

Let’s say you run a ser­vice which needs to be able to make TLS ses­sions to sys­tems which use cer­tifi­cates from pub­lic CAs and you want to make sure a con­nec­tion to the LDAP back­end can not use cer­tifi­cates from pub­lic CAs.

To tight­en the set­up in this case, you need to spec­i­fy that the client which uses OpenLDAP-client libraries is using a dif­fer­ent trust-store for the cer­tifcate validation.

For the openl­dap client util­i­ties there is a glob­al con­fig file for this (on FreeB­SD this is /usr/local/etc/openldap/ldap.conf). For oth­er tools, like PHP, this needs to be done in the per-user con­fig file ~/.ldaprc. Both file have the same syntax.

With php-ldap you nor­mal­ly run the ser­vice either in php-fpm or in an apache-php-module. In both cas­es the process which runs is con­fig­ured to run as a non-root user which may or may not have a home direc­to­ry (in FreeB­SD the www user which is typ­i­cal­ly used for that has no home directory).

HOWTO

  1. cre­ate a home directory
  2. cre­ate a sep­a­rate trust-store for LDAP
  3. con­fig­ure php-ldap / py-ldap to make use of the sep­a­rate trust-store

Step 1 – cre­ate a home directory

Chose a place which is suit­able, and cre­ate a direc­to­ry there. It does­n’t need to be in /home, it can be any­where. The impor­tant part is, that it is read­able by the user which runs the appli­ca­tion which is using php-ldap. It does not need to be writable by this user. In there you need to cre­ate the .ldaprc file (again, needs only be read­able by the user) with the con­tent from step 3.

Step 2 – cre­ate a sep­a­rate trust-store for LDAP

In FreeB­SD the glob­al ldap con­fig is in /usr/local/etc/openldap/ldap.conf. The­o­ret­i­cal­ly you can put the trust-store for LDAP in any place wou want. In my set­up I con­sid­er it to belong into /usr/local/etc/openldap/ssl/. So make a direc­to­ry – like /usr/local/etc/openldap/ssl – for the trust-store, and copy the cer­tifi­cate of the LDAP serv­er there.

Atten­tion! Only the pub­lic cer­tifi­cate, not the pri­vate key! If you only have one file on the serv­er for this, it is the com­bined key+certificate (if you don’t know or are able to deduct by look­ing 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 direc­to­ry and the cer­tifi­cate need to be acces­si­ble (read for the file, exe­cute for the direc­to­ry) by any user which shall make use of this. It does not hurt to have it acces­si­ble by every­one (you made sure there is not the private-key from the serv­er, right?).

Step 3 – con­fig­ure php-ldap / py-ldap to make use of the sep­a­rate trust-store

If you use php-fpm, you need to con­fig­ure a home direc­to­ry in the FPM pool con­fig­ure­ation sec­tion. As already said above, it does not need to be inside /home, but it dpends upon your needs. Here in this exam­ple let me use /home. The FPM con­fig line to add is then some­thing like:
env[HOME] = /home/php-fpm
You could achieve the same via chang­ing the home direc­to­ry in the pass­word data­base, but this would have an effect on all process­es run with this user, where­as here it is just for the php-fpm process­es (and childs).

If you use apache instead of php-fpm, you need to con­fig­ure some­thing sim­i­lar for the cor­re­spond­ing vir­tu­al host:
SetEnv HOME /home/php-fpm

With this you can now con­fig­ure /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 appli­ca­tion, you have to do some­thing sim­i­lar… if all else fails, it needs to be via a real home direc­to­ry in the pass­word 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 restart­ing php-fpm or apache, you should now be able to make real­ly secure con­nec­tions to the ldap server.

Some impor­tant things

  • Every time you change the cer­tifi­cate of the LDAP serv­er, you need to update the cer­ti­facte in the clients.
  • There are two TLS modes for the LDAP serv­er, one is “ldaps”, and one is “ldap+starttls”. If you have your LDAP serv­er run­ning in ldaps-mode (typ­i­cal­ly on port 639), you do not need to spec­i­fy in your php-ldap using appli­ca­tion to enable TLS (which is doing a start­tls after con­nect­ing… typ­i­cal­ly on port 389), but you need to spec­i­fy “ldaps://servername:639” (assum­ing it runs on port 639) instead of just “server­name” at the place in your appli­ca­tion where you are told to enter the serv­er name. For py-ldap I have checked just one appli­ca­tion (net­da­ta), and there TLS needs to be enabled, and the serv­er name has to be with­out “ldaps://” as net­da­ta is pre­fix­ing the “ldaps://” itself if tls is enabled.
  • Some places in the inter­net are telling to add “TLS_REQCERT nev­er” into ldap.conf / .ldaprc. Tech­ni­cal­ly this is not need­ed. Depend­ing on your point of view this can either be good or bad (spec­i­fy­ing it saves some CPU cycles on the serv­er and the client, and some trans­fer time over the net­work – not spec­i­fy­ing it allows to val­i­date the cer­tifi­cat­ed received to be com­pared to the cer­tifcate being avail­able local­ly, but I do not know if OpenL­DAP is doing this, nor did I spend some time to eval­u­ate if this improves secu­ri­ty (if the impor­tant parts of the cer­tifi­cate are out-of-sync, the con­nec­tion will fail)).

Lin­ux­u­la­tor explained: How to cre­ate Lin­ux bina­ries on FreeBSD

There may by cas­es where you want to gen­er­ate a Lin­ux bina­ry on a FreeB­SD machine. This is not a prob­lem with the lin­ux­u­la­tor, but not with the default linux_base port.

As you may know, the linux_base port is designed to deliv­er an inte­grat­ed expe­ri­ence with FreeB­SD native pro­grams. As such some parts of the native FreeB­SD infra­struc­ture is used. If you would try to use a Linux-compiler to gen­er­ate Linux-binaries, you would run into the prob­lem that by default the FreeB­SD includes are used.

Pre­req­ui­sites

To have a ful­ly fea­tured and non-integrated Lin­ux envi­ron­ment on your FreeB­SD sys­tem either mount an exist­ing (and com­pat­i­ble) Lin­ux instal­la­tion some­where into your FreeB­SD sys­tem, or install a linux_dist port. This can be done addi­tion­al­ly to an already installed linux_base port.

Prepa­ra­tion

When you have a com­plete Lin­ux envi­ron­ment avail­able, you need to mount the FreeB­SD devfs to /path/to/complete_linux/dev, lin­procfs to /path/to/complete_linux/proc and lin­sys­fs to /path/to/complete_linux/sys to have a com­plete setup.

Use it

Now you just need to chroot into this  /path/to/complete_linux and you configure/make/install or what­ev­er you need to do to gen­er­ate your desired Lin­ux binary.

A phoronix bench­mark cre­ates a huge bench­mark­ing discussion

The recent Phoronix bench­mark which com­pared a release can­di­date of FreeB­SD 9 with Ora­cle Lin­ux Serv­er 6.1 cre­at­ed a huge dis­cus­sion in the FreeB­SD mail­inglists. The rea­son was that some peo­ple think the num­bers pre­sent­ed there give a wrong pic­ture of FreeB­SD. Part­ly because not all bench­mark num­bers are pre­sent­ed in the most promi­nent page (as linked above), but only at a dif­fer­ent place. This gives the impres­sion that FreeB­SD is infe­ri­or in this bench­mark while it just puts the focus (for a rea­son, accord­ing to some peo­ple) on a dif­fer­ent part of the bench­mark (to be more spe­cif­ic, blog­bench is doing disk reads and writes in par­al­lel, FreeB­SD gives high­er pri­or­i­ty to writes than to reads, FreeB­SD 9 out­per­forms OLS 6.1 in the writes while OLS 6.1 shines with the reads, and only the reads are pre­sent­ed on the first page). Oth­er com­plaints are that it is told that the default install was used (in this case UFS as the FS), when it was not (ZFS as the FS).

The author of the Phoronix arti­cle par­tic­i­pat­ed in parts of the dis­cus­sion and asked for spe­cif­ic improve­ment sug­ges­tions. A FreeB­SD com­mit­ter seems to be already work­ing to get some issues resolved. What I do not like per­son­al­ly, is that the arti­cle is not updat­ed with a remark that some things pre­sent­ed do not reflect the real­i­ty and a retest is necessary.

As there was much talk in the thread but not much obvi­ous activ­i­ty from our side to resolve some issues, I start­ed to improve the FreeB­SD wiki page about bench­mark­ing so that we are able to point to it in case some­one wants to bench­mark FreeB­SD. Oth­ers already chimed in and improved some things too. It is far from per­fect, some more eyes – and more impor­tant­ly some more fin­gers which add con­tent – are need­ed. Please go to the wiki page and try to help out (if you are afraid to write some­thing in the wiki, please at least tell your sug­ges­tions on a FreeB­SD mail­inglist so that oth­ers can improve the wiki page).

What we need too, is a wiki page about FreeB­SD tun­ing (a first step would be to take the man-page and con­vert it into a wiki page, then to improve it, and then to feed back the changes to the man-page while keep­ing the wiki page to be able to cross ref­er­ence parts from the bench­mark­ing page).

I already told about this in the thread about the Phoronix bench­mark: every­one is wel­come to improve the sit­u­a­tion. Do not talk, write some­thing. No mat­ter if it is an improve­ment to the bench­mark­ing page, tun­ing advise, or a tool which inspects the sys­tem and sug­gests some tun­ing. If you want to help in the wiki, cre­ate a First­name­Last­name account and ask a FreeB­SD comit­ter for write access.

A while ago (IIRC we have to think in months or even years) there was some frame­work for auto­mat­ic FreeB­SD bench­mark­ing. Unfor­tu­nate­ly the author run out of time. The frame­work was able to install a FreeB­SD sys­tem on a machine, run some spec­i­fied bench­mark (not much bench­marks where inte­grat­ed), and then install anoth­er FreeB­SD ver­sion to run the same bench­mark, or to rein­stall the same ver­sion to run anoth­er bench­mark. IIRC there was also some DB behind which col­lect­ed the results and maybe there was even some way to com­pare them. It would be nice if some­one could get some time to talk with the author to get the frame­work and set it up some­where, so that we have a con­trolled envi­ron­ment where we can do our own bench­marks in an auto­mat­ic and repeat­able fash­ion with sev­er­al FreeB­SD versions.