Tomcat LDAP Realm Usage/Documentation

Introduction

Implementation of Realm that works with an LDAP directory server accessed via the JAVA LDAP SDK APIs. Requires use of IPlanet/Sun Java LDAP SDK (mozilla released code). It does not require the use of a particular LDAP server just the Java SDK from Iplanet/Sun.
 

The following constraints are imposed on the data structure in the underlying directory server:

Each user that can be authenticated is represented by an individual element in the top level DirContext that is accessed via the connectionURL property. This element has the following characteristics:
          The userid attribute of this element contains the username that is being presented for authentication and is unique. If the DN can be derived from the username being presented for authentication then it is possible to locate the record immediately. This however will not always be possible and a search is needed. Both methods are supported.

          The distinguished name (lookup) can be represented by a pattern passed to an instance of MessageFormat, where the string "{0}" in the pattern is replaced by the username being presented. The search method requires an attribute in the directory to locate the username being presented.
          The element for this user contains an attribute named by the userPassword property. The value of this attribute is retrieved for use in authentication, or the user is authenticated by attempting to bind with the given password. Either method can be used, but many directory administrators do not make the userPassword attribute readable so binding as the user is the preferred method.
          Also-the cost of computing the encrypted password can be high for an application server (tomcat)  under load.
          If using the reading the password and comparing method which is not recommended the following applies:
          The value of the user password attribute is either a cleartext String, or the result of passing a cleartext String through the
          RealmBase.digest() method (using the standard digest support included in RealmBase).
          The user is considered to be authenticated if the presented credentials (after being passed through RealmBase.digest()) are
          equal to the retrieved value for the user password attribute.

Realm role membership

     Each group of users that has been assigned a particular role is represented by an individual element in the top level DirContext that is accessed via the connectionURL property. This element has the following characteristics:
          The set of all possible groups of interest can be selected by a search pattern configured by the roleSearch property.
          The roleSearch pattern optionally includes pattern replacements "{0}" for the distinguished name, and/or "{1}" for the username, of the authenticated user for which roles will be retrieved. The roleBase property can be set to the element that is the base of the search for matching roles. If not specified, the entire context will be searched.
          The roleSubtree property can be set to false if you wish to search only the current level of directory context. The default value of true requests a search of the whole subtree. The element includes an attribute (whose name is configured by the roleName property) containing the name of the role represented by this element.
          Many directory administrators also represent group membership by attaching the groups to the user's record. This eliminates the maintenance of large groups, and instead allows quick group determination. This method is also supported. The requirement here is that the attribute be present in the user's entry and contain the names of the roles. An example is given under the the setRealmRoleType method. Note that the standard <security-role-ref> element in the web application deployment descriptor allows applications to refer to roles programmatically by names other than those used in the directory server itself.
 

Quick Start
 

To set up Tomcat to use LDAPRealm, you will need to follow these steps:

1. Make sure your directory server is configured with a schema that matches the requirements listed above.
2. Configure a username and password for use by Tomcat, that has at least read only access to the information described above. (Tomcat will not write changes to the directory.) Note that if you are reading the password from the directory to compare against an entered password you must have rights to read this field in the directory. This is not a recommended method of handling authentication.
3. Place a copy of the Iplanet LDAPSDK version 4.17 or later driver inside (typically ldapjdk.jar)  inside the $CATALINA_HOME/server/lib directory (if you do not need it visible to web applications) or $CATALINA_HOME/common/lib (if it will be used both by Tomcat 4 and by your apps-recommended).
4. Place the ldaprealm.jar file in $CATALINA_HOME/server/lib
5. If using ssl to communicate with your LDAP server place the sun JSSE libraries (jsse.jar, jnet.jar, jcert.jar) in $CATALINA_HOME/common/lib (only if using jdk 1.3.x or earlier-ssl support is built in to jdk 1.4.x).
6. Set up a <Realm> element, as described below, in your $CATALINA_HOME/conf/server.xml file.
7. If using Mbeans with Tomcat version 4.1.18 or higher then locate the Listener xml entry in your server.xml file and add/modify the entry (shown in bold) as follows:
<Listener className="org.apache.catalina.mbeans.ServerLifecycleListener"
descriptors="/org/apache/catalina/realm/LDAPRealm_mbean.xml"
debug="0"/>

