Making Apache authenticate against AD
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.
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 namedmod_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: addThere is a
-doption toldifdethat specifies the base DN for the search. Of course, I didn’t know the base DN to start with; by leaving it out, I guessldifdepicked 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.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.)