KNOX-242-support basedn, search attribute based LDAP authentication
Summary
At present (as of Knox-0.4.0), LDAP authentication in Knox infers the baseDN of the user using UserDNTemplate and the login id given by client user.
This does not work in enterprises where users could belong to multiple branches of LDAP tree.
This JIRA proposes adding LDAP search to find the bind DN.
Description
Problem with LDAP Authentication in Knox at present (as of Knox 0.4.0)
Knox uses Apache Shiro for doing authentication of clients using LDAP userId and password.
Knox does this using either JNDILdapRealm bundled with Apache Shiro or KnoxLdapRealm implemented by Apache Knox.
Apache Knox KnoxLdapRealm is a subclass of Apache Shiro JNDILdapRealm, adding group lookup feature.
JNDILdapRealm and KnoxLdapRealm have a configuration parameter ldapRealm.userDnTemplate.
Typical value of userDNTemplate would look like uid={0},ou=people,dc=hadoop,dc=apache,dc=org.
To compute bind DN of the client, we swap the place holder {0} with login id provided by the client.
For example, if the login id provided by the client is "guest', the computed bind DN would be uid=guest,ou=people,dc=hadoop,dc=apache,dc=org.
This keeps configuration simple.
However, this does not work if users belong to different branches of LDAP DIT.
For example, if there are some users under ou=people,dc=hadoop,dc=apache,dc=org and some users under ou=contractors,dc=hadoop,dc=apache,dc=org, we can not come up with userDnTemplate that would work for all the users.
Proposed Enhancement
We would add following additional optional configuration parameters: ldapRealm.userSearchAttributeName, ldapRealm.userObjectClass
ldapRealm.userSearchAttributeName
This is an optional parameter.
There is no default value for this parameter. If a value is specified for this parameter, we would find bind DN by doing ldapsearch.
If no value is specified for this parameter, we compute bind DN using userDnTemplate as we are already doing.
ldapRealm.userObjectClass
This is an optional parameter with default value of "person".
Example search filter to find the client bind DN
Assuming,
ldapRealm.userSearchAttributeName=uid
ldapRealm.userObjectClass=person
client specified login id = "guest"
LDAP Filter for doing a search to find the bind DN would be
(&(uid=guest)(objectclass=person))
Bind Credentials for the search to find the client bind DN
System bind DN to search for client bind DN is specified by the property ldapRealm.contextFactory.systemUsername.
Password for system bind DN is specified by the property ldapRealm.contextFactory.systemPassword.
Value of ldapRealm.contextFactory.systemPassword can reference password alias using the format ${ALIAS=ldcSystemPassword}.
If the password alias reference is used, password is obtained from credential store at runtime.
Search Scope for the search to find the client bind DN
Search scope for search to find the client bind DN can be specified with parameter ldapRealm.userSearchBase.
If no value is specified for ldapRealm.userSearchBase, it would default to the value of ldapRealm.searchBase.
Sample Shiro Section of Topology file illustrating KnoxLdapRealm parameters
New parameters added by this enhancement are shown in bold
<provider>
<role>authentication</role>
<name>ShiroProvider</name>
<enabled>true</enabled>
<param>
<!--
session timeout in minutes, this is really idle timeout,
defaults to 30mins, if the property value is not defined,,
current client authentication would expire if client idles contiuosly for more than this value
-->
<name>sessionTimeout</name>
<value>30</value>
</param>
<param>
<name>main.ldapRealm</name>
<value>org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm</value>
</param>
<param>
<name>main.ldapRealm.contextFactory.url</name>
<value>ldap://localhost:33389</value>
</param>
<!-- optional, default value: simple -->
<param>
<name>main.ldapRealm.contextFactory.authenticationMechanism</name>
<value>simple</value>
</param>
<!-- optional, default value: null -->
<param>
<name>main.ldapRealm.contextFactory.systemUsername</name>
<value>uid=guest,ou=people,dc=hadoop,dc=apache,dc=org</value>
</param>
<!-- optional, default value: null -->
<param>
<name>main.ldapRealm.contextFactory.systemPassword</name>
<!--
<value>${ALIAS=ldcSystemPassword}</value>
-->
<value>guest-password</value>
</param>
<param>
<name>main.ldapRealm.searchBase</name>
<value>dc=hadoop,dc=apache,dc=org</value>
</param>
<!-- optional, default value: empty -->
<param>
<name>main.ldapRealm.userDnTemplate</name>
<value>uid={0},ou=people,dc=hadoop,dc=apache,dc=org</value>
</param>
<!-- optional, default value: null -->
<param>
<name>main.ldapRealm.userSearchAttributeName</name>
<value>uid</value>
</param>
<!-- optional, default value: person -->
<param>
<name>main.ldapRealm.userObjectClass</name>
<value>person</value>
</param>
<!-- optional, default value: value of main.ldapRealm.searchBasae -->
<param>
<name>main.ldapRealm.userSearchBase</name>
<value>ou=people,dc=hadoop,dc=apache,dc=org</value>
</param>
<!-- optional, default value: false -->
<param>
<name>main.ldapRealm.authorizationEnabled</name>
<value>true</value>
</param>
<!-- optional, default value: uid={0} -->
<param>
<name>main.ldapRealm.memberAttributeValueTemplate</name>
<value>uid={0},ou=people,dc=hadoop,dc=apache,dc=org</value>
</param>
<!-- optional, default value: value of main.ldapRealm.searchBasae -->
<param>
<name>main.ldapRealm.groupSearchBase</name>
<value>ou=people,dc=hadoop,dc=apache,dc=org</value>
</param>
<!-- optional, default value: groupOfNames -->
<param>
<name>main.ldapRealm.groupObjectClass</name>
<!--
<value>groupOfNames</value>
-->
<value>groupOfUrls</value>
</param>
<!-- optional, default value: member -->
<param>
<name>main.ldapRealm.memberAttribute</name>
<!--
<value>member</value>
-->
<value>memberUrl</value>
</param>
<!-- optional, default value: cn -->
<param>
<name>main.ldapRealm.groupIdAttribute</name>
<value>cn</value>
</param>
<param>
<name>urls./**</name>
<value>authcBasic</value>
</param>
</provider>