Introduction  Download  Installation  Configuration  LDAP  HOWTO  Reference  

Configuration

mod_authz_ldap is configured through a set of directives on the directory level. In particular, the server creates an LDAP server connection for each directory configuration. Subsequent requests to the same directory do not create a new LDAP connection, except when Basic Authentication is done against the directory, as this is done through a bind call in a separate connection each time authentication is necessary.

If mod_authz_ldap should accept that other authorization modules may grant access to a user although mod_authz_ldap has denied it, it must be denied authority by the command

    AuthzLDAPAuthoritative off
The default is that mod_authz_ldap is authoritative.

1. Configuring an LDAP Server

Whenever mod_authz_ldap is supposed to authenticate or authorize (if the user was authenticated by a certificate, only the authorization part is left to the module) a user, it needs to contact an LDAP directory. The following directives configure the directory:

    AuthzLDAPServer         server[:port]
    AuthzLDAPBindDN         DN_to_bind_as
    AuthzLDAPBindPassword   Password_to_bind_with
If no server is configured, a server on the local host on the standard LDAP port 389 is assumed. If no bind credentials are defined, an anonymous bind is performed.

2. Configure LDAP User Authentication

LDAP User Authentication becomes active when the AuthzLDAPMethod directive is set to ldap.

User authentication through basic authentication against an LDAP directory is by default active if AuthName and AuthType are defined for a directory or location. It works as follows:

  1. If the user search scope has been set to something other than base, a query is performed from the configured user base DN for a node with the attribute uid set to the name entered by the user in the login dialog. The user's distinguished name is set to the distinguished name of the node found, provided the search returned a single node. If the user search scope was base, then the distinguished name is constructed from the userkey (normally uid), the username from the dialog and the user search base.
  2. The password is verified by binding to the directory as the user whose distinguished name was found in the previous step, with the password from the login dialog.
  3. If the authentication succeeded, additional requirements are verified:
    • If password aging is set by a require age directive, the last modification timestamp is checked as configured as described in section 3.5 on password expiration below.
    • If particular users are required, they are verified in this step. If the user name does not seem to have the format of a distinguished name, a search in the directory is performed first using the same algorithm as for the verification of the password.
    • If some group membership is required, the group name is searched for and membership the user in the group verified.
As one can see, the module needs quite some additional information to perform all these tasks.

As one can see, mod_authz_ldap needs additional information besides the LDAP server to be able to

  1. Find the user in the directory:
        AuthzLDAPUserKey     UID_attribute_name_in_DN
        AuthzLDAPUserBase    DN_of_user_subtree
        AuthzLDAPUserScope   base|onelevel|subtree
    
    The user node in the DIT below the node specified by the AuthzLDAPUserBase directive is expected to have an attribute named by the value of AuthzLDAPUserKey, the value of which is supposed to be the user id from the basic authentication. The default search scope for a user is base. In that case, the distinguished name of the user will be of the form userkey=uid,userbase. If none of the above values are set, the user must enter his/her distinguished name in the password dialog.
  2. Find the group for the user: see the section 4. on group verification below for details.

After successful authentication, the distinguished name of the user is placed in the authorization header. Subsequent authorization modules must therefore be able to deal with a distinguished name as the user id.

A require user directive will search for the user according to the settings above and verify that exactly one user is returned an matches the distinguished name obtained from the LDAP authentication. If the AuthzLDAPUser* attributes are not set, a require user statement must specify the full distinguished name of a user.

3. Configure Certificate Mapping

To active certificate mapping, set the AuthzLDAPMapMethod directive.

Certificate mapping tries to find a user based on the subject and issuer DN. As uniqueness of distinguished names of subjects across CAs is not guaranteed, only the pairs (issuer, subject) or (issuer, serial) are unique.

The LDAP server has to be prepared to perform this mapping. It needs to contain nodes of the following type (described here in old openldap version 1 notation):

attribute	issuerDN	ces
attribute	subjectDN	ces

objectclass	certificatemap
	requires
		objectClass,
		issuerDN,
		owner
	allows
		subjectDN,
		serialNumber,
		userCertificate

Although it looks as both subjectDN and serialNumber can be omitted, one of them is needed (see the discussion of the AuthzLDAPUseSerial directive), but this cannot be expressed in the above syntax. A similar remark applies to the OpenLDAP syntax below.

