darkness

Thursday, 14 December 2006

Making Apache authenticate against AD

darkness @ 02:03:40

Setting up mod_auth_ldap to talk to Active Directory should be pretty easy. Except it isn’t.

Up front: I’m doing this on CentOS 4.4, Apache 2.0.52 (httpd-2.0.52-28.ent.centos4), OpenLDAP 2.2.13 (openldap-2.2.13-6.4E).

First and foremost, AD LDAP doesn’t allow anonymous binds, so Apache needs a user and password to bind with. This user and password will be used to search AD for the user name entered by the user. On my small and hopefully “mostly default” AD domain, I was able to use just a run-of-the-mill user account for this purpose.

Now you’ll need the DN of that user. I had no idea what that was (though I can now make a pretty good guess; see below) since all the default GUI administration tools seem to hide details like the DN or RDN of an object. So to find the DN for the user you just created, I’ve come up with two options.

  1. Use ldifde. This was available on my Windows Server 2003 controller without having to install anything special, as far as I know. Say I created a user named mod_auth_ldap (which I did). To find the DN:

    J:\>ldifde -r samaccountname=mod_auth_ldap -l dn -f foo.out
    Connecting to "your-ad-server.ad.yourdomain.com"
    Logging in as current user using SSPI
    Exporting directory to file foo.out
    Searching for entries...
    Writing out entries.
    1 entries exported
    
    
    The command has completed successfully
    
    
    J:\>type foo.out
    
    
    dn: CN=Apache mod_auth_ldap,OU=Some Users,DC=ad,DC=yourdomain,DC=com
    changetype: add
    

    There is a -d option to ldifde that specifies the base DN for the search. Of course, I didn’t know the base DN to start with; by leaving it out, I guess ldifde picked the root of the AD domain the computer was a member of. There are other options, to specify things like the server and credentials; similarly, by not specifying these I assume they default to one of the domain’s LDAP servers (or perhaps it defaults to localhost, since I was on one of the LDAP servers) and your current user credentials, respectively.

  2. Go find “ADSI Edit” for your version of Windows. This appears to be a MMC snap-in. It lets you browse the AD LDAP tree as, well, LDAP: I can see my domain’s base DN, and I can graphically browse the DIT. If you have permission, you can even edit things (not recommended; just because it looks like LDAP doesn’t mean it’s not Microsoft LDAP). This looks like a very handy tool to have available for debugging when you’re trying to connect some external LDAP application to AD LDAP. If you can get it installed, I recommend it.

    ADSI Edit (adsiedit.msc, adsiedit.dll, others perhaps) can be found in the Windows Server 2003 Support Tools. It may also be available in the “Administrators Tools Pack” (“adminpak”), though it wasn’t there in the Administrators Tools Pack for Windows 2003. Hopefully ADSI Edit will run (or has versions that will run) under, say, XP.

Looking at the usual “Active Directory Users and Computers” tool, I can see how I could have guessed dc=ad,dc=foo,dc=com from the domain at the root of the tree, and I can see the icons that seem to indicate “organizationalUnit” as the RDN versus the icons that seem to indicate “commonName” (or at least “not organizationalUnit”) for the RDN. I also know now that commonName is the default RDN for a user. So I can now make a decent guess of a user’s DN. (Why should I have to, again?)

In any case, I do recommend testing your new Apache LDAP user with ldapsearch (part of OpenLDAP, and probably a dozen other LDAP kits):

[darkness@gateway ~]$ ldapsearch -x -W \
    -H ldap://server1.ad.yourdomain.com -b dc=ad,dc=yourdomain,dc=com \
    -D "cn=Apache mod_auth_ldap,ou=Some Users,dc=ad,dc=yourdomain,dc=com" \
    'samAccountName=mod_auth_ldap' dn
Enter LDAP Password:
# extended LDIF
#
# LDAPv3
# base <dc=ad,dc=yourdomain,dc=com> with scope sub
# filter: samAccountName=mod_auth_ldap
# requesting: dn
#

