OpenLDAP Authentication via Active Directory

I couldn't find this clearly documented anywhere. There's plenty out there on using OpenLDAP with a legitimate Kerberos system, but not AD. It took me longer than I'm going to admit to get it working1, but it works, so I figured I'd share with the world.

This configuration will allow you to do two things:

  1. Get a ticket from AD with kinit, and have OpenLDAP honor it
  2. Use an AD username and password for OpenLDAP's "simple authentication"

The Active Directory Side

You'll need two keytabs for each LDAP server you have. These are generated in AD. The easiest and cleanest way to get them is to "join" the system to Active Directory. For my environment, there's no need to make this a long term relationship. "Just give me the keytabs and get out. Your money's on the dresser." So I have a minimal configuration file for Samba to take care of this.

[global]
  workgroup = EMPLOYER
  security = ads
  realm = EMPLOYER.COM
  use kerberos keytab = yes

Save that to something like join_ad.conf, then use it to add the server. (This obviously needs to be done by someone with the right level of access in AD.)

% net ads join -s join_ad.conf -U <ad.username>
ad.username's password:

If it worked, you should now have a valid keytab at /etc/krb5.keytab. You can verify with klist:

% klist -k
Keytab name: FILE:/etc/krb5.keytab
KVNO Principal
---- --------------------------------------------------------------------------
   2 host/ldap01.employer.com@EMPLOYER.COM
   2 host/ldap01.employer.com@EMPLOYER.COM
   2 host/ldap01.employer.com@EMPLOYER.COM
   2 host/ldap01@EMPLOYER.COM
   2 host/ldap01@EMPLOYER.COM
   2 host/ldap01@EMPLOYER.COM
   2 LDAP01$@EMPLOYER.COM
   2 LDAP01$@EMPLOYER.COM
   2 LDAP01$@EMPLOYER.COM

Yes, I'm afraid AD adds 9 entries.

Next, you'll need to generate the keytab entries for the LDAP service. (Service names are case-sensitive, and for this particular service, it should be lower-case.)

% net ads keytab add ldap -s join_ad.conf -U <ad.username>
ad.username's password:

This will add to the list in /etc/krb5.keytab, but for security purposes, you should separate them at this point. Start by running ktutil. Follow these steps to create a new file for slapd.

  1. Read in the keytab with rkt /etc/krb5.keytab
  2. If you run list at this point, you'll see the contents:

    ktutil:  list
    slot KVNO Principal
    ---- ---- --------------------------------------
       1    2  host/ldap01.employer.com@EMPLOYER.COM
       2    2  host/ldap01.employer.com@EMPLOYER.COM
       3    2  host/ldap01.employer.com@EMPLOYER.COM
       4    2               host/ldap01@EMPLOYER.COM
       5    2               host/ldap01@EMPLOYER.COM
       6    2               host/ldap01@EMPLOYER.COM
       7    2                   LDAP01$@EMPLOYER.COM
       8    2                   LDAP01$@EMPLOYER.COM
       9    2                   LDAP01$@EMPLOYER.COM
      10    2  ldap/ldap01.employer.com@EMPLOYER.COM
      11    2  ldap/ldap01.employer.com@EMPLOYER.COM
      12    2  ldap/ldap01.employer.com@EMPLOYER.COM
      13    2               ldap/ldap01@EMPLOYER.COM
      14    2               ldap/ldap01@EMPLOYER.COM
      15    2               ldap/ldap01@EMPLOYER.COM
    
  3. We want to remove the first 9 (everything but the "ldap" entries). Running delent 1 nine times will pull the entries off the top of the list. Changes we make here only happen in memory. The original file will not be modified.

  4. Confirm the results with list:

    ktutil:  list
    slot KVNO Principal
    ---- ---- --------------------------------------
       1    2  ldap/ldap01.employer.com@EMPLOYER.COM
       2    2  ldap/ldap01.employer.com@EMPLOYER.COM
       3    2  ldap/ldap01.employer.com@EMPLOYER.COM
       4    2               ldap/ldap01@EMPLOYER.COM
       5    2               ldap/ldap01@EMPLOYER.COM
       6    2               ldap/ldap01@EMPLOYER.COM
    
  5. Write these to a new file with wkt /etc/openldap/slapd.keytab

  6. Enter quit

This leaves /etc/krb5.keytab untouched. If you want to remove the LDAP service entries from that file, you can use ktutil, but leaving them there won't prevent anything from working.

Name Resolution

Before I go any further, let me tell you that this fucking matters. Make sure the A and PTR records for your LDAP server are correct and make sure the hostname and/or FQDN don't appear in /etc/hosts.

From experience, I believe this is what happens when you run kinit then ldapwhoami (or any of the ldap* commands).

  1. The IP of the server you're connecting to is looked up
  2. That IP is resolved back to a FQDN (ldap01.employer.com in this example)
  3. A ticket is requested from Kerberos for "ldap/ldap01.employer.com@EMPLOYER.COM"
  4. If such a ticket exists, it's returned to your client (you'll see it if you run klist later)
  5. Some negotiation happens using the ticket so the server can prove its identity

