Apache lookup module

From RavenWiki
Revision as of 12:07, 28 January 2009 by jw35 (talk | contribs) (→‎Bugs and potential enhancements: TLS dificulties)
Jump to navigationJump to search

This is an Apache2 module (which appears to work in Apache 2.0 and Apache 2.2) designed to perform authorization functions for an existing REMOTE_USER (as derived from Raven or otherwise) by querying lookup.

Please send questions about this module to lookup-support@ucs.cam.ac.uk.

Building the module

To build the module from source and copy it into the correct Apache directory, first unpack the distribution tar file and cd into the directory it creates:

 tar zxf mod_ucam_lookupquery-<ver>.tar.gz
 cd mod_ucam_lookupquery-<ver>

then issue the following command as root:

 apxs -c -i mod_ucam_lookupquery.c

apxs may not be on root's default path and may be renamed, e.g. to apxs2, on some platforms. apxs and related files are sometimes provided by a httpd-devel, apache-devel, or apache2-devel package which will need to be installed. You will also need the OpenLDAP development files which will probably be provided by an openldap2-devel package or similar.

Version 0.90 of the module isn't as portable as one might like. Depending on your platform details (Apache version, OpenLDAP version, C compiler, etc.) you may need to make some or all of the following changes. Some, though not all, of these changes are included in the source RPM above but won't be included by default excpet whan built on RedHat systems.

  • Add, around line 26:
#include "http_request.h"  /* for ap_hook_auth_checker */
#include "apr_lib.h"       /* for apr_isspace.h */
  • At line 150, change
int error_code;

to

int error_code = 0;
  • For some older versions of OpenLDAP, Change lines 187/8 from
else if (strcmp(scope, "one") == 0) intscope = LDAP_SCOPE_ONE;
else if (strcmp(scope, "sub") == 0) intscope = LDAP_SCOPE_SUB;

to

else if (strcmp(scope, "one") == 0) intscope = LDAP_SCOPE_ONELEVEL;
else if (strcmp(scope, "sub") == 0) intscope = LDAP_SCOPE_SUBTREE;
  • Change line 575 from
regex_t *r = ap_pregcomp(p, w, REG_ICASE);

to

ap_regex_t *r = ap_pregcomp(p, w, AP_REG_ICASE);
  • Change line 829 from
while (s > fakeuser && isspace(s[-1])) s--;

to

while (s > fakeuser && apr_isspace(s[-1])) s--;

In addition you might want to change a number of instances (e.g. at lines 877, 910, and 196) of APLOG_ERR to APLOG_INFO to reduce the noise in your Apache error log.

Basic documentation

Load the module by including

 LoadModule ucam_lookupquery_module modules/mod_ucam_lookupquery.so

in the Apache configuration file, replacing 'modules/mod_ucamlookupquery.so' if necessary with the path to the the file containing the module, relative to ServerRoot if it doesn't start '/'.

To use it, include something like

 Require LookupInst CS

