LDAP Security Realm Examples

Version 3

    This article contains some configuration examples for using LDAP with security realms either for authentication or for the loading of a users group membership information.

     

    To try and make this task a little easier the configuration is split into three distinct steps: -

    1. Connections - How to connect to the LDAP server(s).
    2. Authentication - How to search for the user being authenticated.
    3. Authorization - How to load the groups of an already authenticated user.

     

    Connections

    Before configuring the actual security realms to use LDAP the first step is to configure the connection to the LDAP server(s), these connections are defined outside of the security realm.

     

    Basic Connection

    In it's most basic form a connection would be defined with the following attributes: -

    • name - The name is used to reference the connection from other parts of the configuration.
    • url - The url of the LDAP server to connect to.
    • search-dn - The distinguished name to use when establishing a connection to the server.
    • search-credential - The password required for the distinguished name specified to establish the connection to the server.

     

        <management>
            <security-realms>
               ...
            </security-realms>
            <outbound-connections>
                <ldap name="LocalLdap" url="ldap://localhost:10389" search-dn="uid=wildfly,dc=simple,dc=wildfly,dc=org" search-credential="secret"/>
            </outbound-connections>
        </management>
    
    
    
    
    
    
    

     

    Fail Over

    If multiple servers are available then a list of URLs can be specified separated using spaces, if one server is not available the subsequent servers in the list will be used.

     

        <management>
            <security-realms>
               ...
            </security-realms>
            <outbound-connections>
                <ldap name="LocalLdap" url="ldap://localhost:10389 ldap://other:10389" search-dn="uid=wildfly,dc=simple,dc=wildfly,dc=org" search-credential="secret"/>
            </outbound-connections>
        </management>
    
    
    
    
    
    
    

    Additional Properties

    Once the issue WFLY-2214 is resolved it will be possible in WildFly 8 to define additional properties that are passed to the InitialDirContext used to connect to the LDAP server, these can be used to specify additional properties such as timeouts.

     

        <management>
            <security-realms>
               ...
            </security-realms>
            <outbound-connections>
                <ldap name="LocalLdap" url="ldap://localhost:10389" search-dn="uid=wildfly,dc=simple,dc=wildfly,dc=org" search-credential="secret">
                   <properties>
                       <property name="com.sun.jndi.ldap.connect.timeout" value="1000" />
                       <property name="com.sun.jndi.ldap.read.timeout" value="2000" />
                   </properties>
                </ldap>
            </outbound-connections>
        </management>
    
    
    
    
    
    
    

     

    In this example two properties have been set, one controls the timeout for the time it takes to establish the connection to the server, the second timeout controls how long reads from the server will block for before timing out.

     

    Note: The custom properties should not be used to set the search-dn or search-credential values, once a user is being authenticated the password they supply and the discovered distinguished name will be used to establish a connection.

     

    SSL

    When connecting to a server with SSL enabled it may be necessary to specify a trust store or key store containing the appropriate certificates or keys.  The creation of the stores is outside the scope of this article.

     

    The most basic form of SSL that may be required could be for SSL to be enabled on the LDAP server but for the client to still authenticate against the server using a search dn with a credential (username / password).  In this case a security realm needs to be defined which references a trust store which contains the servers certificate or the certificate of the certificate authority used to sign the servers certificate - this can then be referenced from the outbound LDAP connection e.g.

     

        <management>
            <security-realms>
                <security-realm name="LdapSSLRealm">
                    <authentication>
                        <truststore  path="ldap.truststore" relative-to="jboss.server.config.dir" keystore-password="truststore_password" />
                    </authentication>
                </security-realm>
            </security-realms>
            <outbound-connections>
                <ldap name="LocalLdap" url="ldaps://localhost:10389" search-dn="uid=wildfly,dc=simple,dc=wildfly,dc=org" search-credential="secret" security-realm="LdapSSLRealm" />
            </outbound-connections>
        </management>
    
    
    
    
    
    
    

     

    When reading the security realm definition it is important to keep in mind that the authentication block is about verifying the identify of the side of the connection remote to the WildFly instance, in this case that would be the remote LDAP server.

     

    SSL with Client Certificate

    The next example is where the server is going to authenticate against LDAP using it's own certificate, this is the same as the previous example but now a keystore is also added to the security realm.

     

        <management>
            <security-realms>
                <security-realm name="LdapSSLRealm">
                    <server-identities>
                        <ssl>
                            <keystore path="ldap.keystore" relative-to="jboss.server.config.dir" keystore-password="keystore_password" />
                        </ssl>
                    </server-identities>
                    <authentication>
                        <truststore  path="ldap.truststore" relative-to="jboss.server.config.dir" keystore-password="truststore_password" />
                    </authentication>
                </security-realm>
            </security-realms>
            <outbound-connections>
                <ldap name="LocalLdap" url="ldaps://localhost:10389" security-realm="LdapSSLRealm" />
            </outbound-connections>
        </management>
    
    
    
    
    
    
    

     

    The keystore is specified within the 'server identities' section of the security realm as this is the identity that the server will be using to verify itself against LDAP.  If the keystore contains multiple keys then an 'alias' attribute can be set, if the key password does not match the keystore password then a 'key-password' can also be specified.

     

    Note: A search-dn and search-credential are not specified in this final example as the key from the keystore is used instead.

     

    Authentication

    At the time a remote user attempts to connect to the server they would be prompted to enter a username and password, the LDAP configuration within a security realm validates this by first taking the users supplied username and performing a search against the LDAP directory to identify the users distinguished name, once the distinguished name is identified a connection to the LDAP server is created using this discovered distinguished name and the supplied password to verify that the password does correspond to the user.

     

    The examples in this section omit the LDAP connections as they are defined in the previous section.

     

    All LDAP authentication definitions require the following two attributes to be set: -

    • connection - this is the name of the LDAP connection to use for searching and verifying the users password.
    • base-dn - This is the distinguished name of the context that searches for the user should begin from.

     

    One additional attribute that can be set on the 'ldap' element is the 'recursive' element, that is should sub contexts also be searched for the user, by default that is disabled.

     

    Simple

     

        <security-realm name="LdapRealm">
            <authentication>
                <ldap connection="LocalLdap" base-dn="dc=people,dc=darranl,dc=jboss,dc=com">
                     <username-filter attribute="sn"/>
                </ldap>
            </authentication>
        </security-realm>
    
    
    
    
    
    

     

    In addition to the common attributes a 'username-filter' has been specified here which is used to specify that the 'sn' attribute of the LDAP entries should be searched to match against the username entered by the remote user.

     

    Custom Filter

    If a search which is a little more complex is required a filter can be specified for the search, this allows for additional attributes to be queried.  This example illustrates how authentication could be restricted to only those users with a specific memberOf attribute set.

     

        <security-realm name="AS7_Management">
            <authentication>
                <ldap connection="EC2" base-dn="CN=Users,DC=darranl,DC=jboss,DC=org">
                    <advanced-filter filter="(&amp;(sAMAccountName={0})(memberOf=CN=as7admin,CN=Users,DC=darranl,DC=jboss,DC=org))"/>
                </ldap>
            </authentication>
        </security-realm>
    
    
    
    
    
    

     

    Note: An important point to keep in mind here is that the XML needs to remain valid, in the filter where we wanted to use '&' we needed to use '&amp;

     

    Authorization / Group Loading

    The general structure for defining how to load a users groups from LDAP is: -

     

    <authorization>
        <ldap connection="...">
           <username-to-dn> <!-- OPTIONAL -->
               <!-- Only one of the following. -->
               <username-is-dn />
               <username-filter base-dn="..." recursive="..." user-dn-attribute="..." attribute="..." />
               <advanced-filter base-dn="..." recursive="..." user-dn-attribute="..." filter="..." />
            </username-to-dn>
           <group-search group-name="..." iterative="..." group-dn-attribute="..." group-name-attribute="..." >
               <!-- One of the following -->
               <group-to-principal base-dn="..." recursive="..." search-by="...">
                   <membership-filter principal-attribute="..." />
               </group-to-principal>
               <principal-to-group group-attribute="..." />
           </group-search>
        </ldap>
    </authorization>
    
    
    

    General Approach

    Within the LDAP directory it is expected that there are entries for the user accounts and that there are entries for the groups, these are then cross referenced by the use of attributes.  The attributes used to cross reference between the two could be a reference from the user account over to the group entry, or an attribute on the group referencing the users that are members of the group - in some servers both forms of cross reference exist.

     

    It is also common that a user would be authenticating against the server using a simple username, when it comes to searching for the group membership information depending on the directory server in use searches could be performed using this simple name or it could be performed using the distinguished name of the users entry in the directory.

     

    The authentication step of a user connecting to the server would always happen first, only once it has been decided that the user is successfully authenticated does the server move onto loading a users groups.  As the authentication step and the authorization step both use a connection to the LDAP server the realm contains an optimisation that any connection used for authentication will be re-used for the group loading step.  As will be shown within the configuration steps below it is possible to define rules within the authorization section to convert a users simple username to their distinguished name, this is potentially duplicating a search that would have occurred during the authentication step so if a username to distinguished name search has already been performed the result of that search is cached and re-used without requiring a repeat.

     

    username-to-dn

    As mentioned above there may sometimes be a need to define within the authorization configuration how to map from the username supplied by the user being authenticated to the distinguished name of their entry within the LDAP directory.  The username-to-dn element is how this is defined, this element is only required if both of the following are true: -

    • The authentication step was not against LDAP.
    • The group searching is using the distinguished name during the searching.

    Do try and keep the first bullet point in mind, as you read the examples below this will feel as though the authentication configuration is being duplicated and it is true that it is - if you are only using LDAP for authentication this is not required as the distinguished name will be obtained during authentication.

     

    1:1 username-to-dn

    This is the most basic form of the configuration and is used to specify that the username entered by the remote user is actually the users distinguished name.

     

    <username-to-dn>
       <username-is-dn />
    </username-to-dn>
    
    
    

     

    As this is defining a 1:1 mapping there is no additional configuration possible.

     

    username-filter

    The next option is very similar to the simple option described above for the authentication step, quite simply an attribute is specified that is searched for a match against the supplied username.

     

    <username-to-dn>
        <username-filter base-dn="dc=people,dc=darranl,dc=jboss,dc=com" recursive="false" attribute="sn" user-dn-attribute="dn" />
    </username-to-dn>
    
    

     

    The attributes that can be set here are: -

    • base-dn - The distinguished name of the context to begin the search.
    • recursive (default false) - Should the search extend to sub contexts?
    • attribute (default uid) - The attribute of the users entry to try and match against the supplied username.
    • user-dn-attribute (default dn) - The attribute to read to obtain the users distinguished name.

     

    advanced-filter

    The final option is to specify an advanced filter, as in the authentication section this is an opportunity to use a custom filter to locate the users distinguished name.

     

    <username-to-dn>
        <advanced-filter base-dn="dc=people,dc=darranl,dc=jboss,dc=com" recursive="false" filter="sAMAccountName={0}" user-dn-attribute="dn" />
    </username-to-dn>
    
    

     

    For the attributes that match the ones in the 'username-filter' the meaning and default values are the same so I will not list them here again, this leaves one new attribute: -

    • filter - Custom filter used to search for a users entry where the username will be substituted in the {0} place holder.

    Note: The XML must remain valid after the filter is defined so if any special characters are used such as '&' ensure the propper form is used e.g. &amp;

     

    The Group Search

    As described above there are two different styles that can be used when searching for group membership information, the first style is where the user's entry contains an attribute that references the groups the user is a member of - the second style is where the group contains an attribute referencing the user's entry.

     

    If there is a choice of which style to use then it would be recommended that the configuration for a user's entry referencing the group is used, this is because for this style group information can be loaded by reading attributes of known distinguished names without having to perform any searches - the opposite approach requires extensive searches to identify the groups that reference the user.

     

    Before describing the configuration here are a couple of LDIF examples to illustrate this.

     

    Principal to Group - LDIF example.

     

    dn: uid=TestUserOne,ou=users,dc=principal-to-group,dc=wildfly,dc=org
    objectClass: extensibleObject
    objectClass: top
    objectClass: groupMember
    objectClass: inetOrgPerson
    objectClass: uidObject
    objectClass: person
    objectClass: organizationalPerson
    cn: Test User One
    sn: Test User One
    uid: TestUserOne
    distinguishedName: uid=TestUserOne,ou=users,dc=principal-to-group,dc=wildfly,dc=org
    memberOf: uid=GroupOne,ou=groups,dc=principal-to-group,dc=wildfly,dc=org
    memberOf: uid=Slashy/Group,ou=groups,dc=principal-to-group,dc=wildfly,dc=org
    userPassword:: e1NTSEF9WFpURzhLVjc4WVZBQUJNbEI3Ym96UVAva0RTNlFNWUpLOTdTMUE9PQ==
    
    dn: uid=GroupOne,ou=groups,dc=principal-to-group,dc=wildfly,dc=org
    objectClass: extensibleObject
    objectClass: top
    objectClass: groupMember
    objectClass: group
    objectClass: uidObject
    uid: GroupOne
    distinguishedName: uid=GroupOne,ou=groups,dc=principal-to-group,dc=wildfly,dc=org
    memberOf: uid=GroupFive,ou=subgroups,ou=groups,dc=principal-to-group,dc=wildfly,dc=org
    
    dn: uid=GroupFive,ou=subgroups,ou=groups,dc=principal-to-group,dc=wildfly,dc=org
    objectClass: extensibleObject
    objectClass: top
    objectClass: groupMember
    objectClass: group
    objectClass: uidObject
    uid: GroupFive
    distinguishedName: uid=GroupFive,ou=subgroups,ou=groups,dc=principal-to-group,dc=wildfly,dc=org
    
    

     

    This example illustrates where we have a user 'TestUserOne' who is a member of 'GroupOne', 'GroupOne' is then in-turn a member of 'GroupFive'.  The group membership is shown by the use of a 'memberOf' attribute which is set to the distinguished name of the group the user (or group) is a member of.

     

    It is not shown here but a user could potentially have multiple 'memberOf' attributes set, one for each group the user is directly a member of.

     

    Group to Principal - LDF Example

     

    dn: uid=TestUserOne,ou=users,dc=group-to-principal,dc=wildfly,dc=org
    objectClass: top
    objectClass: inetOrgPerson
    objectClass: uidObject
    objectClass: person
    objectClass: organizationalPerson
    cn: Test User One
    sn: Test User One
    uid: TestUserOne
    userPassword:: e1NTSEF9SjR0OTRDR1ltaHc1VVZQOEJvbXhUYjl1dkFVd1lQTmRLSEdzaWc9PQ==
    
    dn: uid=GroupOne,ou=groups,dc=group-to-principal,dc=wildfly,dc=org
    objectClass: top
    objectClass: groupOfUniqueNames
    objectClass: uidObject
    cn: Group One
    uid: GroupOne
    uniqueMember: uid=TestUserOne,ou=users,dc=group-to-principal,dc=wildfly,dc=org
    
    dn: uid=GroupFive,ou=subgroups,ou=groups,dc=group-to-principal,dc=wildfly,dc=org
    objectClass: top
    objectClass: groupOfUniqueNames
    objectClass: uidObject
    cn: Group Five
    uid: GroupFive
    uniqueMember: uid=TestUserFive,ou=users,dc=group-to-principal,dc=wildfly,dc=org
    uniqueMember: uid=GroupOne,ou=groups,dc=group-to-principal,dc=wildfly,dc=org
    
    

     

    This example shows the same user 'TestUserOne' who is a member of 'GroupOne' which is in turn a member of 'GroupFive' - however in this case it is an attribute 'uniqueMember' from the group to the user being used for the cross reference.

     

    Again the attribute used for the group membership cross reference can be repeated, if you look at 'GroupFive' there is also a reference to another user 'TestUserFive' which is not shown here.

     

    General Group Searching

    Almost there   Before looking at the examples for the two approaches shown above we first need to define the attributes common to both of these.

     

    <group-search group-name="..." iterative="..." group-dn-attribute="..." group-name-attribute="..." >
        ...
    </group-search>
    

     

    • group-name (default SIMPLE) - This attribute is used to specify the form that should be used for the group name returned as the list of groups the user is a member of, this can either be the simple form of the group name or the groups distinguished name, if the distinguished name is required this attribute can be set to 'DISTINGUISHED_NAME'

     

    • iterative (default false) - This attribute is used to indicate if after identifying the groups a user is a member of we should also iteratively search based on the groups to identify which groups the groups are a member of.  If iterative searching is enabled we keep going until either we reach a group that is not a member if any other groups or a cycle is detected.

     

    Cyclic group membership is not a problem, we continually check if we are searching a group that has already been searched and stop that branch of the iteration.

     

    For iterative searching to work the group entries need to look the same as user entries, that is the same approach used to identify the groups a user is a member of is then used to identify the groups the group is a member of.  This would not be possible if say once we are talking about group to group membership the name of the attribute used for the cross reference changes or if the direction of the reference changes.

     

    • group-dn-attribute (default dn) - On an entry for a group which attribute is it's distinguished name.
    • group-name-attribute (default uid) - On an entry for a group which attribute is it's simple name.

     

    Principal to Group Example Configuration

    Based on the example LDIF from above here is an example configuration iteratively loading a users groups where the attribute used to cross reference is the 'memberOf' attribute on the user: -

     

    <authorization>
        <ldap connection="LocalLdap">
            <username-to-dn>
                <username-filter base-dn="ou=users,dc=principal-to-group,dc=wildfly,dc=org" recursive="false" attribute="uid" user-dn-attribute="dn" />
            </username-to-dn>
            <group-search group-name="SIMPLE" iterative="true" group-dn-attribute="dn" group-name-attribute="uid">
                <principal-to-group group-attribute="memberOf" />
            </group-search>
        </ldap>
    </authorization>
    

     

    This example does show the 'username-to-dn' definition although that may be optional depending on the authentication definition.  As you may notice many of these attributes are using the default values so could be omitted from the configuration, in fact when WildFly persists the configuration these attributes will be removed as they match the defaults - they are just shown here to save cross referencing the defaults.

     

    The most important aspect of this configuration is that the 'principal-to-group' element has been added with a single attribute: -

    • group-attribute (default memberOf) - The name of the attribute on the user entry that matches the distinguished name of the group the user is a member of.

     

    Group to Principal Example Configuration

    This example shows an iterative search for the group to principal LDIF example shown above.

     

    <authorization>
        <ldap connection="LocalLdap">
            <username-to-dn>
                <username-filter base-dn="ou=users,dc=group-to-principal,dc=wildfly,dc=org" recursive="false" attribute="uid" user-dn-attribute="dn" />
            </username-to-dn>
            <group-search group-name="SIMPLE" iterative="true" group-dn-attribute="dn" group-name-attribute="uid">
                <group-to-principal base-dn="ou=groups,dc=group-to-principal,dc=wildfly,dc=org" recursive="true" search-by="DISTINGUISHED_NAME">
                    <membership-filter principal-attribute="uniqueMember" />
                </group-to-principal>
            </group-search>
        </ldap>
    </authorization>
    

     

    Here an element 'group-to-principal' is added, this element is used to define how searches for groups that reference the user entry will be performed, the following attributes are set: -

    • base-dn - The distinguished name of the context to use to begin the search.
    • recursive (default false) - Should sub contexts also be searched?
    • search-by (default DISTINGUISHED_NAME) - The form of the role name used in searches, could also be set to SIMPLE.

    Within the 'group-to-principal' element there is a 'membership-filter' element to define the cross reference.

    • principal-attribute (default member) - The name of the attribute on the group entry that references the user entry.

     

     

    Comments

     

    Do feel free to comment on this article below, however if your comment is more about assistance getting your own configuration working please create a new forum discussion showing your current configuration, an extract of the LDIF from your own directory and if applicable any errors you may be seeing.