7. Restart Tomcat 4 if it is already running.

                                         Realm Element Attributes

To configure LDAPRealm, you will create a <Realm> element and nest it in your CATALINA_HOME/conf/server.xml file, as described above. The following attributes are supported by this implementation:

 
Attribute Description
authType The type of authentication to perform for users.  Choices here are user (for authenticate against the server), or digest (retrieve password and compare locally). It is preferred to let the ldap server validate authentication credentials using the user option. This also does not require the program to retrieve the password 
attribute and perform the calculations to build a match string.  Default value is "user".
className The fully qualified Java class name of this Realm implementation. You MUST specify the value "org.apache.catalina.realm.LDAPRealm" here.
connectionHost The connection hostname for the LDAP server. This realm implementation takes advantage of setup of an HA environment for LDAP. The host attribute can contain a single hostname or a series of hostnames seperated by spaces. The syntax for the hostname list is the following: "hostname1 hostname2 hostname3" etc.... You may also specify different ports for each hostname using the following syntax: "hostname1 hostname2:393 hostname3:400" etc.... At least one host should not have a port included as the connectionPort option is required and will be attached to that host. So in the above example the port specified in the connectionPort attribute will be attached to hostname1. Default value for this is "localhost".
connectionFailOverType If specifying multiple hostnames for HA, this attribute allows specifying how to do failover if a host is determined to be unreachable. Here are the options: 
"serial"-try each host in turn until one is available, this can take as long as 45 seconds or more depending on TCP network settings in your OS. 
"0-N" seconds-simultaneously try all hosts until one comes back (which is based on which one comes back first and mark it as the one that is active), this option will set a delay between the threads to wait before trying the next. 0 (zero) means try all at once. For an HA environment a good choice is to use a value of between 5-10 seconds for this depending on needs. Default is "serial".
connectionName The directory server distinguished username used to establish an LDAP connection, an e.g. might be "cn=admin,o=mycompany.com"
connectionPassword The directory server password used to establish a LDAP connection.
connectionPort The directory server port number to establish the LDAP connection.  Default value is "389".
connectionType This value should be either "ldap" or "ldaps" depending on standard LDAP protocol or ssl.  Default value is "ldap".


