Apache and Subversion authentication with Microsoft Active Directory LDAP

Last updated on 2007-12-03@23:38. The company I work for had finally realized the benefits of a decent source code versioning system so after a short evaluation they settled on Subversion. To make user management easier they also wanted to use Microsoft Active Directory, so I set off on a quest to make Apache talk to our Active Directory 2003 server for authentication. Before I explain to you how I set this all up on Debian Etch I have to get something off my chest first. Sensitive people may want to skip the next paragraph.

Microsoft Active Directory is a bit-rotten crock that should have never seen the light of day. After two full days of waving dead chickens at it, trying to make any sense of it's irrational behavior I would love nothing more than to pick it up and throw it off the roof of our building, BofH style, aiming it at the PHB that bought it in the first place. Or it's programmer. Whomever passes by first. It's API only vaguely resembles LDAP after at least three bottles of whiskey or one pan galactic gargle blaster and squinting really, really hard. Fortunately our management has seen the light of day after this little misadventure and in a few months we're migrating to Open-Xchange.

Right. That's settled. Back to making it work because we need Subversion before we have migrated to Open-Xchange. Let's start off by installing a bunch of software that we need: apache2, subversion and libapache2-svn. Make sure that the correct modules are enabled by symlinking then from /etc/apache2/mod-enabled to /etc/apache2/mods-available. Here are the relevant files:

$ ls -al /etc/apache2/mods-enabled
alias.load -> ../mods-available/alias.load
auth_basic.load -> ../mods-available/auth_basic.load
authnz_ldap.load -> /etc/apache2/mods-available/authnz_ldap.load
authz_default.load -> ../mods-available/authz_default.load
authz_user.load -> ../mods-available/authz_user.load
dav.load -> ../mods-available/dav.load
dav_svn.conf -> ../mods-available/dav_svn.conf
dav_svn.load -> ../mods-available/dav_svn.load
ldap.load -> ../mods-available/ldap.load

Apache2 on Debian Etch ships with mod_authnz_ldap instead of mod_auth_ldap, so if most of the online tutorials for LDAP authentication did not work for you, that's why. mod_authnz_ldap works just a little bit different. I am going to implement things in such a way that makes it easy to test your configuration in between. First we get Active Directory working and then we look at Subversion.

Active Directory authentication

Start off by creating a directory where later on you will host Subversion repositories and create a basic Apache configuration for it. For ease of testing make sure that you can view directory indexes. I chose to put my repositories under /var/lib/svn and I will use a virtual server for it. Create a new configuration file /etc/apache2/sites-available/svn and symlink it from /etc/apache2/sites-enabled/.

NameVirtualHost *
<VirtualHost *>
        DocumentRoot /var/lib/svn
        ServerName svn.example.com
        ErrorLog /var/log/apache2/error.log
        LogLevel warn
        CustomLog /var/log/apache2/access.log combined
        ServerSignature On
        <Directory "/var/lib/svn">
                Options Indexes FollowSymLinks MultiViews
                Order allow,deny
                allow from all
        </Directory>
</VirtualHost>

Now you should modify your local LDAP configuration. There's a problem with references when using Active Directory so you need to turn them off. Edit your /etc/ldap/ldap.conf and add:

REFERRALS       off

Now you can add the LDAP configuration directives to your Apache configuration. I find it very useful to test Active Directory using the LDAP protocol first. You can use this Java LDAP browser to test Active Directory an lookup some of the information that you need to add to Apache. LDAP authentication is a two-step process. First you need to bind LDAP to apache, then you can query information. So, you need an LDAP account to bind to. I recommend setting up a separate user for this and grating him rights to read everything but write nothing. You can test this account using the applet. Note that when connecting to the applet you need to specify the account to bind to as the "user principal name" (username@example.com) instead of the "distinguished name" (CN=username,DC=example,DC=com). In Apache you can use either. Here's a screenshot of the applet showing the settings that should work.

If port 389 does not work for you for some reason, try port 3268. That port speaks a different LDAP dialect apparently (yes, that confuses me too). After you have filled out the hostname, port and protocol version you can click the "Fetch DNs" button to fill the "Base DN" field. When you click "connect" you should be able to browse your Active Directory. When this works it's time to add the Apache LDAP configuration directives. I will explain them one by one afterwards. Add this to your VirtualHost configuration:

<Location "/">
        AuthBasicProvider ldap
        AuthType Basic
        AuthzLDAPAuthoritative off
        AuthName "My Subversion server"
        AuthLDAPURL "ldap://directory.example.com:389/DC=example,DC=com?sAMAccountName?sub?(objectClass=*)" NONE
        AuthLDAPBindDN "CN=apache,CN=Users,DC=example,DC=com"
        AuthLDAPBindPassword hackme
        require valid-user
</Location>

AuthBasicProvider ldap and AuthType Basic tell Apache to use LDAP for authentication. AuthzLDAPAuthorative off tells Apache that LDAP does not have the final word over who gets access and who doesn't. This is one of the differences between mod_auth_ldap and mod_authnz_ldap. In our case, LDAP just passes some information back to Apache and mod_authz_user has the final decision over who gets access and who does not. The AuthName directive sets the title that the users will see on their login popup. Next up is the AuthLDAPUR:. It's built up as such:

"protocol://hostname:port/base?attribute?scope?filter" NONE

