Our LDAP clients previously connected to a single IP address (ldap.restek.wwu.edu) which was a virtual address (using CARP) shared by two LDAP servers in a master/slave setup with replication. The slave of this pair was rarely used because the master rarely went down. CARP, therefore, created host-level redundancy for our LDAP directory but not service-level. The other host would have to lose network connectivity entirely for the slave to be used.
The load on the server wasn’t enough to justify load-balancing, but the setup had obvious problems. For simplicity, the two shared the same SSL certificate/keys with just one CN (ldap.restek.wwu.edu) and no subjectAltNames to make connecting to other hostnames (i.e., directly to the slave) over SSL impossible. Another problem was that when you needed to restart the OpenLDAP daemon for an upgrade on the master, it would be unable to look things up. This required workarounds like adjusting CARP settings temporarily during upgrades. Anyway, the setup was problematic and clearly not effective long-term.
Our certificates expired a day or two ago, and I decided to use the opportunity to fix the setup.
1. New DNS names
Rather than having only one name, I decided to create two more aliases for the LDAP servers: ldap1, and ldap2. These were simply CNAMEs for our master and slave LDAP server, respectively. This follows the convention that I used for nameserver naming: ns1, ns2, ns3, etc.
2. New certificates
I also created new and unique self-signed certificates for each LDAP server. I created certificates with information like the following:
Subject:
C=US, ST=Washington, L=Bellingham, O=Western Washington University, OU=ResTek,
CN=vali.restek.wwu.edu/emailAddress=admin@restek.wwu.edu
X509v3 Subject Alternative Name:
DNS:vali.restek.wwu.edu, DNS:ldap1.restek.wwu.edu, DNS:ldap.restek.wwu.edu
The Subject Alternative Name (subjectAltName) piece, described in RFC 3280, is to ensure that people can securely connect to any of those three names without warnings or errors about the names not matching. I believe it is possible to also use wildcard certificates, but I prefer this. Wildcards are ambiguous
I added the primary hostname to subjectAltName because some reading indicated that a client might prefer alternative names over subject CN. It may also be possible to leave CN blank in the case where you use subjectAltName, but I’m not sure how older clients handle this.
I installed these certificates on the clients (all FreeBSD servers) into /etc/ssl/certs with descriptive filenames.
vali-restek-wwu-edu-ldap-20081223163515.crt
vidar-restek-wwu-edu-ldap-20081223173447.crt
These “2008″ numbers are simply the issue dates of the certificates. I wasn’t sure whether I wanted to use issue date, expiration date, neither, or what. But I ended up sticking with issue date for now. The main reason behind this was to allow a new administrator to install new certificates without renaming the old ones.
If you’re at all familiar with SSL configuration, you may know that most applications allow you to specify a path to a directory of certificates rather than a filename. This is most often used for CA certificates which you trust. How are these located when you need them? It seems that OpenSSL expects them to be located according to their hash. Rather than rename them, I created links to the actual certificates using commands like the following:
$ openssl x509 -hash -noout -in vali-restek-wwu-edu-ldap-20081223163515.crt
51afeb96
$ ln -s vali-restek-wwu-edu-ldap-20081223163515.crt 51afeb96.0
The extra digit (i.e. 0) appended to the end of the symbolic link name is used in case of multiple certificates with the same hash value.
3. pam_ldap, nss_ldap configuration
I won’t go over the entire configurations I have – just what I changed. First, I changed the URI to include both hosts:
uri ldap://ldap1.restek.wwu.edu/ ldap://ldap2.restek.wwu.edu/
The second will be tried if the first fails, according to the documentation and my tests. Obviously this is much better than a single CARP’ed IP address.
I also changed the certificate location from a single file to a directory:
tls_cacertdir /etc/ssl/certs
This is where the symlinks come into play. Notice I did not have to specify the actual filenames — just the directory where certificates are located.
4. Postfix
The manual page ldap_table(5) was extremely helpful (it described the necessity of the hash symlinks shown above in addition to exactly what Postfix needed). Relevant parts of my Postfix LDAP configuration file (ldap.cf):
server_host = ldap://ldap1.restek.wwu.edu ldap://ldap2.restek.wwu.edu
start_tls = yes
tls_ca_cert_dir = /etc/ssl/certs/
tls_require_cert = yes
5. Dovecot
Dovecot documentation wasn’t as clear. I ended up looking at the source and discovered that it does support options similar to Postfix, but they aren’t in the wiki to my knowledge. They are, however, documented in the example LDAP configuration included with the distribution.
uris = ldap://ldap1.restek.wwu.edu ldap://ldap2.restek.wwu.edu
tls = yes
tls_ca_cert_dir = /etc/ssl/certs
tls_require_cert = demand
6. Apache (mod_authnz_ldap)
Not much needed to change in the Apache configuration, but one thing did require testing. The syntax to specify multiple hosts is as follows:
AuthLDAPURL "ldap://ldap1.restek.wwu.edu ldap2.restek.wwu.edu/ou=People,dc=restek,dc=wwu,dc=edu?uid?" TLS
I’m still trying to figure out how to configure Apache to trust certain certificates.
In order to make this easy to maintain in the future, I may end up setting up a local CA or using a free provider. This way, the clients won’t need to be reconfigured when the certificates change.