Sandor Molnar created KNOX-3558:
-----------------------------------
Summary: LDAP proxy role memberOf values are dropped by the
KnoxIDF LdapUserParamsProvider, so /token fails on a role listing call
Key: KNOX-3558
URL: https://issues.apache.org/jira/browse/KNOX-3558
Project: Apache Knox
Issue Type: Bug
Components: JWT, KnoxIDF
Reporter: Sandor Molnar
Assignee: Sandor Molnar
Fix For: 3.1.0
h3. Problem
The KnoxIDF token endpoint, with the {{roles}} scope, returns 500 ("Failed to
fetch user parameters for <user>"). This happens when
{{LdapUserParamsProvider}} points at the Knox LDAP proxy, the REST roles lookup
is enabled, and the user has no backend LDAP groups.
# {{LdapUserParamsProvider.getParamsFor}} reads the user entry, asking for
{{memberOf}}.
#* {{LDAPRolesLookupInterceptor.handleSearch}} gets the user's roles from
{{POST .../roles/lookup}} (HTTP 200, 5 roles) and writes them into {{memberOf}}.
#* The user has no backend groups, so there is no template DN, and
{{addRoleAttribute}} writes each role as a bare {{cn=<role>}} with no parent DN.
# {{rolesFromMemberOf}} only accepts direct {{cn=}} children of
{{ou=groups,<baseDn>}}, so it drops every role.
# The provider then falls back to {{fetchRoles}}: a one-level search of
{{ou=groups,<baseDn>}} with {{(objectClass=groupOfNames)}}, filtering
{{member}} on the client side.
#* {{isAllGroupsSearch}} treats this as "list all roles" and calls
{{listRoles}}, i.e. {{GET .../roles?offset=0}}. That pages through every role
in the system to enrich one user.
#* The roles backend refuses the listing (HTTP 403). The interceptor throws
"Failed to retrieve roles from roles lookup service.", the LDAP search fails,
and {{/token}} returns 500.
{noformat}
RestApiLdapRolesLookup - Sending role lookup request to
http://awc-auth/api/v1/auth/roles/lookup: {"user_id":"admin","groups":[]}
RestApiLdapRolesLookup - Received role lookup response ...: HTTP 200, body:
{"user_id":"admin","roles":[{"scope":"global","name":"temporary-local-admin"},
...]}
RestApiLdapRolesReverseLookup - Sending role list request to
http://awc-auth/api/v1/auth/roles?offset=0
RestApiLdapRolesReverseLookup - Role list request ... failed with HTTP 403
Caused by: java.lang.RuntimeException: Failed to fetch user parameters for admin
at
org.apache.knox.gateway.service.knoxidf.userparams.LdapUserParamsProvider.getParamsFor(LdapUserParamsProvider.java:170)
{noformat}
When the user does have backend groups, each role DN is the first backend
group's DN with its {{cn}} renamed, e.g. {{cn=<role>,ou=<backend OU>,...}}.
That DN doesn't match the role entries the proxy publishes for an all-groups
search, or the DN {{ReverseRoleLookupMappingVisitor}} expects in {{memberOf=}}
filters. If it isn't directly under {{ou=groups,<baseDn>}}, the provider drops
it too.
h3. Proposed fix
*LDAP proxy ({{LDAPRolesLookupInterceptor}})*
* Always write each role in {{memberOf}} as {{cn=<role>,ou=groups,<baseDn>}}
(i.e. {{createGroupDnFromName(role)}}), instead of the bare {{cn=<role>}} or a
renamed backend group DN.
* This is the same DN that {{handleAllGroupsSearch}} uses for role entries and
that the reverse lookup expects. A user's {{memberOf}} then names entries the
proxy actually serves, and the value can be fed back into a {{memberOf=}}
filter.
* Not configurable. All three are derived from {{ou=groups,<baseDn>}}, so a
separate setting for {{memberOf}} would only let them disagree. If a different
groups base is ever needed, make that base itself configurable.
* The role is still the {{cn}}, so clients that take the group name from
{{memberOf}}'s {{cn}} (e.g. Hadoop {{LdapGroupsMapping}}) are not affected.
*KnoxIDF ({{LdapUserParamsProvider}})*
* New topology param {{user.params.provider.ldap.groupSearchBase}}, defaulting
to {{ou=groups,<baseDn>}}, which is the DN the proxy now writes.
** Direct {{cn=}} children of it in {{memberOf}} are taken as roles. This lets
directories whose groups live elsewhere (e.g. {{ou=Groups}} under another OU)
work without changing {{baseDn}}.
** The {{fetchRoles}} fallback searches this base too, instead of the hardcoded
{{"ou=groups," + baseDn}}.
* {{fetchRoles}} searches only the user's own groups with
{{(&(objectClass=groupOfNames)(member=\{0\}))}}, passing the user DN as a
filter argument so JNDI escapes it. It no longer lists every group and filters
on the client side.
** On the proxy this is not an all-groups search, so it no longer turns into
{{listRoles}}. A user with no roles gets an empty {{roles}} claim instead of a
failed {{/token}}.
** On a plain LDAP server it returns the same groups without reading all of
them.
h3. Tests
* {{LDAPRolesLookupInterceptorTest}}:
** a user without groups gets {{memberOf: cn=<role>,ou=groups,<baseDn>}};
** a user with groups gets the same form, not a renamed backend group DN.
* {{LdapUserParamsProviderTest}}:
** the default {{groupSearchBase}} accepts the proxy's {{memberOf}} values
without a fallback search;
** a custom {{groupSearchBase}} accepts its own children and rejects
{{ou=groups,<baseDn>}};
** the fallback uses the per-user {{member}} filter on {{groupSearchBase}}.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)