Kerberos is a great protocol for single sign-on authentication. It's supported by many protocols, allowing you to not have to enter a password to each and every one of them; instead, the protocols behind the scenes (not "just" Kerberos, but also the things that embed tickets, such as GSSAPI, or the things that embed GSSAPI, such as SASL or SPNEGO) use your ticket-granting-ticket to ask for security credentials for the service you're trying to use, magic happens, and you're authenticated. I blogged about kerberos before (even if it was ages ago); since then, I've not only used it on my own systems, but also on the systems of various customers.

One thing I've learned in that time, however, is that most web application developers have a bad case of NIHilism when it comes to authentication. Most webservers that I've seen have a wide range of methods to do authentication in the webserver through various means, including things like certificate-based authentication, one-time password modules, and, yes, kerberos. Yet almost no webapp out there will look at the magic variables that those webservers set to explain we're authenticated, instead reinventing the wheel through webforms and other various stupid means. Sigh.

So, that means, no kerberos authentication for webapps. Worse, if the application has no way to pass on authentication to something external, that means users will now have to learn another password: one for Kerberos, one for the webapp. And, probably, one for this other webapp, too -- because once you add one webapp, people expect you to add more of them.

Well, mostly. In some cases, webapps do have ways to externalize authentication. In most cases this means "store passwords in a database", or "try authenticating against this other service here".

When "this other service here" is an IMAP server, then all you need to do is make sure cleartext authentication on the IMAP server eventually ends up trying to authenticate against the Kerberos server, and you're all set. When "this other service here" is an LDAP server, however, you're out of luck. Right?

It turns out that no, you're not. I recently learned that OpenLDAP can, in fact, check "simple" bind requests by checking some other service, and that this other service can be a Kerberos realm. Doing so is called "Pass-through authentication" in the OpenLDAP documentation, and this is how you do it:

  • Add a "userPassword" attribute to the user who will authenticate, with a value of the form "{SASL}principal@KERBEROS.REALM". That is, take the user's principal name, tack "{SASL}" in front of it, and put that in the userPassword attribute.
  • Make sure you have saslauthd installed and running, with the kerberos5 mechanism active. In Debian, that means you have to install sasl2-bin, and edit /etc/default/saslauthd, so it has the variable START set to yes (yuck), and the variable MECHANISMS set to kerberos5.
  • Create a file /etc/ldap/sasl/slapd.conf, and add the following contents:

mech_list: plain
pwcheck_method: saslauthd

This directs the SASL libraries, loaded by slapd, to talk to saslauthd when trying to authenticate some SASL things - Make sure slapd has the correct permissions to access the saslauthd unix domain socket. On Debian, that means you need to add the "openldap" user to the "sasl" group:

`adduser openldap sasl`

(obviously that won't be active until the next slapd restart)

And that's it; if you now try a simple bind against the LDAP directory, and enter your Kerberos password, you should be in. If it doesn't work, try running "testsaslauthd"; if that works, it means the error is in your slapd configuration. If it doesn't, then the problem is in saslauthd.

Some notes:

  • this is not the same thing as using Kerberos authentication for LDAP. OpenLDAP has the ability to allow SASL binds for LDAP, which is way more interesting (since it allows authentication to actually be done by Kerberos rather than by "entering a password"); instead, with this method, it is possible to verify a password for a simple bind against a Kerberos password.
  • Since this can't use hashed passwords, this method is inherently insecure. Only use it if all else fails, and for the love of $DEITY, at least make sure all the network connections which are going to contain such passwords are encrypted with SSL or TLS or similar. Otherwise, everyone who can sniff anything on your network will learn all the Kerberos passwords, which is a very very bad thing.

Oh, and if you're a webapp developer: please please please make it easy for me to use an external authentication mechanism. This isn't hard; all you need is a separate page that will read the magic variables (probably REMOTE_USER if you're using apache), set a session variable or whatever, and then redirect to your normal "we've just logged in" page. ikiwiki gets this right; you can too!

(for added bonus points, have some way to declare a mapping from REMOTE_USER values to internal, readable, usernames, but it's not too crucial)