LDAP Group Sync by Filter doesn't sanitize DNs
Zendesk: https://gitlab.zendesk.com/agent/tickets/107196
In the original LDAP group sync (sync via group CN) we sanitize DNs, which also downcases the DN. This helps to match values in Ruby since Ruby is case-sensitive while LDAP is not - we can't rely on the case given to us by LDAP. When checking memberships later on in sync we also downcase the user's stored DN from extern_uid
to ensure we're able to match successfully.
The newer sync by LDAP filter feature does not sanitize DNs. In this customer's case the LDAP server is sending mixed-case DNs back. We're unable to compare those values successfully which results in GitLab 're-adding' the user to the group each time. Although the user shouldn't notice any problems since they're never actually removed from the group, the customer noticed the 'Given access' date updating constantly. This also causes a lot of unnecessary database writes.
The bug can be traced from this point in LDAP sync code: https://gitlab.com/gitlab-org/gitlab-ee/blob/b37558cdd70e025355ea7626898f56c0f91fb1a7/ee/lib/ee/gitlab/auth/ldap/sync/group.rb#L110
def get_member_dns(group_link)
group_link.cn ? dns_for_group_cn(group_link.cn) : UserFilter.filter(@proxy, group_link.filter)
end
If we follow dns_for_group_cn
we see that the DN is parsed (and sanitized) at https://gitlab.com/gitlab-org/gitlab-ee/blob/b37558cdd70e025355ea7626898f56c0f91fb1a7/ee/lib/ee/gitlab/auth/ldap/sync/proxy.rb#L73. The actual downcasing happens at https://gitlab.com/gitlab-org/gitlab-ee/blob/b37558cdd70e025355ea7626898f56c0f91fb1a7/lib/gitlab/auth/ldap/dn.rb#L232. Note that downcasing isn't the only sanitization of DNs that needs to happen. There can also be random whitespace that LDAP ignores but not Ruby.
parsed_dn = ::Gitlab::Auth::LDAP::DN.new(dn).to_a
By comparison, when syncing via an LDAP filter, the DNs are returned in raw form directly from the LDAP search - https://gitlab.com/gitlab-org/gitlab-ee/blob/b37558cdd70e025355ea7626898f56c0f91fb1a7/ee/lib/ee/gitlab/auth/ldap/user_filter.rb#L18
@proxy.adapter.ldap_search(options).map(&:dn).tap do |dns|
logger.debug "Found #{dns.count} mathing users for filter #{@filter}"
end
I also notice something else that is probably out of scope for this issue, but worth noting. We cache all group sync LDAP lookups in memory using EE::Gitlab::Auth::LDAP::Sync::Proxy
. However, the filter search results are not cached. This might be a little more difficult to cache but something we might want to consider in the future. Otherwise, if users re-use the same filter in many groups we will call to LDAP each time to get the same results.