base is the BaseDN you want to search under. Pick whatever worked in the Java Applet. Usually just your domain name (above it's example.com) will do. The LDAP attribute is what you try to match to the username that the user typed in. Browse through LDAP to see what possibilities are available. The sAMAccountName is the name that Windows users use to login to their system. The scope parameter tells LDAP how deep to search beneath the BaseDN. Do yourself a favour and leave it on "sub" (all the way). The filter determines what kind of objects should be returned. In my example I play safe again and say "all objects".

Officially the base, attribute, scope and filter are all optional variables but Active Directory refused to play ball if I did not specify everything. Also, I have no idea why the URL needs to be in double quotes and why it needs to be followed by the word NONE. All I know is that it doesn't work if I omit it. If someone knows, please leave a comment so I can amend this article.

Updated on 2007-12-03@23:38. Alex Belbey contributes that NONE specified the kind of connection to use. In this case an unsecured connection (as opposed to e.g. an SSL or TLS encrypted connection).

NONE
stablish an unsecure connection on the default LDAP port. This is the same as ldap:// on port 389.
SSL
Establish a secure connection on the default secure LDAP port. This is the same as ldaps://
TLS/STARTTLS
Establish an upgraded secure connection on the default LDAP port. This connection will be initiated on port 389 by default and then upgraded to a secure connection on the same port.

After the AuthLDAPURL is the user information for the user you wish to bind LDAP to. You can use the distinguished name as I have done in the example, but you can also use the user principal name:

AuthLDAPBindDN "apache@example.com"

Finally we tell Apache with the require directive that all users should be given access. If you now restart your Apache server with /etc/init.d/apache2 restart you should be able to successfully login. Congratulations, the hardest part is done. If it does not work then you need to look at the apache error log to see what goes wrong. It's a bit cryptic so I will explain that as well. As I explained before, LDAP authentication is a two-step process of binding and querying. Either step can fail and the error log will tell you why. If the bind step fails then there is something wrong with the AuthLDAPBindDN, the AuthLDAPBindPassword or the AuthLDAPURL. Here's what a bind failure looks like:

auth_ldap authenticate: user apache authentication failed; URI / [LDAP: ldap_simple_bind_s() failed][Invalid credentials]

If the bind works but something goes wrong with the query, the error is probably caused by a fault AuthLDAPURL and will look something like:

auth_ldap authenticate: user John Doe authentication failed; URI / [ldap_search_ext_s()
for user failed][Operations error]

It's also possible that you do not see any error at all in the logfile. In that case, LDAP works but something goes wrong when Apache's mod_authz_user tries to determine if it should grant access or not.

Update: Mark van Sintfiet adds that in order for require ldap-group to work, you should use the full distinguishedName field in the ldap-group directive. If you do not, Active Directory will fail to authenticate. You can use the Java LDAP browser mentioned above to lookup the distinguishedName.

Subversion integration

Adding subversion to the LDAP/Apache mix is actually quite easy. Start off by removing the <Directory> block and the DocumentRoot directive because you cannot access the same URL though regular Apache and Subversion at the same time. You can also simply point the DocumentRoot somewhere else so you can create an information page when users hit the root. I will be setting up two groups of repositories that are writable by two groups of LDAP users, plus a sandbox repository for everyone so they can play with Subversion. Start by creating two directories in /var/lib/svn that will hold the repositories. Then create some Subversion repositories.

$ cd /var/lib/svn
$ mkdir group1
$ mkdir group2
$ svnadmin create /var/lib/svn/sandbox
$ svnadmin create /var/lib/svn/group1/g1-repository
$ svnadmin create /var/lib/svn/group1/g2-repository

Now you need to create some <Location> directives in Apache for these repositories. The require ldap-group directives tell Apache to only allow in a certain group. Note that the ldap-group value must not be in quotes. By using a <LimitExcept> I only protect writing to a repository. Everyone can read all repositories. Here is what the full configuration looks like in the end:

NameVirtualHost *
<VirtualHost *>
        DocumentRoot /var/lib/svn/htdocs
        ServerName svn.example.com
        ErrorLog /var/log/apache2/error.log
        LogLevel warn
        CustomLog /var/log/apache2/access.log combined
        ServerSignature On
        <Location "/">
                AuthBasicProvider ldap
                AuthType Basic
                AuthzLDAPAuthoritative off
                AuthName "My Subversion server"
                AuthLDAPURL "ldap://directory.example.com:389/DC=example,DC=com?sAMAccountName?sub?(objectClass=*)" NONE
                AuthLDAPBindDN "CN=apache,CN=Users,DC=example,DC=com"
                AuthLDAPBindPassword hackme
                require valid-user
        </Location>
        # The sandbox repository can be written to by anyone
        <Location "/sandbox">
                DAV svn
                SVNPath /var/lib/svn/sandbox
        </Location>
        # repositories for Group 1
        <Location "/group1">
                DAV svn
                SVNParentPath /var/lib/svn/group1
                SVNListParentPath on  # Show an index of all repositories in /var/lib/svn/group1
                <LimitExcept GET PROPFIND OPTIONS REPORT>
                        require ldap-group CN=Group 1,DC=example,DC=com
                </LimitExcept>
        </location>
        # repositories for Group 2
        <Location "/group2">
                DAV svn
                SVNParentPath /var/lib/svn/group2
                SVNListParentPath on  # Show an index of all repositories in /var/lib/svn/group2
                <LimitExcept GET PROPFIND OPTIONS REPORT>
                        require ldap-group CN=Group 2,DC=example,DC=com
                </LimitExcept>
        </location>
</VirtualHost>

The DAV svn directive tells Apache that Subversion will handle these requests. The SVNPath directive allows access to a single repository and SVNParentPath allows access to a directory full of repositories. By setting SVNListParentPath it will show all the repositories in the directory. Compared to getting Active Directory to work, this is all very easy.

I hope this article saves someone from the Active Directory nightmare I had. Happy (sub)versioning!

References
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值