# Apache mod_auth_ldap, Some Users, ad.yourdomain.com
dn: CN=Apache mod_auth_ldap,OU=Some Users,DC=ad,DC=yourdomain,DC=com

# search reference
ref: ldap://ForestDnsZones.ad.yourdomain.com/DC=ForestDnsZones,DC=ad,DC=yourdomain,DC=com

# search reference
ref: ldap://DomainDnsZones.ad.yourdomain.com/DC=DomainDnsZones,DC=ad,DC=yourdomain,DC=com

# search reference
ref: ldap://ad.yourdomain.com/CN=Configuration,DC=ad,DC=yourdomain,DC=com

# search result
search: 2
result: 0 Success

# numResponses: 5
# numEntries: 1
# numReferences: 3

Gosh, I wonder what those search reference things are? Oh well, we’ll just ignore them… (Yeah, you quite possibly can’t; keep reading.)

OK, now to configure Apache. I based my configuration on this article on how to have Apache authenticate against AD. Here’s my configuration:

<Location /woc>
        AuthLDAPAuthoritative on
        AuthType Basic
        AuthName "My web application"
        AuthLDAPBindDN "cn=Apache mod_auth_ldap, ou=Some Users, dc=ad, dc=yourdomain, dc=com"
        AuthLDAPBindPassword "sekret"
        AuthLDAPURL "ldap://server1.ad.yourdomain.com/dc=ad,dc=yourdomain,dc=com?samAccountName?sub?(objectClass=*)"
        #require valid-user  # you can use this if you want any user
        require group CN=Web app users,OU=Some Groups,DC=ad,DC=yourdomain,DC=com
</Location>

A few notes. First, I’m using regular LDAP, not LDAP with Start TLS or LDAP over SSL (LDAPS). According to “How to enable LDAP over SSL with a third-party certification authority” Windows (2000, at least, and I bet 2003 too) doesn’t support Start TLS. LDAPS is not enabled by default unless you… do some stuff. I haven’t done that, so my passwords are being passed in the clear. You have been warned.

Next, note there are no quotes around the group. This is according to the documentation:

This directive specifies an LDAP group whose members are allowed access. It takes the distinguished name of the LDAP group. Note: Do not surround the group name with quotes.

So you’ve put the Apache configuration in. You reload the configuration, and go to test it with some user in the Web app users group. And… it fails. Maybe you get an error in Apache’s error log such as:

[Wed Dec 13 23:34:41 2006] [warn] [client 127.0.0.1] [7122]
auth_ldap authenticate: user mod_auth_ldap authentication failed;
URI /woc [ldap_search_ext_s() for user failed][Operations error]

Let me save you some time and point you to Apache bug # 26538.

Essentially the problem seems to be that you’re searching at the root of the AD tree, and when you do that (on the normal LDAP port, at least) AD returns some referrals. OpenLDAP will, by default, then proceed to attempt to follow those referrals… with anonymous binds. AD denies the searches from anonymously-binded connections, and mod_auth_ldap chokes and refuses to go any further. I tried using the “global catalog” (ldap://server1.ad.yourdomain.com:3268) and it kind of worked, except it seemed that I wasn’t able to see the group I created (Web app users). In retrospect I might have been searching wrong, so it might be fine to use the “global catalog.” What I ended up doing is turning off referrals system-wide by putting REFERRALS off in /etc/openldap/ldap.conf (on CentOS 4). Despite the fact that this option is undocumented in ldap.conf(5), it worked to fix the error.

Of course, then I got another error saying my user wasn’t in the group I had created. The problem: I tried to get smart and set Web app users as the user’s primary group. My guess is that a group object doesn’t list a particular user in a member attribute if the group is the user’s primary group. In other words, if I had left Domain users as the user’s primary group, then added Web app users, it would have been fine. The solution: … don’t make Web app users any user’s primary group.

Thus do I finally have Apache authenticating against AD. Reminder: use HTTPS, lest you pass your AD passwords in the clear. (And get that LDAPS working.)

No Comments »

No comments yet.

RSS feed for comments on this post. TrackBack URL

Leave a comment

Powered by WordPress