in a protection block (except that this example doesn't currently work - see below). See below for other configuration options.

OPTIONS
-------

LookupAuthoritative   
  Value:    on|off                 
  Default:  on

LookupHostPort        
  Value:    space-separated list   
  Default:  ldap.lookup.cam.ac.uk:389        (actually uses LDAP_PORT)
  
(1) IPv6 literal addresses can be enclosed in []. 
(2) It tries each one till one answers, one way or another.
   
LookupBase
  Value:    string
  Default:  "o=University of Cambridge,dc=cam,dc=ac,dc=uk"
  

AUTHENTICATION
--------------

LookupBindDN
  Value:    string
  Default:  unset
  
LookupPwd
  Value:    string
  Default:  unset    
  
If both of these are set, a non-anonymous LDAP bind is done, first setting up 
an encrypted (TLS) session. If either one is not set, a non-encrypted, 
anonymous bind is done.

  

REQUIREMENTS
------------

Apart from LookupQuery, all these come in pairs, LookupXxx and LookupXxxMatch.
The former do exact matches on the data, the latter do regular expression
matches. Multiple data values for a Require implement OR. Multiple Requires 
implement AND. It is not clear if this is the True Apache Way or not - I have
failed to find a definitive statement - but it seems to me that whether
multiple Requires do AND or OR should be controlled from a higher level
(compare "satisfy any" for Require+Allow) because each run of the module just
deals with a single Require and returns yes/no. There would have to be a much 
more elaborate memory scheme to implement OR at this level.

Require LookupInst[Match] name1 [name2 ...]
  Runs:     ou=people sub (uid=%u) inst
  Needs:    (a) Exactly one entry
            (b) Exactly one attribute value, must match one of the names 
  Example:  Require LookupInst CS CL 
 
Require LookupAttr[Match] comma-separated-attrnames value1 [value2 ...]
  Runs:     ou=people sub (uid=%u) comma-separated-attrnames
  Needs:    (a) Exactly one entry
            (b) At least one attribute value must match something in the list
  Example:  Require LookupAttr cn,displayName "Jon Warbrick" "Philip Hazel"
  
Require LookupParentInst[Match] name1 [name2 ...]
  Runs:     ou=people sub (uid=%u) inst  
  Needs:    (a) Exactly one entry
            (b) Exactly one attribute value, must match one of the names 
  Else:     ou=insts sub (instID=inst) parentInstID
  Needs:    (a) Exactly one entry
            (b) Exactly one attribute value, must match one of the names
  Else:     Loop up the tree till ROOT (which is checked).
  Example:  Require LookupParentInst COLL
  
Require LookupUserInGroup[Match] name1 [name2 ...]
  Runs:     ou=groups sub (uid=%u) groupID
  Needs:    (a) One or more entries
            (b) At least one of the groupIDs to match one of the names.
  Example:  Require LookupUserInGroup 100001 100656
  
Require LookupQuery baseplus scope filter
  Runs:     baseplus scope filter
  Needs:    One or more entries (no attributes are looked up)
  Example:  RequireLookupQuery ou=groups sub 
              (&(uid=%u)(groupTitle=*Computing Service*))
  
The last rather artificial example checks that the user is a member of at least
one group that has "Computing Service" in its title. To run a query on the base 
itself, the first argument can be given as an empty string in quotes.

In the filter, literal characters * ( ) \ must be escaped as follows:

  *  =>  \2A
  (  =>  \28
  )  =>  \29
  \  =>  \5C

The sequence %u is replaced by the userid. To include a literal % before a 
lower case u, use \75.

Bugs and potential enhancements

  • What about Apache 1.3?
  • Is there any way to keep a cached connection to ldap.lookup.cam.ac.uk? If so, should it be done?
  • 'Require LookupInst[Match]' expects exactly one attribute value, which won't be the case for someone affiliated to multiple institutions. To confuse matters, it reports a 'Temporary failure accessing the database' (because it gets an unexpected number of attributes). It should require at least one attribute value, which must match one of the names. As a work around, you can use something like
Require LookupQuery ou=people sub (&(uid=%u)(instID=CS))
  • The same applies (without the convenient work around) to LookupParentInst[Match]
  • There is no scope for matching against the allUid when checking group membership using a privileged account. Probably needs to run
ou=groups sub (&(uid=%u)(allUid=%u)) groupID
  • Authenticated binds force the use of TLS and require that the OpenLDAP client libraries are configured to verify the JANET certificate used by lookup. Error messages in the Apache log such as 'ldap_start_tls_s: Connect error (-11)' indicate that this isn't the case.
    • The module doesn't provide any way to customise the location of trusted CA certificates. For OpenLDAP the default location of a file listing trusted CA certificates and/or a directory containing 'OpenSSL-hashed' trusted CA certificates can be set per-user from ~/.ldaprc or system-wide from ldap.conf (perhaps in /etc/openlap or /etc/ldap) using the TLS_CACERT and TLS_CACERTDIR directives. See 'man ldap.conf'.
    • The CA certificate required is one for the 'GTE CyberTrust Global Root' - it is included by default in many Linux distributions, but not SuSE. It can be obtained from Globalsign at http://secure.globalsign.net/cacert/ct_root.pem.
    • Setting 'TLS_REQCERT allow' in ldap.conf/.ldaprc will disable all certificate checking, which is fine for testing but a BAD IDEA otherwise.
    • Versions of OpenLDAP linked against GnuTLS (rather than OpenSSL - Debian/Ubuntu in particular) don't support TLS_CACERTDIR. Further some (esp. that included with Ubuntu 8.04 'Hardy Herron') don't seem to be able to successfully verify lookup's certificate at all.