diff options
author | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2021-09-16 16:31:31 -0400 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-09-16 21:47:28 +0000 |
commit | f912a0c89d77e59eaba31c4ad86d0d0428e0b4da (patch) | |
tree | 673d56a6f936355090b48b58b199a68546049bd1 | |
parent | 66cb7984420f7f88e03c7834b98fbe814c0b40fc (diff) | |
download | mongo-f912a0c89d77e59eaba31c4ad86d0d0428e0b4da.tar.gz |
SERVER-59876 Ensure hostname is progated for internal sasl auth
(cherry picked from commit f9383c046f2895f6622fe48f063ab3c174afcbb6)
-rw-r--r-- | jstests/auth/sharding_scram_cache.js | 53 | ||||
-rw-r--r-- | src/mongo/client/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/client/async_client.cpp | 3 | ||||
-rw-r--r-- | src/mongo/client/authenticate.cpp | 10 | ||||
-rw-r--r-- | src/mongo/client/authenticate.h | 1 | ||||
-rw-r--r-- | src/mongo/client/dbclient_base.cpp | 3 | ||||
-rw-r--r-- | src/mongo/client/native_sasl_client_session.cpp | 32 | ||||
-rw-r--r-- | src/mongo/client/scram_client_cache.h | 23 |
8 files changed, 120 insertions, 6 deletions
diff --git a/jstests/auth/sharding_scram_cache.js b/jstests/auth/sharding_scram_cache.js new file mode 100644 index 00000000000..52d325c7fb4 --- /dev/null +++ b/jstests/auth/sharding_scram_cache.js @@ -0,0 +1,53 @@ +/** + * Validate client side scram cache is used + * + * Ensure that for each cache miss, an entry is added + * + * @tags: [requires_sharding] + */ +(function() { +'use strict'; + +function setup(conn) { + var adminDB = conn.getDB('admin'); + + adminDB.createUser({user: 'root', pwd: "pwd", roles: ["root", "clusterMonitor"]}); +} + +function runTests(conn) { + let adminDB = conn.getDB('admin'); + + assert.eq(1, adminDB.auth('root', "pwd")); + + adminDB.collTest.insert({x: 1}); + + const ss = adminDB.serverStatus(); + jsTestLog(tojson(ss)); + + const sc = ss.scramCache; + + // Validate that for each miss, we get an entry + // If the cache was not used, the missed count would exceed the count of entries + assert.gte(sc["SCRAM-SHA-1"].count + 2, sc["SCRAM-SHA-1"].misses); + + assert.gte(sc["SCRAM-SHA-256"].count + 2, sc["SCRAM-SHA-256"].misses); + + assert(sc["SCRAM-SHA-1"].count > 0 || sc["SCRAM-SHA-256"].count > 0, + "Cache was not used at all"); + + adminDB.logout(); +} + +// TODO: Remove 'shardAsReplicaSet: false' when SERVER-32672 is fixed. +var st = + new ShardingTest({shards: 1, keyFile: 'jstests/libs/key1', other: {shardAsReplicaSet: false}}); +setup(st.s); + +// Validate mongos +runTests(st.s); + +// Validate a RS member +runTests(st.configRS.getPrimary()); + +st.stop(); +})(); diff --git a/src/mongo/client/SConscript b/src/mongo/client/SConscript index a1e79accfec..58c8dc9d3dd 100644 --- a/src/mongo/client/SConscript +++ b/src/mongo/client/SConscript @@ -101,6 +101,7 @@ saslClientEnv.Library( LIBDEPS=[ '$BUILD_DIR/mongo/base/secure_allocator', '$BUILD_DIR/mongo/bson/util/bson_extract', + '$BUILD_DIR/mongo/db/commands/server_status', '$BUILD_DIR/mongo/executor/remote_command', '$BUILD_DIR/mongo/rpc/command_status', '$BUILD_DIR/mongo/rpc/metadata', diff --git a/src/mongo/client/async_client.cpp b/src/mongo/client/async_client.cpp index 08e17ceb8f2..763d5b60399 100644 --- a/src/mongo/client/async_client.cpp +++ b/src/mongo/client/async_client.cpp @@ -168,7 +168,8 @@ Future<void> AsyncDBClient::authenticateInternal(boost::optional<std::string> me } #endif - return auth::authenticateInternalClient(clientName, mechanismHint, _makeAuthRunCommandHook()); + return auth::authenticateInternalClient( + clientName, remote(), mechanismHint, _makeAuthRunCommandHook()); } Future<void> AsyncDBClient::initWireVersion(const std::string& appName, diff --git a/src/mongo/client/authenticate.cpp b/src/mongo/client/authenticate.cpp index 3fb41a28b27..66414275cb6 100644 --- a/src/mongo/client/authenticate.cpp +++ b/src/mongo/client/authenticate.cpp @@ -283,22 +283,24 @@ Future<std::string> negotiateSaslMechanism(RunCommandHook runCommand, } Future<void> authenticateInternalClient(const std::string& clientSubjectName, + const HostAndPort& remote, boost::optional<std::string> mechanismHint, RunCommandHook runCommand) { return negotiateSaslMechanism(runCommand, internalSecurity.user->getName(), mechanismHint) - .then([runCommand, clientSubjectName](std::string mechanism) -> Future<void> { + .then([runCommand, clientSubjectName, remote](std::string mechanism) -> Future<void> { auto params = getInternalAuthParams(0, mechanism); if (params.isEmpty()) { return Status(ErrorCodes::BadValue, "Missing authentication parameters for internal user auth"); } - return authenticateClient(params, HostAndPort(), clientSubjectName, runCommand) + return authenticateClient(params, remote, clientSubjectName, runCommand) .onError<ErrorCodes::AuthenticationFailed>( - [runCommand, clientSubjectName, mechanism](Status status) -> Future<void> { + [runCommand, clientSubjectName, remote, mechanism]( + Status status) -> Future<void> { auto altCreds = getInternalAuthParams(1, mechanism); if (!altCreds.isEmpty()) { return authenticateClient( - altCreds, HostAndPort(), clientSubjectName, runCommand); + altCreds, remote, clientSubjectName, runCommand); } return status; }); diff --git a/src/mongo/client/authenticate.h b/src/mongo/client/authenticate.h index e5825e52111..dc94b355d24 100644 --- a/src/mongo/client/authenticate.h +++ b/src/mongo/client/authenticate.h @@ -110,6 +110,7 @@ Future<void> authenticateClient(const BSONObj& params, * than once, but will only call the AuthCompletionHandler once. */ Future<void> authenticateInternalClient(const std::string& clientSubjectName, + const HostAndPort& remote, boost::optional<std::string> mechanismHint, RunCommandHook runCommand); diff --git a/src/mongo/client/dbclient_base.cpp b/src/mongo/client/dbclient_base.cpp index ab36bec7810..005657dc963 100644 --- a/src/mongo/client/dbclient_base.cpp +++ b/src/mongo/client/dbclient_base.cpp @@ -476,7 +476,8 @@ Status DBClientBase::authenticateInternalUser() { #endif auto status = - auth::authenticateInternalClient(clientName, boost::none, _makeAuthRunCommandHook()) + auth::authenticateInternalClient( + clientName, HostAndPort(getServerAddress()), boost::none, _makeAuthRunCommandHook()) .getNoThrow(); if (status.isOK()) { return status; diff --git a/src/mongo/client/native_sasl_client_session.cpp b/src/mongo/client/native_sasl_client_session.cpp index 3fa871bda5d..cf0a3f40007 100644 --- a/src/mongo/client/native_sasl_client_session.cpp +++ b/src/mongo/client/native_sasl_client_session.cpp @@ -32,12 +32,14 @@ #include "mongo/client/native_sasl_client_session.h" #include "mongo/base/init.h" +#include "mongo/bson/bsonobjbuilder.h" #include "mongo/client/sasl_client_conversation.h" #include "mongo/client/sasl_plain_client_conversation.h" #include "mongo/client/sasl_scram_client_conversation.h" #include "mongo/client/scram_client_cache.h" #include "mongo/crypto/sha1_block.h" #include "mongo/crypto/sha256_block.h" +#include "mongo/db/commands/server_status.h" #include "mongo/util/str.h" namespace mongo { @@ -56,6 +58,36 @@ MONGO_INITIALIZER(NativeSaslClientContext)(InitializerContext* context) { auto* scramsha1ClientCache = new SCRAMClientCache<SHA1Block>; auto* scramsha256ClientCache = new SCRAMClientCache<SHA256Block>; +template <typename HashBlock> +void cacheToBSON(SCRAMClientCache<HashBlock>* cache, StringData name, BSONObjBuilder* builder) { + auto stats = cache->getStats(); + + BSONObjBuilder sub(builder->subobjStart(name)); + sub.append("count", stats.count); + sub.append("hits", stats.hits); + sub.append("misses", stats.misses); +} + +/** + * Output stats about the SCRAM client cache to server status. + */ +class ScramCacheStatsStatusSection : ServerStatusSection { +public: + ScramCacheStatsStatusSection() : ServerStatusSection("scramCache") {} + bool includeByDefault() const override { + return true; + } + + BSONObj generateSection(OperationContext* opCtx, + const BSONElement& configElement) const override { + BSONObjBuilder builder; + cacheToBSON(scramsha1ClientCache, "SCRAM-SHA-1", &builder); + cacheToBSON(scramsha256ClientCache, "SCRAM-SHA-256", &builder); + return builder.obj(); + }; + +} scramCacheStatusSection; + } // namespace NativeSaslClientSession::NativeSaslClientSession() diff --git a/src/mongo/client/scram_client_cache.h b/src/mongo/client/scram_client_cache.h index fb43d76b622..d8cc4493cb8 100644 --- a/src/mongo/client/scram_client_cache.h +++ b/src/mongo/client/scram_client_cache.h @@ -68,6 +68,15 @@ private: using HostToSecretsMap = stdx::unordered_map<HostAndPort, HostToSecretsPair>; public: + struct Stats { + // Count of cache entries + int64_t count{0}; + // Number of cache hits + int64_t hits{0}; + // Number of cache misses + int64_t misses{0}; + }; + /** * Returns precomputed SCRAMSecrets, if one has already been * stored for the specified hostname and the provided presecrets @@ -81,6 +90,7 @@ public: // Search the cache for a record associated with the host we're trying to connect to. auto foundSecret = _hostToSecrets.find(target); if (foundSecret == _hostToSecrets.end()) { + ++_stats.misses; return {}; } @@ -89,8 +99,10 @@ public: // stale cached secrets. We'll need to rerun the SCRAM computation. const auto& foundPresecrets = foundSecret->second.first; if (foundPresecrets == presecrets) { + ++_stats.hits; return foundSecret->second.second; } else { + ++_stats.misses; return {}; } } @@ -116,9 +128,20 @@ public: } } + /** + * Return metrics about the cache + */ + Stats getStats() const { + const stdx::lock_guard<Latch> lock(_hostToSecretsMutex); + Stats stats = _stats; + stats.count = _hostToSecrets.size(); + return stats; + } + private: mutable Mutex _hostToSecretsMutex = MONGO_MAKE_LATCH("SCRAMClientCache::_hostToSecretsMutex"); HostToSecretsMap _hostToSecrets; + mutable Stats _stats; }; } // namespace mongo |