This is an authentication module for Apache that allows you to authenticate HTTP clients using user entries in an LDAP directory. The current stable version is 1.4.7. The latest version and the change log is always available at http://www.rudedog.org/auth_ldap/.
auth_ldap supports the following features:
The module is compatible with Apache 1.3.x. To use the SSL extensions, you must use the Netscape SDK. Note that the Mozilla SDK is not the same as the Netscape SDK, and does not support SSL.
Under Unix, auth_ldap can be built as a DSO (Dynamic
Shared Object) using Apache's apxs
program. You
should be able to build it using the old-style
Configure
method, but I don't personally use that
method, so I can't guarantee support for it. The directions in
this section apply to building auth_ldap as a DSO.
Note that a separate makefile has been provided for building under Windows NT with Visual C++ 5.0.
make
(Unix) or make -m
Makefile.NT
(Windows NT), which should compile the DSO.
make install
to install the module
with the rest of your Apache modules. Under Windows NT, you will
have to copy the DLL by hand into the directory with the rest of
your modules.
LoadModule
directive to your httpd.conf file, such as
# For Unix LoadModule auth_ldap_module libexec/auth_ldap.so # For Windows NT LoadModule auth_ldap_module modules/AuthLDAP.dll
Note that this module has not been extensively tested under Windows NT, as I do not have easy access to an NT box. Because Apache on NT is threaded, there are a lot of concurrency issues. I think that I have addressed most of the concurrency issues, although the locks are pretty coarse grained. If you have any experiences, good or bad, with the module under Windows NT, please let me know, so I can improve these instructions and the module. I'm especially open to receiving code improvements for NT.
auth_ldap uses the following algorithm to authenticate a user
require
directives), otherwise
deny access.
The following directives are used by the module.
Syntax:
AuthLDAPBindDN distinguished-name
Context: directory,
.htaccess
Override: AuthConfig
Status: Extension
Module: auth_ldap
An optional DN used to bind to the server when searching for entries. If not provided, auth_ldap will use an anonymous bind.
Syntax:
AuthLDAPBindPassword password
Context: directory,
.htaccess
Override: AuthConfig
Status: Extension
Module: auth_ldap
A bind password to use in conjunction with the bind DN. Note that the bind password is probably sensitive data, and should be properly protected. You should only use the AuthLDAPBindDN and AuthLDAPBindPassword if you absolutely need them to search the directory.
Syntax:
AuthLDAPAuthoritative < on(default) | off
>
Context: directory,
.htaccess
Override: AuthConfig
Status: Extension
Module: auth_ldap
Set to off if this module should let other authentication modules attempt to authenticate the user, should authentication with this module fail. Control is only passed on to lower modules if there is no DN or rule that matches the supplied user name (as passed by the client).
Syntax: AuthLDAPURL url
Context: directory,
.htaccess
Override: AuthConfig
Status: Extension
Module: auth_ldap
An RFC 2255 URL which specifies the LDAP search parameters to use. The syntax of the URL is
ldap://host:port/basedn?attribute?scope?filter
ldap | For regular ldap, use the string ldap. For secure LDAP, use ldaps instead. Secure LDAP is only available if auth_ldap was compiled with SSL support. |
host:port |
The name/port of the ldap server (defaults to localhost:389 for ldap, and localhost:636 for ldaps). To specify multiple, redundant LDAP servers, just list all servers, separated by spaces. auth_ldap will try connecting to each server in turn, until it makes a successful connection.
Once a connection has been made to a server, that
connection remains active for the life of the
If the LDAP server goes down and breaks an existing connection, auth_ldap will attempt to re-connect, starting with the primary server, and trying each redundant server in turn. Note that this is different than a true round-robin search. |
basedn | The DN of the branch of the directory where all searches should start from. At the very least, this must be the top of your directory tree, but could also specify a subtree in the directory. |
attribute | The attribute to search for. Although RFC 2255 allows a comma-separated list of attributes, only the first attribute will be used, no matter how many are provided. If no attributes are provided, the default is to use uid. It's a good idea to choose an attribute that will be unique across all entries in the subtree you will be using. |
scope | The scope of the search. Can be either one or sub. Note that a scope of base is also supported by RFC 2255, but is not supported by this module. If the scope is not provided, or if base scope is specified, the default is to use a scope of sub. |
filter |
A valid LDAP search filter. If not provided, defaults to
(objectClass=*) , which will search for all
objects in the tree. Filters are limited to approximately
8000 characters (the definition of
MAX_STRING_LEN in the Apache source code). This
should be than sufficient for any application.
|
When doing searches, the attribute, filter and username passed
by the HTTP client are combined to create a search filter that
looks like
(&(filter)(attribute=username))
.
For example, consider an URL of
ldap://ldap.airius.com/o=Airius?cn?sub?(posixid=*)
.
When a client attempts to connect using a username of
Babs Jenson, the resulting search filter will be
(&(posixid=*)(cn=Babs Jenson))
.
See below for examples of
AuthLDAPURL
URLs.
Syntax: AuthLDAPRemoteUserIsDN
Context:
AuthLDAPRemoteUserIsDN < off(default) | on
>
Override:
Status:
Module:
If this directive is set to on, the value of the REMOTE_USER environment variable will be set to the full distinguished name of the authenticated user, rather than just the username that was passed by the client. It is turned off by default, which is consistent with the behavior of previous auth_ldap releases.
Syntax:
AuthLDAPCertDBPath /path/to/cert7.db/directory
Context: server config
Override: Not Applicable
Status: Extension
Module: auth_ldap
Specifies in which directory auth_ldap should look for the certificate authorities database. There should be a file named cert7.db in that directory.
Syntax: AuthLDAPCacheSize size
Context: server config
Override: Not Applicable
Status: Extension
Module: auth_ldap
Specifies the maximum size of the LDAP search cache. Set it to -1 to disable search caching, and set it to 0 to make it unlimited. The default size is 10KB. See the section below on caching for complete information on caching LDAP operations in auth_ldap.
Syntax: AuthLDAPCacheTTL time
Context: server config
Override: not applicable
Status: Extension
Module: auth_ldap
Specifies the time (in seconds) that an item in the search cache remains valid. The default is 600 seconds (10 minutes). A value of 0 means that items in the cache never go stale.
Syntax: AuthLDAPOpCacheSize size
Context: server config
Override: Not applicable
Status: Extension
Module: auth_ldap
This specifies the size of the cache auth_ldap uses to cache LDAP operations. The default is 1024 entries. Setting it to -1 disables operation caching.
Syntax: AuthLDAPOpCacheTTL time
Context: server config
Override: Not applicable
Status: Extension
Module: auth_ldap
Specifies the time (in seconds) that entries in the operation cache remain valid. The default is 600 seconds.
Syntax:
AuthLDAPCacheCompareOps < on(default) | off
>
Context: server config
Override: Not applicable
Status: Extension
Module: auth_ldap
If this directive is set to on, auth_ldap will cache any compare operations (these are used to satisfy require user directives).
Syntax:
AuthLDAPFrontPageHack < off(default) | on
>
Context: directory,
.htaccess
Override: AuthConfig
Status: Extension
Module: auth_ldap
See the section on using Microsoft FrontPage with LDAP .
require user
The require user
directive can accept multiple users
on a line if the attribute values don't have spaces in them. If
the attribute values do have spaces, just put multiple require
user directives, with one attribute value per line. Thus, for a
AuthLDAPURL
of
ldap://ldap/o=Airius?cn
, the following directives
could be used
require user Barbara Jenson require user Fred User require user Joe Manager
Note that with this configuration, Barbara Jenson could sign on as
Babs Jenson or any other cn that she has in
her LDAP entry. Only the single require user
line is
needed to support all values of the attribute in the user's entry.
If the uid attribute was used instead of the cn attribute, the above three lines could be condensed to
require user bjenson fuser jmanager
require group
The require group
directive takes the distinguished name
of any LDAP entry that has either member or
uniqueMember attributes. For example, assume that the
following entry existed in the LDAP directory:
dn: cn=Administrators, o=Airius objectClass: groupOfUniqueNames uniqueMember: cn=Barbara Jenson, o=Airius uniqueMember: cn=Fred User, o=Airius
The following directive would grant access to both Fred and Barbara:
require group cn=Administrators, o=Airius
AuthLDAPURL ldap://ldap.airius.com:389/ou=People, o=Airius?uid?sub?(objectClass=*) require valid-user
AuthLDAPURL ldap://ldap.airius.com ldap1.airius.com/ou=People, o=Airius require valid-user
AuthLDAPURL ldap://ldap.airius.com/ou=People, o=Airius?cn require valid-user
AuthLDAPURL ldap://ldap.airius.com/o=Airius?uid require group cn=Administrators, o=Airius
AuthLDAPURL ldap://ldap.airius.com/o=Airius?uid??(qpagePagerID=*) require valid-user
AuthLDAPURL ldap://ldap.airius.com/o=Airius?uid??(|(qpagePagerID=*)(uid=jmanager)) require valid-user
This last example shows the power of filters, but may be confusing. It helps to evaluate what the search filter will look like based on who connects. The text in blue is the part that is filled in based on the attribute passed in the URL. The text in red is the part that is filled in based on the filter passed in the URL. The text in green is the information that is retrieved from the HTTP client. If Fred User connects as fuser, the filter would look like
(&(|(qpagePagerID=*)(uid=jmanager))(uid=fuser))
This search will only succeed if fuser has a pager. When Joe Manager connects as jmanager, the filter looks like
(&(|(qpagePagerID=*)(uid=jmanager))(uid=jmanager))
This search will succeed whether jmanager has a pager or not.
For improved performance, auth_ldap will cache many LDAP operations. Caching can easily double or triple the throughput of Apache when it's serving auth_ldap-protected pages. In addition, the load on the LDAP server will be significantly decreased.
auth_ldap supports two types of LDAP caching: search caching and operation caching.
auth_ldap uses LDAP searches to map the user
credentials passed during a basic authentication to a
distinguished name in the directory. In other words, if client
connects to server using the username bjensen, and the
AuthLDAPURL directive contains
ldap://ldap/o=Airius?uid
, then auth_ldap
will do an LDAP search operation on uid=bjensen
on
the directory tree rooted at o=Airius
to map the
username bjensen to an actual DN. This could take a
significant amount of time, depending on the size of the directory
and other factors. Search caching is used to cache the results of
those searches.
Search caching is handled with the Netscape LDAP SDK's
ldap_memcache
functions or the OpenLDAP
ldap_enable_cache
function. Therefore, cache behavior
could differ depending on the SDK used. For more information about
the differences in caching, consult the appropriate SDK
documentation or source code.
The second type of caching is used to cache LDAP operations that
aren't cached using the SDK caching
routiones. auth_ldap will cache both bind
operations and compare operations. Without bind
caching, auth_ldap is forced to do three binds during
the course of a single authentication. The first is to bind with
the DN and password provided by the AuthLDAP
directives. The second is to rebind using the credentials provided
by the client, while the third is to rebind back to the original
DN/password in order to do further operations in the
directory. With bind caching, auth_ldap caches the
results of the last bind for a particular DN, and will not attempt
to rebind if the same credentials are presented to Apache. This
can result in significant performance improvements.
auth_ldap will also cache some LDAP compare
operations. auth_ldap uses compare operations to
validate require user directives. For example, to use the
situation above, assume auth_ldap has done a search for
uid=bjensen
and found a distinguished name. If there
is a require user bjensen
directive, then
auth_ldap will use a compare operation to determine if
uid=bjensen
is valid for the retrieved distinguished
name. If compare caching is turned on, then it will cache the
results of the operation.
Note that while auth_ldap also uses compare operations to determine group membership in require group directives, it does not (yet) cache these operations.
Caching compare operations is the most memory intensive cache. It also seems to have the least impact on performance, and should be the first one to get turned off
Note that the AuthLDAPOpCacheSize
directive only
determines the initial size of the cache, but doesn't limit the
size of the cache at all. Choosing too small a number will result
in a seriously reduced cache efficiency. Choosing too large a
number will waste some memory, but not much. It's much better to
err on the large side when choosing the size. The default is 1024
entries, which should be sufficient to cache at least 3000
distinguished names.
These benchmarks compare the performance for different cache configurations. They list the time to complete the benchmark, and the number of LDAP operations actually performed the LDAP server. All benchmarks were performed with Apache 1.3.4. Apache, the WWW client and the directory server all resided on the same system.
The first benchmark did 10,000 retrievals of the same page. The
page was protected by a <Location>
specification in access.conf that contained a single
require user
directive.
LDAP Operations | ||||||
---|---|---|---|---|---|---|
Cache Level | Real Time | Connect | Bind | Search | Compare | Total |
None | 6:04 | 6 | 60,006 | 30,000 | 30,000 | 120,012 |
Search | 4:53 | 6 | 60,006 | 6 | 30,000 | 90,018 |
Search, bind | 2:20 | 6 | 18 | 6 | 30,000 | 30,030 |
Search, bind, compare | 1:46 | 6 | 18 | 6 | 6 | 36 |
The second benchmark did 10,000 retrievals of the same page, but
chose a random username from a pool of 1000 names for each
connection. The page was protected by a
<Location>
specification in
access.conf that contained a require
valid-user
directive. Since this doesn't require a LDAP
compare operation, there is no benchmark for the third level of
caching (search, bind and compare).
LDAP Operations | |||||
---|---|---|---|---|---|
Cache Level | Real Time | Connect | Bind | Search | Total |
None | 5:47 | 7 | 60,002 | 30,001 | 90,009 |
Search | 5:00 | 7 | 60,002 | 9,970 | 69,979 |
Search, bind | 2:51 | 7 | 9,672 | 9,968 | 19,647 |
These benchmarks are only included to give an idea about how caching can improve performance. They do not really simulate real world conditions, since both servers and the client were in a sandbox.
auth_ldap will not talk to any SSL server unless that server has a certificate signed by a known Certificate Authority. Once you've built auth_ldap with SSL support, you have to tell auth_ldap where it can find a database containing the known CAs. This database is in the same format as Netscape Communicator's cert7.db database. I don't know what that format that is; the easiest thing to do is start up a fresh copy of netscape, and grab the resulting $HOME/.netscape/cert7.db file. If anybody can tell me how to create one of those databases myself, don't hesitate to drop me a line.
To specify a secure LDAP server, use ldaps://
in the
AuthLDAPURL
directive, instead of
ldap://
. auth_ldap does not support mixed
secure and insecure servers. I can't really see a compelling
reason to add support but anyone out there who wants it is welcome
to try to convince me.
Normally, FrontPage uses FrontPage-web-specific user/group files (i.e., the mod_auth module) to handle all authentication. Unfortunately, you cannot just change to LDAP authentication by adding the proper directives, because it will break the Permissions forms in the FrontPage client, which attempt to modify the standard text-based authorization files.
If you are planning to use FrontPage with auth_ldap, you should compile auth_ldap with the -DAUTH_LDAP_FRONTPAGE_HACK. You could do this by adding it to the CFLAGS line in the Makefile.
Once you've created a FrontPage web, you can add LDAP authentication by adding the following directives to every .htaccess file that gets created in the web
AuthLDAPURL the url AuthLDAPAuthoritative off AuthLDAPFrontPageHack on
AuthLDAPAuthoritative
must be off to allow
auth_ldap to decline group authentication so that
Apache will fall back to file authentication for checking group
membership. This allows the FrontPage-managed group file to be
used.
FrontPage restricts access to a web by adding the require
valid-user
directive to the .htaccess files. If
AuthLDAPFrontPageHack
is not on, the
require valid-user
directive will succeed for any user
who is valid as far as LDAP is concerned. This means that
anybody who has an entry in the LDAP directory is considered a
valid user, whereas FrontPage considers only those people in the
local user file to be valid. The purpose of the hack is to force
Apache to consult the local user file (which is managed by
FrontPage) - instead of LDAP - when handling the require
valid-user
directive.
Once you've added the directives as above, FrontPage users will be able to perform all management operations from the FrontPage client.
LoadModule
directive
for auth_ldap comes after the one for
mod_auth.
<Location>
or
<Directory>
directives, they won't work. This
is because auth_ldap snarfs the
AuthUserFile
directive found in FrontPage
.htaccess files so that it knows where to look for
the valid user list. If the auth_ldap directives
aren't in the same .htaccess file as the FrontPage
directives, then the hack won't work, because it won't be able
to find the FrontPage-managed user file.
Thanks to
Copyright © 1998, 1999 Enbridge Pipelines Inc.
Copyright © 1999 Dave Carrigan
All rights reserved.
This module is free software; you can redistribute it and/or modify it under the same terms as Apache itself. This module is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. The copyright holder of this module can not be held liable for any general, special, incidental or consequential damages arising out of the use of the module.