A definition for the attributes issuerDN and subjectDN and for the objectclass certificatemap should be added to the directory server. Openldap version 1 users can use the file authzldap.slapd.conf file for this purpose, version 2 users will use authzldap.schema. The latter contains official object IDs.

attributetype ( 1.3.6.1.4.1.4263.5.1 NAME 'issuerDN'
	DESC 'The user friendly version of the distinguished name of the 
              issuer of a certificate'
	EQUALITY caseExactIA5Match
	SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SINGLE-VALUE )

attributetype ( 1.3.6.1.4.1.4263.5.2 NAME 'subjectDN'
	DESC 'The user friendly version of the distinguished name of
              the subject of a certificate'
	EQUALITY caseExactIA5Match
	SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SINGLE-VALUE )

objectclass ( 1.3.6.1.4.1.4263.5.3 NAME 'authzLDAPmap'
	DESC 'Map Entry for mod_authz_ldap'
	SUP top AUXILIARY
	MUST ( issuerDN $ owner )
	MAY ( subjectDN $ serialNumber $ userCertificate )

Certificate Mapping searches for a node in a subtree of the directory that has issuerDN and subjectDN as read from the certificate. It is configured through the following directives:

    AuthzLDAPMapMethod certificate|issuerserial|issuersubject
    AuthzLDAPMapBase        base_dn_for_search_for_certificate_dn
    AuthzLDAPMapScope       base|onelevel|subtree
This only makes sense, if mod_ssl has been configured to request a client certificate through the SSLRequireSSL option (see the mod_ssl documentation for details).
    SSLRequireSSL
While earlier releases of this module read the subjectDN from the basic authentication header, they now read it from the mod_ssl context, as all the variables used for the mapping.

Last, but not least, it is possible to directly match a user against her certificate. However, this requires that the directory server is able to perform an equality match for the userCertificate attribute. This is not the case by default in OpenLDAP, but can be enabled by changing the definition of userCertificate in core.schema. Change the definition from

attributetype ( 2.5.4.36 NAME 'userCertificate'
        SYNTAX 1.3.6.1.4.1.1466.115.121.1.8 )
to
attributetype ( 2.5.4.36 NAME 'userCertificate'
        EQUALITY octetStringMatch
        SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
Strictly speaking, this violates some generally accepted schema standards, but it is so immensly useful.

mod_authz_ldap can now directly match the certificate if the directive AuthzLDAPMapMethod is set to certificate. In this case, the module performs a search under the the base specified by AuthzLDAPUserBase with scope as set by AuthzLDAPUserScope for a user having a userCertificate attribute with the same certificate as the SSL client supplied.

Note: +FakeBasicAuth is no longer needed for certificate authentication. The module finds the subject and issuer distinguished names from the SSL context, there is no necessity to misuse a fake basic authentication any longer. If +FakeBasicAuth is specified, the basic authentication will simply be overwritten wherever the module is active.

It is possible to request both basic authentication with LDAP credentials and a certificate from a user, by setting AuthzLDAPMapMethod to both.

4. Configure Group Verification

The require group directive in the context of the mod_authz_ldap module asks to verify membership of a user in a group defined in the LDAP directory. The group must be of objectclass groupOfNames, the members must be defined in member attributes, which have distinguished names of users as values.

    AuthzLDAPGroupBase    base_for_onlevel_group_search
    AuthzLDAPGroupKey     name_attribute_of_group
Groups are expected to have a distinguished name of the form
    groupkey=groupkeyvalue,groupbase
A user is accepted if the group has a member attribute with current user's distinguished name as value. This can be changed using the AuthzLDAPMemberKey directive.

If either directive is not configured, the require statement must specify the full distinguished name of a group.

5. Configure Roles and Filters

Starting with release 0.14, mod_authz_ldap can verify roles of a user. In the most general form, a role is a required value of an attribute of the user's node. mod_authz_ldap simply needs to know the name of the attribute, which can be specified with the configuration directive

    AuthzLDAPRoleAttributeName attrname
If this is set, you can include a directive
    require role rolename
in the server configuration file, which will cause a user to be allowed only if he has an attribute of name attrname with value rolename.

Note that * is a valid value for the role name, this results in the user being accepted if he simply has that attribute. This can be used e.g. to accept all users that have a mailbox (if some attribute contains the name of his mailbox). Substring matches are also possible, on could accept a user exactly if she has a mailbox in a given domain, if an attribute mailboxhost contains the mailbox host. A requirement for a role of the form *.dom.ain will do the trick. This generality of the role mechanism can often save you from having to extend the schema and define a special role attribute.

Even greater generality is offered by the require filter directive of the form

    require filter [ SCOPE ] filter-expression
The SCOPE must be one of the strings BASE, ONLEVEL or SUBTREE. If the word after filter is not one of these strings, it is assumed that the scope was not specified. In this case, the default of BASE will be used and the rest of the line is used as a filter expression. The requirement succeeds if the filter returns something, it fails if nothing is returned by the filter, or if the filter causes an LDAP error.

The filter-expression does not need to be static, the module is able to replace certain strings in the filter by information retrieved from the request. The following replacements are currently implemented:

%%
a literal percent sign (%)
%f
The name of the file requested.
%m
the request method (GET, POST, ...), so that we can control via ldap what types of request a user can perform.
%r
The name or IP address of the requesting client, as in the apache access log (so only the address if client name lookup is disabled).
%s
the server name
%t
The current time in the format YYYYMMDDhhmmss

6. Configure Authorization based on File Ownership

In some applications, it is necessary to base authorization decisions on file ownership. Unfortunately, as the apache process runs with rather low privileges, ordinary access control mechanisms are not suitable. However, using the ``require owner'' and ``require group-owner'' directives, one can restrict access to a file to the owner or members of the group of a file. This model of course assumes Unix file permissions, and is inherently not portable. This is why this functionality must be explicitely enabled using the --with-owner configuration flags.

7. Configure Password Expiration

For password aging, mod_authz_ldap must have a way to find out the last modification time of the password. The user entry in the directory needs to have an attribute for that. With OpenLDAP, the option lastmod on needs to be set in the slapd configuration file. The configuration directive AuthzLDAPModifyKey must be specified as follows:

    AuthzLDAPModifyKey modifytimestamp

By requireing a maximal age, only users that have modifed their password recently enough will be accepted:

    require age 3.5
This denies users access that have last changed their password more than 3.5 days ago.

8. Proxy Authentication

The module can also be used for proxy authentication. The module will normally recognize automatically whether proxy authentication or normal authentication is in effect. In cases where it may not be clear which kind of authentication is happening, the the flag AuthzLDAPProxyAuthentication must be set to on for proxy authentication, and to off for normal authentication. One such case is a reverse proxy, which works as a proxy, but authenticates as a server.

9. Miscellaneous

Logging

The mod_authz_ldap module has its own log level, which is set by default to LOG_DEBUG. If the log level of the server is set to debug, all the information available from the module will be logged to the error log. However, the log information flowing from the module can be reduced by setting a different log level with the AuthzLDAPLogLevel directive. Acceptable parameters are the same as for the LogLevel directive.

Transparent Authentication

When using certificate authentication (AuthzLDAPUseCertificate set to on), the basic authentication header is no longer necesary. However, some applications will still need a user name, which e.g. in CGI-programms is read from the basic authentication header. For these programs, the basic authentication header is overwritten with the distinguished name of the owner of the certificate. In some cases, e.g. in a reverse proxy authenticating with certificates, the application may require an additional athentication. This means that the basic authentication header may not be overwritten by mod_authz_ldap, which can be prevented with the directive

    AuthzLDAPSetAuthorization off
The default is on.

10. Examples

Two examples show how this module can be used to secure a web server or reverse proxy.

10.1. Web server authentication with X.509 certificates

This example describes how mod_authz_ldap can be used to authorize a user authenticated by an X.509 certificate. The LDAP server in use is an OpenLDAP server, for other servers, the preparatory steps (modification of the shema, user and group definitions) may be different.

  1. Prepare the directory to store short forms of certificates of a user. This involves adding suitable object types and attribute definitions to your directory, as described in above.
  2. Add nodes for the certificate mapping to the directory. You may want to modify the script addcertmap of the distribution for this purpose:
    ldapadd -c -D cn=root,dc=othello,dc=ch -w secret <<EOF
    dn: ou=AuthzLDAPCertmap,dc=othello,dc=ch
    objectclass: top
    
    dn: uid=afm,ou=AuthzLDAPCertmap,dc=othello,dc=ch
    objectclass: top
    objectclass: authzLDAPmap
    issuerDN: /C=CH/ST=Schwyz/L=Altendorf/O=Othello/CN=Othello internal
     CA/Email=ca@othello.ch
    subjectDN: /C=CH/ST=Schwyz/L=Altendorf/O=Othello/CN=Andreas Mueller
     /Email=afm@othello.ch
    owner: uid=afm,ou=People,dc=othello,dc=ch
    
    EOF
    
  3. Configure Apache to authenticate through a client certificate, to map the certificate DN to a user node in the directory and to authorize access to the directory /confidential based on the group membership of the user:
        <Location /confidential>
            SSLRequireSSL
            AuthName        AuthzLDAP
            AuthType        Basic
            AuthzLDAPServer "localhost:389"
    
            AuthzLDAPMethod certificate
            AuthzLDAPMapMethod issuersubject
            AuthzLDAPMapBase ou=AuthzLDAPCertmap,dc=othello,dc=ch
            AuthzLDAPMapScope onelevel
    
    	require valid-user
        </Location>
    

10.2. Secure Reverse Proxy

One particularly usefull application of apache and its proxy module is as a security gateway to an internal web server. The ProxyPass and ProxyPassReverse directives in conjunction with the above configuration lead to a secure proxy gateway to some internal server. Here es an example configuration:

    <Location />
        SSLRequireSSL
        AuthName        AuthzLDAP
        AuthType        Basic
        AuthzLDAPMethod certificate
        AuthzLDAPServer "localhost:389"

        AuthzLDAPMapMethod issuersubject
        AuthzLDAPMapBase ou=People,dc=othello,dc=ch
        AuthzLDAPMapScope onelevel

        require valid-user
    </Location>
    ProxyPass        / http://intrahost.doma.in/
    ProxyPassReverse / http://intrahost.doma.in/
This grants a user authenticated through an X.509 certificate and belonging to the group authz access to the web server on intrahost.doma.in. Note that you cannot do without the AuthName or AuthType directive.

11. Caveats

Since the module modifies the basic authentication headers of a request, a call to the authentication function in a subrequest will find an already transformed user name, which cannot be authenticated again. The authentication function only performs authentication for the initial request. As a consequence, if a subrequest leads to a directory differently authenticated than the initial request, the mapping for the subrequest may be incorrect. However, the authorization is performed so differing require clauses for the subrequest are handled correctly.

12. Performance Impact

In a large directory, authentication with mod_authz_ldap becomes slow as soon as one of the searches performed is slow. Indexes can improve the performance considerably, the following table shows the attributes that need to be indexed for the various parts of mod_authz_ldap
Search Type Attribute Index Type
User Search uid equality
Group Search cn equality
Certificate Mapping issuerDN equality
subjectDN equality

The module measures the elapsed and the cpu time for each request and logs it in log level debug to the error log. On a 233MHz Pentium, one can see that an immeasurable amount of cpu time is spent in the module (hardly ever more than the 10ms resolution). The elapsed time depends very much on whether this is the first call to the module: during the first call, an LDAP connection is established, which makes take the module around 70ms if the LDAP server resides on the same machine. Subsequent calls to the authorization function usually take just above 1ms, but calls to the authentication function less than 1ms. The performance hit by this module is therefore negligible: a simple directory listing request on the same machine takes around 150ms to complete, so the impact of the module is around 1%.

Even a somewhat dated Sun SPARCstation 10 spends only a few milliseconds in the authentication function, the authorization function is even cheaper.

All this changes for worse if the LDAP server is overloaded or otherwise slow. E. g. if the certificate issuer or subject attribute is not indexed, the search for the owner of the certficate may well be slow.

13. Troubleshooting

mod_authz_ldap makes extensive use of ap_log_rerror to log debug messages to the servers error log. All critical actions are documented there, and all errors generate an error message. The first step when debugging mod_authz_ldap is therefore to raise the server's log level to debug.

If you cannot solve the problem from the debug output, you may send a problem report to the author. Please equip the message with a succinct Subject:-line, or it may get lost between the junk mail messages (if your subject is ``Help'', it may well happen that the message is not read at all).

 
© Dr. Andreas Müller, Beratung und Entwicklung.