Integrating Active Directory With Apache

See also: Apache Tips and Tricks

At work CVS access has always been fairly open, after all we're all friends behind the firewall ... right? Well it was decided from upon high that we're a big enough company that this is no longer appropriate, "many things need to change". The one thing I wasn't quite sure how to fix was to protect ViewCVS (a web read-only CVS client).

Here are my requirements:

  1. Must use Active Directory for the user/pass database
  2. Must use Active Directory groups to determine membership in "departmentA"
  3. Must be able to implement group based restrictions, eg. "repositoryA" must only be accessible by "departmentA"

So being a Debian sorta fellow I did a search for all the Apache and LDAP packages which my servers had packages for:

# apt-cache --names-only search libapache | grep -i ldap  
libapache-auth-ldap - LDAP authentication module for Apache  
libapache-authnetldap-perl - LDAP authentication for Apache+mod_perl  
libapache-authznetldap-perl - LDAP access control for Apache+mod_perl  
libapache-mod-ldap - Apache authentication via LDAP directory

I'm not a mod_perl fan so that ruled out two, which left me with mod-ldap or Auth_LDAP. I did some reading. It appears that mod-ldap just stores usernames/passwords in an LDAP tree, it doesn't actually validate credentials by binding to an LDAP server (which seems mostly useless to me but maybe I'm missing something) so that was no use to me. Which left me with Auth-LDAP. I tracked down it's homepage and started to dispair as there was no mention of Active Directory, since AD isn't quite normal LDAP that's normally a bad sign. I spent some more time staring listlessly at the documentation ...

After gathering enough entropy I started to experiement, in the end it was quite easy. The hardest part is wrapping your head around LDAP URLs. Once you grok that the rest is straight forward enough.

  1. Install libapache-auth-ldap into Apache (I'm assuming you know how to do this)
  2. Make sure you know the DN for your Active Directory server,typically it will be in the format of cn=users,dc=domain,dc=com
  3. Setup an Active Directory user that has read only permission to view all your users attributes
  4. Using the username you just created, login to AD, using a normal LDAP browser, and view a different users information
  5. In the users information you should see a memberOf attribute, cut and paste the value somewhere you'll be able to grab it later. Normally the value will look something like CN=DepartmentA,CN=Users,DC=domain,DC=com
  6. Configure Apache to require a valid user to enter a directory:

    <Location /cgi-bin/viewcvs>
        AuthType Basic
        AuthName "Restricted Directory"
        AuthLDAPURL ldap://adserver.domain.com/dc=domain,dc=com?sAMAccountName?sub
        AuthLDAPBindDN anonymous@domain.com
        AuthLDAPBindPassword secretpassword
        require valid-user  
    </Location>
    

Open up a browser and try and access your newly restricted directory, you should be prompted for a username and password and it should only let you enter if you enter in a user/pass combo that exists in your Active Directory server.

  • Assuming that works, lets add in the group restrictions. We only want /cvsroot/reposA to only be accessible by people in departmentA. All you need to do is to change the AuthLDAPURL to have a filter, like this (should be all one long line):

    AuthLDAPURL ldap://adserver.domain.com/dc=domain,dc=com?sAMAccountName?sub?(memberOf=CN=departmentA,CN=Users,DC=domain,DC=com)
    

    This looks a little confusing but all you are doing is saying that in order for the URL to match a user they must have an LDAP attribute called memberOf which matches CN=departmentA,CN=Users,DC=domain,DC=com

And more or less, that's it. You're done! There are some other useful tips which are included below:

  • Active Directory doesn't allow anonymous binds. This is why you have to specify the AuthLDAPBindDN and AuthLDAPBindPassword directives

  • Microsoft has somewhat made up for disallowing anonymous binds by setting things up so that you don't need to know a users proper DN in order to bind to the server. You can bind to any AD server using a DN of the format user@domain.com

  • I specify sub as part of the LDAP URL because our users are broken out into sub-trees, if you have the standard AD setup you shouldn't need it (eg. set the URL to AuthLDAPURL ldap://adserver/cn=users,dc=domain,dc=com?sAMAccountName)

  • You can't restrict access using Apache based on GET parameters, only on directories and files (eg. from an authentication point of view /path/to/cgi?var=value and /path/to/cgi?var=value2 are identical)

  • If you specify each repository in your viewcvs.conf with the cvs_roots variable, ViewCVS will use GET parameters to differentiate between repositories. The only way around this I found was to make sure that all your repositories are in one directory (eg. /cvsroot/reposA and /cvsroot/reposB) and then just point ViewCVS at the root directory and let it browse from that point down. Using this scheme you get URL's like /viewcvs.cgi/reposA/module which works nicely with Apache authentication

  • You might also want to specify the global catalog port number 3268 instead of the standard 636 or 389. The search is much broader and will encompase all domains you might have in AD