Authenticating Users for LDAP and Active Directory

7 minute read

CloudBees CD uses account information from multiple sources. In most cases, the primary account information source is an external LDAP or Active Directory repository: both user and group information is retrieved from the repository. Local users and groups are defined within CloudBees CD.

Use the following LDAP information and examples to enter information in your LDAP template. If you are using the Active Directory template, you may still use the LDAP information as reference—the templates are similar with only a slight difference in the Active Directory template.

Configuring LDAP

A number of options need to be customized for any LDAP configuration. The following sample configuration shows how we have our own LDAP configuration set up. After the sample, see a list of properties with a description.

Sample LDAP Configuration

<bean id="LdapDirectoryProvider" class="com.electriccloud.security.ldap.LdapDirectoryProviderImpl"> <property name="providerName" value="LDAP"/> <property name="url" value="ldap://dir.electric-cloud.com/dc=electric-cloud,dc=com"/> <property name="managerDn" value="uid=JohnDoe,ou=People,=electric-cloud,dc=com"/> <property name="managerPassword" value="****"/> <property name="userBase" value="ou=People"/> <property name="userSearchFilter" value="uid={0}"/> <property name="userNameAttribute" value="uid"/> <property name="fullUserNameAttribute" value="gecos"/> <property name="emailAttribute" value="mail"/> <property name="groupBase" value="ou=Group"/> <property name="groupMemberFilter" value="(|(member={0})(memberUid={1}))"/> <property name="groupMemberAttributes" value="member,memberUid"/> <property name="groupSearchFilter" value="(|(groupOfNames)(objectClass=posixGroup))"/> <property name="groupNameAttribute" value="cn"/> </bean>

The following properties configure LDAP mapping:

emailAttribute —(optional) The attribute in a user record that contains the user’s email address. If the attribute is not specified, the account name and domain name are concatenated to form an email address.

fullUserNameAttribute —(optional) The attribute in a user record that contains the user’s full name (first and last) for display in the UI. If this attribute is not specified or the resulting value is empty, the user’s account name is used instead.

managerDn —(optional) The DN of a user who has read-only access to LDAP user and group directories. If this property is not specified, the server attempts to connect as an unauthenticated user. Not all servers allow anonymous read-only access.

This user does not need to be an admin user with modify privileges.

managerPassword —(optional) If the managerDn property is set, this password is used to authenticate the manager user.

providerName —This human readable name will be displayed in the user interface to identify users and groups that come from this provider.

userBase —This string is prepended to the basedn to construct the directory DN that contains user records.

userNameAttribute —The attribute in a user record that contains the user’s account name.

userSearchFilter —This LDAP query is performed in the context of the user directory to search for a user by account name. The string {0} is replaced with the user’s login ID. Typically, the query compares a user record attribute with the substituted user login ID.

url —The LDAP server URL is in the form protocol://host:port/basedn. Protocol is either ldap or ldaps (for secure LDAP). The port is implied by the protocol, but can be overridden if it is not at the default location (389 for ldap, 636 for ldaps). The basedn is the path to the top level directory that contains users and groups at this site. This is typically the domain name where each part is listed with a dc= and separated by commas.

Spaces in the basedn must be URL encoded ( %20 ).

In addition to user information, the LDAP server can be queried for group information. This query is optional because the local group mechanism can refer to LDAP users and local users. However, the following elements can be used to tell the server how to map groups in LDAP

groupBase —(optional) This string is prepended to the basedn to construct the directory DN that contains group records.

groupMemberAttributes —(optional) A comma separated attribute names list that identifies a group member. Most LDAP configurations only specify a single value, but if there is a mixture of POSIX and LDAP style groups in the directory, multiple attributes might be required.

groupMemberFilter —(optional) This LDAP query is performed in the groups directory context to identify groups that contain a specific user as a member. There are two common forms of group record in LDAP directories: POSIX style groups where members are identified by account name, and groupOfNames or uniqueGroupOfNames records where members are identified by the full user DN. Both forms are supported, so the query is passed two parameters: {0} is replaced with the full user record DN, and "{1} is replaced with the user’s account name.