debug The level of debugging detail logged by this Realm to the associated Logger. Higher numbers generate more detailed output. If not specified, the default debugging detail level is zero (0).
digest The digest algorithm used to store passwords in non-plaintext formats. Valid values are those accepted for the algorithm name by the java.security.MessageDigest class. See Digested Passwords for more information. If not specified, passwords are stored in clear text.
poolMin Initial size of the LDAP connection pool desired. Minimum value of 1, maximum cannot be greater than poolMax below. Default is "5" initial pooled connections.
poolMax Maximum size the connection pool can grow to. Value must be equal to or greater than poolMin. Default is "20".  If you see repeated messages in the log about pool waiting for connections to free you should increase this value.
realmRoleType This value should be "user" if you wish to have realm roles determined from the user's ldap record. The value should be "list" if another record contains what roles a user is in.  For large numbers of users many directory administrators have found it better to place role membership data directly in each user's record and attach it to a multivalued attribute like memberOf.  Default value for this is "user".
roleAutoFormat This allows a role to be assigned automatically for any user that succeeds authentication. Where this is useful is for instance you want to have a role auto assigned just to allow access control within your application. Use {0} in a messageformat patter to substitute the full DN, or {1} for the userid. For instance a value here of "role{1}" would cause a automatic role to be created of "roletony" when tony authenticates in. One use I have used is to use "{1}" for allowing a small list of users in the web.xml for specific pieces of the application. Default for this is "".
roleBase The location within the directory tree to begin searches for role membership.  This only needs to be set if realmRoleType is "list".  If not set it will default to the same location as the searchRoot.  As an example this can be set to "o=mycompany.com".
roleName The name of the directory server attribute containing the role name.  For realmRoleType of user this should contain the attribute name in a user's record to determine role membership (e.g. memberOf).  For realmRoleType of "list" this should contain the attribute within the role record that gives the actual name of the role (e.g. cn).
roleSearch An LDAP search pattern for selecting roles in this Realm, following the syntax supported by the java.text.MessageFormat class. Use {0} to substitute in the distinguished name of the user you want roles for, and/or {1} to substitute in the username of the user you want roles for. An example is "uniquemember={0}" would match all records that have this to find the realmname, the realmname would be derived from the attribute specified in roleName.  This attribute should only be set when realmRoleType is "list".
roleSubtree Set to true if you want role searches to search subtrees of the element selected by roleBase. Specify false to cause only the top level element to be searched.  The default value is "true".
searchType The method to match passed values and find them in the LDAP directory. Two methods are supported: A value of "search" indicates to build a search string using the userPattern string and doing a search for the value. A value of "buildDN" indicates to build a valid DN  from the passed value by also utilizing the userPattern and trying to read the record directly. 
BuildDN is the faster method, but not all directory setups have the values necessary to locate the user record directly based just on a passed userid. For example a DN may be first,last name and contain a unique attribute of uid for the record. This kind of setup requires a search. 
If on the other hand your DN looks like this 
     uid=doe,dc=mycompany,dc=com then this will work for the buildDN strategy. The value chosen here along with the userPattern determine how records will be located within the LDAP directory. Whatever method is chosen you need to ensure that given the value passed by a user that only ONE record is eturned from the the LDAP server. If multiple records are returned then the authentication method will fail the authentication. Default value is "search" however if you can build a dn directly with a userid then use "buildDN".
searchRoot The location within the directory tree to begin searches for users.  As an example this can be set to "o=mycompany.com".
sslTrustStore Specify the full path to the truststore for the server certs CA. No value indicates trust all SSL server connections implicitly.  If you are using SSL you can store the server root CA here for validation during ssl connects.  Default value for this "" which is to not validate the root ca, but trust it automatically.
sslTrustStorePassword Specify the password for the truststore specified in sslTrustStore.  If not using a trust store then set this to "".
userPassword The name of the directory server attribute (in the user element) that contains the cleartext or digested user password (depending on the setting of the digest attribute).  If using authType of digest this value must be specified.  Also, the user that is binding to the directory, specified in connectionName must have the authority to read this attribute.  It is not recommended to use this method of authentication as it burdens the application server for tasks it should not have to perform.
userPattern An LDAP search pattern for selecting users in this Realm, following the syntax supported by the java.text.MessageFormat class. Use {0} to substitute in the distinguished name of the user you want to select.  An example is "uid={0}" if using a searchType of user.  If you need to construct the full DN based on the user value then you may need to use something of the sort "uid={0},o=mycompany.com".

Examples

Perhaps the best way to understand proper configuration of the LDAPRealm module is through some examples.  Here is a directory schema and then we will follow this with two example setups using it.

Sample realm definition:

<Realm className="org.apache.catalina.realm.LDAPRealm"
debug="0"
connectionHost="ldapserver.host.name"
authType="user"
connectionPort="389"
poolMin="10"
poolMax="20"
searchType="search"
searchRoot="o=mycompany.com"
userPattern="uid={0}"
realmRoleType="user"
roleName="uid"
roleAutoFormat="autheduser"
/>

 

The roleAutoFormat parameter means every user that authenticates will be automatically placed in a role called autheduser. This provides a very easy way to force all users to authenticate without having to setup roles for all users.