summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Benvenuto <mark.benvenuto@mongodb.com>2021-09-16 16:31:31 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-09-16 21:47:28 +0000
commitf912a0c89d77e59eaba31c4ad86d0d0428e0b4da (patch)
tree673d56a6f936355090b48b58b199a68546049bd1
parent66cb7984420f7f88e03c7834b98fbe814c0b40fc (diff)
downloadmongo-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.js53
-rw-r--r--src/mongo/client/SConscript1
-rw-r--r--src/mongo/client/async_client.cpp3
-rw-r--r--src/mongo/client/authenticate.cpp10
-rw-r--r--src/mongo/client/authenticate.h1
-rw-r--r--src/mongo/client/dbclient_base.cpp3
-rw-r--r--src/mongo/client/native_sasl_client_session.cpp32
-rw-r--r--src/mongo/client/scram_client_cache.h23
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