groupNameAttribute —(optional) The group record attribute that contains the name of the group.

groupSearchFilter —(optional) This LDAP query is performed in the context of the groups directory to enumerate group records.

Determining LDAP Mapping

A typical POSIX user record in LDAP looks similar the example below. To set up a mapping for this record, it is necessary to identify various record components. First, identify the path in the directory that contains user records. In this example, the build user has a distinguished name (dn) of uid=build,ou=People,dc=mycompany,dc=com. This name uniquely identifies the build user account and this path splits into three parts:

base dn: dc=mycompany,dc=com

user base: ou=People

user element: uid=build

The baseDn is the parent of the directory that contains users. This value should be combined with the protocol and server to form the URL. In this case, the URL is ldaps://dir/dc=mycompany,dc=com.

Next, the userBase is the portion of the path that identifies the directory containing all user account records. This value is used directly as the userBase configuration element.

The remaining portion identifies the user without the People directory: uid=build. The user name is replaced in this value with the string {0} to form the userSearchFilter: uid={0}. This query allows the server to search for a user’s account name by looking for a record with a matching uid attribute.

The final mapping step is to identify user record attributes that hold the account name, full user name, and (optionally) the user’s email address. In this example, the account name is uid (identified earlier), the full user name attribute is gecos, and there is no email attribute.

At this point, the server is able to authenticate a user, look up a user by name, and determine the user’s full name. For many installations this is sufficient.

Sample LDAP User Record

# build, People, electric-cloud.comdn: uid=jdoe, ou=People, dc=mycompany, dc=comlogin Shell: /bin/bashuid Number: 508gid Number: 508object Class: accountobject Class: posixAccountobject Class: topobject Class: shadow Accountuid: jdoegec os: John Doe cn: Johnhome Directory: /net/filer/homes/build

Also, you can configure the server to look for LDAP groups that refer to user accounts. A typical group record is shown below. Like a user account, an LDAP group has a distinguished name with a baseDn, a group base, and a group element. In this case, the basedn is still dc=mycompany,dc=com. The groupBase configuration element is ou=Group, and the group name is cn=build_users.

The server needs to identify records in the directory that correspond to groups—it does this by applying the groupMemberFilter to the records in the groupBase directory. In this case, group records are members of the posixAccount object class, so the filter can be set to objectClass=posixGroup. To display a group by its name, the server needs to know which attribute represents the group name. In this case, set the groupNameAttribute to cn.

Finally, the server needs a filter to determine which accounts belong to the group and the attribute name that represents a single group member. Group membership can be identified in one of two ways. Either the members are listed by account name, or by their LDAP distinguished name. In this case, POSIX group membership is determined by account name, so set the groupMemberAttributes property to memberUid, and set the groupMemberFilter to memberUid={1}.

Sample LDAP Group Record

# build_users, Group, myexample.com dn: cn=build_users,ou=Groups,dc=mycompany,dc=comobject Class: posixGroupobject Class: topgidNumber: 100member Uid: jdoe member Uid: mary cn: build_users

Sample Active Directory Configuration File

The following XML file defines parameters needed to connect to an Active Directory server and the query to use for looking up user information.

<beans xmlns="\https://www.springframework.org/schema/beans" xmlns: xmlns:tx xmlns:aop xsi:schemaLocation \https://www.springframework.org/schema/beans/spring-beans-2.0.xsd \https://www.springframework.org/schema/tx \https://www.springframework.org/schema/tx/spring-tx-2.0.xsd \https://www.springframework.org/schema/aop \https://www.springframework.org/schema/aop/spring-aop-2.0.xsd" default-lazy-init="true" <bean id="ADDirectoryProvider" class="com.electriccloud.security.ldap.ActiveDirectoryProviderImpl"> <property name="providerName" value="ActiveDirectory" /> <!-- START OF CUSTOMIZATIONS --> <!-- URL to AD server of the form: "r;ldap://host:port/base_dn" --> <property name="url" value="ldap://server:389/dc=company,dc=com" /> <!-- Required credentials for an account that has read-only access to the server --> <property name="managerDn" value="cn=myuser,cn=Users,dc=company,dc=com" /> <property name="managerPassword" value="mypw" /> <property name="userBase" value="cn=Users" /> <property name="userSearchFilter" value="(&(sAMAccountName={0}) (objectClass=user))" /> <property name="userNameAttribute" value="sAMAccountName" /> <property name="fullUserNameAttribute" value="name" /> <!-- Optional group configuration. This is only needed if you intend --> <!-- to manage groups externally via AD. --> <!-- <property name="groupBase" value="value="/>--> <!-- <property name="groupMemberFilter" value="member={0}"/> --> <!-- <property name="groupMemberAttributes" value="member"/>--> <!-- <property name="groupSearchFilter" value="(objectClass=group)"/>--> <!-- <property name="groupNameAttribute" value="cn"/>--> <!-- Max number of results to get back in one query. This must be --> <!-- no greater than the AD server's setting, which is typically 1000. --> <property name="pageSize" value="500" /> <!-- END OF CUSTOMIZATIONS --> </bean> </beans>`

LDAP Group Hierarchy

The LDAP or Active Directory provider can now be configured to recognize the nested group hierarchy in the LDAP or Active Directory server in the following use cases.

Access Control for LDAP Group Hierarchies

When Recursively Traverse Group Hierarchy is selected (enabled) for the directory provider and access control is set up for a given LDAP group in CloudBees CD, the users belonging to any nested groups in LDAP will automatically inherit the access control rules defined for the parent groups. For more information about Recursively Traverse Group Hierarchy, see To create a new Active Directory provider and To create a new LDAP directory provider for the directory provider configuration details.

In this LDAP hierarchy:

  • Group1

    • User11

    • Group11

      • User111

      • User112

      • Group111

    • Group12

      • User121

      • User122

These access control rules have been set up with Recursively Traverse Group Hierarchy enabled:

  • Group1: Project:Default Read→Allow, Modify→Allow, Execute→Deny

  • Group12: Project:Default Read→Allow, Modify→Deny, Execute→Allow

Because the users in Group111 and Group11 are also considered to be members of Group1, the access control rules defined for Group1 also apply to them:

Project:Default Read→Allow, Modify→Allow, Execute→Deny

Because the users in Group12 are also considered members of both Group1 and Group12, the access control rules defined in both groups may apply to the users in Group 12. However, CloudBees CD does not support the inherent group hierarchy. Thus, CloudBees CD does not apply special precedence logic based on the group hierarchy, and the same precedence logic that is currently applied for a user belonging to two or more groups will apply in this situation, giving Deny a higher precedence:

Project:Default Read→Allow, Modify→Deny, Execute→Deny

Manual Task Assignees and Approvers

If Recursively Traverse Group Hierarchy and Include Nested Group Users as Approvers are both enabled for the directory provider, users in nested LDAP groups are allowed to complete or approve a manual task when a parent LDAP group is assigned as an assignee or an approver for the task. For more information about these membership options, see To create a new Active Directory provider and To create a new LDAP directory provider for the directory provider configuration details.

In the following LDAP hierarchy with both Recursively Traverse Group Hierarchy and Include Nested Group Users as Approvers enabled:

  • Group1

    • User11

    • Group11

      • User111

    • Group12

      • User121

When Group1 is assigned as an approver for a manual task, User11, User111, and User 121 can approve the task.

Notifications

If Recursively Traverse Group Hierarchy and Include Nested Group Users in Notifications are both enabled for the directory provider, users in nested LDAP groups will be included when sending notifications to a parent LDAP group. For more information about these membership options,see To create a new Active Directory provider and To create a new LDAP directory provider for the directory provider configuration details.

In the following LDAP hierarchy with both Recursively Traverse Group Hierarchy and Include Nested Group Users in Notifications enabled:

  • Group1

    • User11

    • Group11

      • User111

    • Group12

      • User121

When notifications are set up to be sent to Group1, they are sent to User11, User111, and User 121.