So, you can connect to the server by any name that points to the right IP (hostname, FQDN, aliases, etc.) but the name in the keytab must be whatever DNS returns when you look up that IP. I don't care if it says "i-juggle-goat-balls-with-my-tongue.employer.com". You put that in the keytab and be happy about it!

Of course, if you're using SSL or TLS when connecting, the name you connect to needs to match the server's certificate (not necessarily what's in the keytab), but that's true with or without Kerberos.

Server Configuration

This will be somewhat specific to RHEL5, but the basics should be the same on any Linux distribution with OpenLDAP.

Packages

Make sure you have cyrus-sasl-gssapi installed.

Kerberos

Edit /etc/krb5.conf to tell your system where the AD servers are. You can read the documentation or find examples on-line. If you're using Kerberos authentication at the OS level, this is probably already done.

OpenLDAP

The LDAP server needs to be able to read its keytab, but no one else should.

% chown root:ldap /etc/openldap/slapd.keytab
% chmod 640 /etc/openldap/slapd.keytab

To tell OpenLDAP where to find this keytab, edit /etc/sysconfig/ldap and add this line:

KRB5_KTNAME=/etc/openldap/slapd.keytab

Finally, the way Kerberos/AD identifies users is almost certainly not going to match the way LDAP identifies them. In order to do anything useful with your directory, OpenLDAP needs to know who you are, so you'll need to map one to the other. In my case, it meant adding this to /etc/openldap/slapd.conf:

authz-regexp
  uid=(.+),cn=gssapi,cn=auth
  uid=$1,ou=users,dc=employer,dc=com

It should go without saying that the uid attribute for these users needs to match the username in AD.

Now you can run service ldap restart to apply the changes. At this point, you should be able to run kinit ad.username followed by ldapwhoami and get something like this:

% kinit ad.username
Please enter the password for ad.username@EMPLOYER.COM:
% ldapwhoami
SASL/GSSAPI authentication started
SASL username: rob.mcbroom@EMPLOYER.COM
SASL SSF: 56
SASL data security layer installed.
dn:uid=rob.mcbroom,ou=users,dc=employer,dc=com

SASL

To allow simple authentication using AD credentials, OpenLDAP will need to be able to verify those credentials on the back-end. It can't do this natively, so it uses saslauthd.

Make sure no one but root can read /etc/krb5.keytab.

% chown root:root /etc/krb5.keytab
% chmod 600 /etc/krb5.keytab

Edit /etc/sysconfig/saslauthd and make sure the mechanism is "kerberos5":

MECH=kerberos5

Create /usr/lib64/sasl2/slapd.conf. Make sure the LDAP server can read this file. It only needs to contain the following line:

pwcheck_method: saslauthd

You'll need to restart OpenLDAP again to pick up this file. You'll also need to get saslauthd running. It's usually installed by default, but not enabled. Start it and enable it at boot.

% service saslauthd start
% chkconfig saslauthd on

Simple authentication still works as it did before. That is, the LDAP server will consult the userPassword attribute for a user. You can keep storing passwords here for some users, but for those that you want to authenticate against AD, you'll need to replace the password with a pointer to their AD account.

userPassword: {SASL}rob.mcbroom@EMPLOYER.COM

When the LDAP server tries to check the password, this will say "It's not here. Go ask saslauthd."

You should now be able to run ldapwhoami -xZW and enter the AD password when prompted. (I'm assuming your server requires TLS for simple authentication. If it doesn't, it should.)

Client Configuration

Packages

The client tools for Kerberos and OpenLDAP will be pre-installed in most cases, but you might need to install cyrus-sasl-gssapi manually on RHEL clients.

For Debian/Ubuntu clients, install

  • krb5-user
  • ldap-utils
  • libsasl2-gssapi-mit

Kerberos

Edit /etc/krb5.conf for your environment. I use the same krb5.conf on my Mac OS X client2 as on my RHEL5 server, so once you figure out what works, you should be able to copy the config around to each system. This is what I'm using:

[libdefaults]
 default_realm = EMPLOYER.COM
 dns_lookup_realm = true
 dns_lookup_kdc = true
 ticket_lifetime = 24h
 forwardable = yes

[realms]
 EMPLOYER.COM = {
  kdc = employer.com:88
  admin_server = employer.com:749
 }

[domain_realm]
 employer.com = EMPLOYER.COM
 .employer.com = EMPLOYER.COM
[appdefaults]
 pam = {
   debug = false
   ticket_lifetime = 36000
   renew_lifetime = 36000
   forwardable = true
   krb4_convert = false
 }

OpenLDAP

If you haven't done this already, you can save yourself a lot of typing by adding some defaults to ~/.ldaprc. At the very least, you should add a URI and a BINDDN (for simple authentication).

URI    ldap://ldap.employer.com
BINDDN uid=rob.mcbroom,ou=users,dc=employer,dc=com

I'm assuming this has been done in all of the ldap* commands I show in this post.

Troubleshooting

The three main components in the setup — Active Directory, OpenLDAP and saslauthd — have one thing in common: Useless or nonexistent error messages3. There's a reason half of this post is dedicated to troubleshooting. This system is extremely touchy and you will run into problems.

Double Check the Basics

  • Verify permissions. Make sure the LDAP server can read the files it needs to read.
  • Check for name resolution issues. If you use nscd, reload it or just shut it down during testing so it doesn't interfere.
  • Make sure you can kinit from the LDAP server. If you can, you know it can at least find the AD server(s).

Start Over

  • Stop and restart OpenLDAP and saslauthd.
  • Kill or reload nscd (reload, not restart).
  • Run kdestroy on the client end. (It sometimes stops asking for a service ticket if it failed once.)

GSSAPI Authentication Problems

Make sure you have a ticket (run kinit) before trying to connect to OpenLDAP.

Make sure your LDAP server is offering GSSAPI.

% ldapsearch -xLLL -s base -b "" supportedSASLMechanisms
dn:
supportedSASLMechanisms: GSSAPI

If you don't see this, either cyrus-sasl-gssapi isn't installed or it isn't being loaded properly. Verify that it's available by running this on the server:

% pluginviewer | grep -i gssapi
GSSAPI LOGIN ANONYMOUS PLAIN EXTERNAL
Plugin "gssapiv2" [loaded],     API version: 4
    SASL mechanism: GSSAPI, best SSF: 56, supports setpass: no
GSSAPI LOGIN ANONYMOUS PLAIN EXTERNAL
Plugin "gssapiv2" [loaded],     API version: 4
    SASL mechanism: GSSAPI, best SSF: 56

Make sure you're getting a ticket from AD on the client end. After running one of the ldap* commands, run klist. You should see an entry that wasn't there before:

01/21/11 08:04:19  01/21/11 18:03:48  ldap/ldap01.employer.com@EMPLOYER.COM
    renew until 01/28/11 08:03:48

If you don't see this entry, it could mean the keytab wasn't created in AD or that the name your client is requesting doesn't match what was created. Use Wireshark to monitor port 88 and look at packets where the protocol is KRB5. This should tell you what your client is requesting and how Kerberos is responding.

If you do see the entry, the problem is most likely on the LDAP server. Make sure OpenLDAP is using the right keytab. You can inspect the contents with klist:

% klist -k /etc/openldap/slapd.keytab
Keytab name: FILE:/etc/openldap/slapd.keytab
KVNO Principal
---- --------------------------------------------------------------------------
   3 ldap/ldap01.employer.com@EMPLOYER.COM

The principal here should match what you see using klist on the client after connecting.

Simple Authentication Problems

You can test that saslauthd is working independent of OpenLDAP by running this on the LDAP server:

% testsaslauthd -u username -p password -s ldap -r EMPLOYER.COM
0: OK "Success."

You should see 0: OK "Success.". Errors are logged in /var/log/messages, or you can open another session on the LDAP server and watch interactively in debug mode to see both success and failure:

% service saslauthd stop
% /usr/sbin/saslauthd -d -a kerberos5

If testsaslauthd works, but simple authentication via LDAP doesn't, check the errors from saslauthd. You'll probably see errors from saslauthd when using testsaslauthd, but not when authenticating via simple auth in LDAP. This means OpenLDAP isn't even trying to use SASL.

  • Make sure /usr/lib64/sasl2/slapd.conf exists with the right contents and can be read by the LDAP server.
  • Make sure the user's password is {SASL}whatever

If you see identical errors using testsaslauthd and simple authentication, then OpenLDAP is configured correctly. The problem is with SASL.

Make sure its using the right ("host") keytab:

% klist -k /etc/krb5.keytab
Keytab name: FILE:/etc/krb5.keytab
KVNO Principal
---- --------------------------------------------------------------------------
   3 host/ldap01.employer.com@EMPLOYER.COM

saslauthd internal error

I got to see this a lot:

Jan 20 12:48:12 ldap01 saslauthd[4765]: auth_krb5: k5support_verify_tgt
Jan 20 12:48:12 ldap01 saslauthd[4765]: do_auth         : auth failure: [user=rob.mcbroom] [service=ldap] [realm=EMPLOYER.COM] [mech=kerberos5] [reason=saslauthd internal error]

This error message should read "You might as well fire up Wireshark now because I'm not going to tell you shit". So, fire up Wireshark on the LDAP server and tell it to listen on port 88 while you try again. Look for packets where the protocol is KRB5. You can see what the server is requesting and how Kerberos responds.


  1. For one thing, AD is a black hole. It'll say "no", but offer nothing as to why. The other reason it took so long is that I don't know what I'm doing. 

  2. Mac users should check out /System/Library/CoreServices/Ticket Viewer.app

  3. As you'll see, Wireshark is a pretty good substitute for actual error messages. I don't know if I could have sorted things out without it. 

blog comments powered by Disqus