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.
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.
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:
-
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.
-
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.
-
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
-
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.
-
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.
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.
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 require
ing 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
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.
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.
Two examples show how this module can be used to secure a web server
or reverse proxy.
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.
- 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.
-
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
- 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>
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.
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.
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.
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).