Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow caching of successful "bind" attempts to LDAP server for configurable period of time #15988

Merged
merged 21 commits into from
Dec 30, 2020

Conversation

traceon
Copy link
Contributor

@traceon traceon commented Oct 14, 2020

I hereby agree to the terms of the CLA available at: https://yandex.ru/legal/cla/?lang=en

Changelog category:

  • Improvement

Changelog entry:

  • Added verification_cooldown parameter in LDAP server connection configuration to allow caching of successful "bind" attempts for configurable period of time

Detailed description / Documentation draft:

  • Documented in a form of in-line comments in the sample config.xml file. See <ldap_servers> tag.

@robot-clickhouse robot-clickhouse added the pr-improvement Pull request with some product improvements label Oct 14, 2020
@traceon traceon marked this pull request as draft October 14, 2020 22:47
@robot-clickhouse robot-clickhouse added the submodule changed At least one submodule changed in this PR. label Oct 14, 2020
@robot-clickhouse robot-clickhouse removed the submodule changed At least one submodule changed in this PR. label Oct 19, 2020
@traceon
Copy link
Contributor Author

traceon commented Oct 19, 2020

@den-crane Does this look like a good solution for the slow LDAP server operations, that you were pointing out?

verification_cooldown - a period of time, in seconds, after a successful bind attempt, during which a user will be assumed
                         to be successfully authenticated for all consecutive requests without contacting the LDAP server.
                        Specify 0 (the default) to disable caching and force contacting the LDAP server for each authentication request.

Updated syntax tests.
Linked specifications to ldap/authentication and ldap/external_user_directory features.
@traceon traceon marked this pull request as ready for review November 5, 2020 15:32
src/Access/Authentication.h Outdated Show resolved Hide resolved
traceon and others added 9 commits November 10, 2020 00:10
…down parameter tests written in authentications.py and server_config.py, helper functions written in common.py
Added verification cooldown tests to the ldap/authentication feature.
* master: (70 commits)
  Update documentation-issue.md
  Add an option to use existing tables to perf.py
  DOCSUP-4280: Update the SELECT query (ClickHouse#17231)
  DOCSUP-3584 edit and translate (ClickHouse#17176)
  Fixed flaky test_storage_s3::test_custom_auth_headers
  Update 01560_merge_distributed_join.sql
  Minor improvements
  Slightly more correct
  Auto version update to [20.13.1.1] [54444]
  Auto version update to [20.12.1.5236] [54443]
  Update roadmap
  Add favicon; add loading indicator
  Fix race condition; history and sharing capabilities
  Update bitmap-functions.md
  Fix exception message
  Use default value for read-only flag in metadata for Disk3.
  ISSUES-16605 try fix review comment
  trigger CI
  ISSUES-16605 try fix integration failure
  ISSUES-16605 try fix integration test failure
  ...
src/Access/Authentication.h Outdated Show resolved Hide resolved
src/Access/LDAPParams.h Show resolved Hide resolved
src/Access/LDAPParams.h Show resolved Hide resolved
src/Access/LDAPParams.h Show resolved Hide resolved
src/Access/User.cpp Outdated Show resolved Hide resolved
src/Access/Authentication.cpp Outdated Show resolved Hide resolved
src/Access/LDAPParams.h Show resolved Hide resolved
@vitlibar
Copy link
Member

vitlibar commented Dec 2, 2020

I suppose it's better to use a ready out-of-the-box solution. I mean the class ExpireLRUCache which Poco provides. It's thread-safe, it's well-tested, and it already can do what you want. Check it out:

class ExternalAuthenticators
{
    ...
private:
    using CacheOfCorrectPasswords = ExpireLRUCache<UUID /* identifier of user */, UInt128 /* hash of password */>>;

    struct LDAPServerInfo
    {
        std::shared_ptr<const LDAPServerParams> params;
        CacheOfCorrectPasswords cache_of_correct_passwords; // clears every time when `params` changes
    };

    std::mutex mutex;
    std::unordered_map<String, LDAPServerInfo> ldap_servers;
};

void ExternalAuthenticators::setLDAPServerParams(const String & server, const std::shared_ptr<const LDAPServerParams> & params)
{
    std::scoped_lock lock(mutex);
    auto it = ldap_servers.find(server);
    if (it == ldap_servers.end())
    {
        ldap_servers[server].params = params;
        return;
    }
    
    const auto & old_params = it->second.params;
    if ((params == old_params) || (*params == *old_params))
        return;
    
    it->second.params = params;
    it->second.cache_of_correct_passwords.reset();
}

bool ExternalAuthenticators::isPasswordInCacheOfCorrectPasswords(const String & ldap_server, const UUID & user_id, const String & password)
{
    std::scoped_lock lock(mutex);
    auto it = ldap_servers.find(ldap_server);
    if (it == ldap_servers.end())
        return false;

    auto & cache_of_correct_passwords = it->second.cache_of_correct_passwords;
    auto hash = cache_of_correct_passwords.get(user_id);
    return hash && (*hash == md5(password));
}

void ExternalAuthenticators::putCorrectPasswordToCache(const String & ldap_server, const UUID & user_id, const String & password)
{
    std::scoped_lock lock(mutex);
    auto it = ldap_servers.find(ldap_server);
    if (it == ldap_servers.end())
        return;

    auto & cache_of_correct_passwords = it->second.cache_of_correct_passwords;
    cache_of_correct_passwords.set(user_id, md5(password));
}

@vitlibar vitlibar self-assigned this Dec 2, 2020
@traceon
Copy link
Contributor Author

traceon commented Dec 2, 2020

I didn't use ExpireLRUCache because I didn't like the idea of decoupling something that is already perfectly mapped. There is one-to-one correspondence between the cached info and the user info. This decoupling is not necessary, and provides additional orphan cache entry cleanup problems. Also, it looks like ExpireLRUCache doesn't support unlimited cache size.

Copy link
Member

@vitlibar vitlibar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vitlibar
Copy link
Member

vitlibar commented Dec 14, 2020

I didn't use ExpireLRUCache because I didn't like the idea of decoupling something that is already perfectly mapped.

As you said that's not a tight spot so we're not forced to speed up our code here at any cost. It's better to keep invariants.

@traceon
Copy link
Contributor Author

traceon commented Dec 24, 2020

@vitlibar ExpireLRUCache seemingly updates the expire time on access. This is not what I need. It is poorly documented (even has bugs in the docs.) It is not used anywhere in ClickHouse. I will move the caching to the ExternalAuthenticators as per your request, but not going to use ExpireLRUCache, it is completely wrong tool for this task.

@vitlibar vitlibar merged commit a84887a into ClickHouse:master Dec 30, 2020
@@ -88,8 +88,8 @@ class Authentication
void setServerName(const String & server_name_);

/// Checks if the provided password is correct. Returns false if not.
/// User name and external authenticators' info are used only by some specific authentication type (e.g., LDAP_SERVER).
bool isCorrectPassword(const String & password_, const String & user_, const ExternalAuthenticators & external_authenticators) const;
/// User name and external authenticators are used by the specific authentication types only (e.g., LDAP_SERVER).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is one of the reasons why password_ used to be passed first: it's used almost for each authentication type unlike user_.

@traceon traceon deleted the ldap-cache-login branch January 5, 2021 22:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pr-improvement Pull request with some product improvements
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants