summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorSara Golemon <sara.golemon@mongodb.com>2019-12-04 17:12:10 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-02-12 19:16:45 +0000
commit812c8338f496da3f43174330e37f07f0aad442d3 (patch)
tree80baa88c0eb7aec60fe1d199b27308deae87d49c /src/mongo/db
parent37d1ef0d02582ac95a2adf835a341e0ead12abb3 (diff)
downloadmongo-812c8338f496da3f43174330e37f07f0aad442d3.tar.gz
SERVER-44858 Implement speculative sasl auth
create mode 100644 jstests/auth/speculative-auth-replset.js create mode 100644 jstests/auth/speculative-sasl-start.js create mode 100644 jstests/ssl/speculative-auth-replset.js create mode 100644 jstests/ssl/speculative-authenticate.js create mode 100644 src/mongo/db/auth/sasl_commands.h create mode 100644 src/mongo/db/s/balancer/core_options_stub.cpp
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/auth/SConscript2
-rw-r--r--src/mongo/db/auth/authentication_session.h9
-rw-r--r--src/mongo/db/auth/sasl_commands.cpp74
-rw-r--r--src/mongo/db/auth/sasl_commands.h42
-rw-r--r--src/mongo/db/auth/sasl_options.cpp10
-rw-r--r--src/mongo/db/auth/sasl_options.idl2
-rw-r--r--src/mongo/db/commands/SConscript1
-rw-r--r--src/mongo/db/commands/authentication_commands.cpp22
-rw-r--r--src/mongo/db/commands/authentication_commands.h5
-rw-r--r--src/mongo/db/commands/server_status_servers.cpp15
-rw-r--r--src/mongo/db/repl/SConscript2
-rw-r--r--src/mongo/db/repl/replication_info.cpp27
-rw-r--r--src/mongo/db/s/SConscript1
-rw-r--r--src/mongo/db/s/balancer/core_options_stub.cpp42
-rw-r--r--src/mongo/db/stats/counters.cpp59
-rw-r--r--src/mongo/db/stats/counters.h27
16 files changed, 320 insertions, 20 deletions
diff --git a/src/mongo/db/auth/SConscript b/src/mongo/db/auth/SConscript
index 6c579a188eb..a7cce5850bb 100644
--- a/src/mongo/db/auth/SConscript
+++ b/src/mongo/db/auth/SConscript
@@ -245,6 +245,7 @@ env.Library(
'$BUILD_DIR/mongo/db/commands',
'$BUILD_DIR/mongo/db/commands/authentication_commands',
'$BUILD_DIR/mongo/db/commands/test_commands_enabled',
+ '$BUILD_DIR/mongo/db/stats/counters',
'sasl_options_init',
],
)
@@ -271,6 +272,7 @@ env.Library(
],
LIBDEPS=[
'$BUILD_DIR/mongo/base',
+ '$BUILD_DIR/mongo/db/stats/counters',
'$BUILD_DIR/mongo/idl/server_parameter',
],
)
diff --git a/src/mongo/db/auth/authentication_session.h b/src/mongo/db/auth/authentication_session.h
index 5a26c46efd4..ff86ea7ca08 100644
--- a/src/mongo/db/auth/authentication_session.h
+++ b/src/mongo/db/auth/authentication_session.h
@@ -45,8 +45,8 @@ class AuthenticationSession {
AuthenticationSession& operator=(const AuthenticationSession&) = delete;
public:
- explicit AuthenticationSession(std::unique_ptr<ServerMechanismBase> mech)
- : _mech(std::move(mech)) {}
+ explicit AuthenticationSession(std::unique_ptr<ServerMechanismBase> mech, bool speculative)
+ : _mech(std::move(mech)), _speculative(speculative) {}
/**
* Sets the authentication session for the given "client" to "newSession".
@@ -72,8 +72,13 @@ public:
return _mech->setOptions(options);
}
+ bool isSpeculative() const {
+ return _speculative;
+ }
+
private:
std::unique_ptr<ServerMechanismBase> _mech;
+ bool _speculative{false};
};
} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_commands.cpp b/src/mongo/db/auth/sasl_commands.cpp
index 1932b7e9a4e..a6276d3fec9 100644
--- a/src/mongo/db/auth/sasl_commands.cpp
+++ b/src/mongo/db/auth/sasl_commands.cpp
@@ -39,6 +39,7 @@
#include "mongo/bson/mutable/algorithm.h"
#include "mongo/bson/mutable/document.h"
#include "mongo/bson/util/bson_extract.h"
+#include "mongo/client/authenticate.h"
#include "mongo/client/sasl_client_authenticate.h"
#include "mongo/db/audit.h"
#include "mongo/db/auth/authentication_session.h"
@@ -51,6 +52,7 @@
#include "mongo/db/commands.h"
#include "mongo/db/commands/authentication_commands.h"
#include "mongo/db/server_options.h"
+#include "mongo/db/stats/counters.h"
#include "mongo/util/base64.h"
#include "mongo/util/log.h"
#include "mongo/util/sequence_util.h"
@@ -125,6 +127,7 @@ public:
CmdSaslStart cmdSaslStart;
CmdSaslContinue cmdSaslContinue;
+
Status buildResponse(const AuthenticationSession* session,
const std::string& responsePayload,
BSONType responsePayloadType,
@@ -212,6 +215,9 @@ Status doSaslStep(OperationContext* opCtx,
<< " on " << mechanism.getAuthenticationDatabase() << " from client "
<< opCtx->getClient()->session()->remote();
}
+ if (session->isSpeculative()) {
+ authCounter.incSpeculativeAuthenticateSuccessful(mechanism.mechanismName().toString());
+ }
}
return Status::OK();
}
@@ -220,7 +226,8 @@ StatusWith<std::unique_ptr<AuthenticationSession>> doSaslStart(OperationContext*
const std::string& db,
const BSONObj& cmdObj,
BSONObjBuilder* result,
- std::string* principalName) {
+ std::string* principalName,
+ bool speculative) {
bool autoAuthorize = false;
Status status = bsonExtractBooleanFieldWithDefault(
cmdObj, saslCommandAutoAuthorizeFieldName, autoAuthorizeDefault, &autoAuthorize);
@@ -240,7 +247,16 @@ StatusWith<std::unique_ptr<AuthenticationSession>> doSaslStart(OperationContext*
return swMech.getStatus();
}
- auto session = std::make_unique<AuthenticationSession>(std::move(swMech.getValue()));
+ auto session =
+ std::make_unique<AuthenticationSession>(std::move(swMech.getValue()), speculative);
+
+ if (speculative &&
+ !session->getMechanism().properties().hasAllProperties(
+ SecurityPropertySet({SecurityProperty::kNoPlainText}))) {
+ return {ErrorCodes::BadValue,
+ "Plaintext mechanisms may not be used with speculativeSaslStart"};
+ }
+
auto options = cmdObj["options"];
if (!options.eoo()) {
if (options.type() != Object) {
@@ -280,17 +296,11 @@ Status doSaslContinue(OperationContext* opCtx,
return doSaslStep(opCtx, session, cmdObj, result);
}
-CmdSaslStart::CmdSaslStart() : BasicCommand(saslStartCommandName) {}
-CmdSaslStart::~CmdSaslStart() {}
-
-std::string CmdSaslStart::help() const {
- return "First step in a SASL authentication conversation.";
-}
-
-bool CmdSaslStart::run(OperationContext* opCtx,
- const std::string& db,
- const BSONObj& cmdObj,
- BSONObjBuilder& result) {
+bool runSaslStart(OperationContext* opCtx,
+ const std::string& db,
+ const BSONObj& cmdObj,
+ BSONObjBuilder& result,
+ bool speculative) {
opCtx->markKillOnClientDisconnect();
Client* client = opCtx->getClient();
AuthenticationSession::set(client, std::unique_ptr<AuthenticationSession>());
@@ -301,7 +311,7 @@ bool CmdSaslStart::run(OperationContext* opCtx,
}
std::string principalName;
- auto swSession = doSaslStart(opCtx, db, cmdObj, &result, &principalName);
+ auto swSession = doSaslStart(opCtx, db, cmdObj, &result, &principalName, speculative);
if (!swSession.isOK() || swSession.getValue()->getMechanism().isSuccess()) {
audit::logAuthentication(
@@ -315,6 +325,20 @@ bool CmdSaslStart::run(OperationContext* opCtx,
return true;
}
+CmdSaslStart::CmdSaslStart() : BasicCommand(saslStartCommandName) {}
+CmdSaslStart::~CmdSaslStart() {}
+
+std::string CmdSaslStart::help() const {
+ return "First step in a SASL authentication conversation.";
+}
+
+bool CmdSaslStart::run(OperationContext* opCtx,
+ const std::string& db,
+ const BSONObj& cmdObj,
+ BSONObjBuilder& result) {
+ return runSaslStart(opCtx, db, cmdObj, result, false);
+}
+
CmdSaslContinue::CmdSaslContinue() : BasicCommand(saslContinueCommandName) {}
CmdSaslContinue::~CmdSaslContinue() {}
@@ -371,4 +395,26 @@ MONGO_INITIALIZER(PreSaslCommands)
}
} // namespace
+
+void doSpeculativeSaslStart(OperationContext* opCtx, BSONObj cmdObj, BSONObjBuilder* result) try {
+ auto mechElem = cmdObj["mechanism"];
+ if (mechElem.type() != String) {
+ return;
+ }
+
+ authCounter.incSpeculativeAuthenticateReceived(mechElem.String());
+
+ auto dbElement = cmdObj["db"];
+ if (dbElement.type() != String) {
+ return;
+ }
+
+ BSONObjBuilder saslStartResult;
+ if (runSaslStart(opCtx, dbElement.String(), cmdObj, saslStartResult, true)) {
+ result->append(auth::kSpeculativeAuthenticate, saslStartResult.obj());
+ }
+} catch (...) {
+ // Treat failure like we never even got a speculative start.
+}
+
} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_commands.h b/src/mongo/db/auth/sasl_commands.h
new file mode 100644
index 00000000000..394e6034b0c
--- /dev/null
+++ b/src/mongo/db/auth/sasl_commands.h
@@ -0,0 +1,42 @@
+/**
+ * Copyright (C) 2018-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include "mongo/bson/bsonobj.h"
+#include "mongo/bson/bsonobjbuilder.h"
+
+namespace mongo {
+class OperationContext;
+
+/**
+ * Handle isMaster: { speculativeAuthenticate: {...} }
+ */
+void doSpeculativeSaslStart(OperationContext* opCtx, BSONObj cmdObj, BSONObjBuilder* result);
+} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_options.cpp b/src/mongo/db/auth/sasl_options.cpp
index ac23ebf6618..d390ad99524 100644
--- a/src/mongo/db/auth/sasl_options.cpp
+++ b/src/mongo/db/auth/sasl_options.cpp
@@ -29,6 +29,7 @@
#include "mongo/db/auth/sasl_options.h"
#include "mongo/db/auth/sasl_options_gen.h"
+#include "mongo/db/stats/counters.h"
#include "mongo/util/text.h"
@@ -46,4 +47,13 @@ SASLGlobalParams::SASLGlobalParams() {
// Default value for auth failed delay
authFailedDelay.store(0);
}
+
+namespace {
+MONGO_INITIALIZER_WITH_PREREQUISITES(InitSpeculativeCounters, ("EndStartupOptionStorage"))
+(InitializerContext*) {
+ authCounter.initializeMechanismMap(saslGlobalParams.authenticationMechanisms);
+ return Status::OK();
+}
+} // namespace
+
} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_options.idl b/src/mongo/db/auth/sasl_options.idl
index 5c6070e9701..c3753ff8d95 100644
--- a/src/mongo/db/auth/sasl_options.idl
+++ b/src/mongo/db/auth/sasl_options.idl
@@ -36,6 +36,8 @@ global:
server_parameters:
authenticationMechanisms:
+ # Note: mongo/db/stats/counter.cpp makes the assumption that this
+ # setting will never be changed at runtime.
description: "The set of accepted authentication mechanisms"
set_at: startup
default:
diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript
index 568e23c6272..679571a308f 100644
--- a/src/mongo/db/commands/SConscript
+++ b/src/mongo/db/commands/SConscript
@@ -192,6 +192,7 @@ env.Library(
'$BUILD_DIR/mongo/db/auth/sasl_options',
'$BUILD_DIR/mongo/db/auth/user_document_parser',
'$BUILD_DIR/mongo/db/commands',
+ '$BUILD_DIR/mongo/db/stats/counters',
'$BUILD_DIR/mongo/util/net/ssl_manager',
]
)
diff --git a/src/mongo/db/commands/authentication_commands.cpp b/src/mongo/db/commands/authentication_commands.cpp
index dd273d77faa..d51d9815d08 100644
--- a/src/mongo/db/commands/authentication_commands.cpp
+++ b/src/mongo/db/commands/authentication_commands.cpp
@@ -40,6 +40,7 @@
#include "mongo/base/status.h"
#include "mongo/bson/mutable/algorithm.h"
#include "mongo/bson/mutable/document.h"
+#include "mongo/client/authenticate.h"
#include "mongo/client/sasl_client_authenticate.h"
#include "mongo/config.h"
#include "mongo/db/audit.h"
@@ -52,6 +53,7 @@
#include "mongo/db/commands.h"
#include "mongo/db/commands/test_commands_enabled.h"
#include "mongo/db/operation_context.h"
+#include "mongo/db/stats/counters.h"
#include "mongo/platform/random.h"
#include "mongo/rpc/metadata/client_metadata.h"
#include "mongo/rpc/metadata/client_metadata_ismaster.h"
@@ -360,4 +362,24 @@ void disableAuthMechanism(StringData authMechanism) {
}
}
+void doSpeculativeAuthenticate(OperationContext* opCtx,
+ BSONObj cmdObj,
+ BSONObjBuilder* result) try {
+ auto mechElem = cmdObj["mechanism"];
+ if (mechElem.type() != String) {
+ return;
+ }
+
+ auto mechanism = mechElem.String();
+ authCounter.incSpeculativeAuthenticateReceived(mechanism);
+
+ BSONObjBuilder authResult;
+ if (cmdAuthenticate.run(opCtx, "$external", cmdObj, authResult)) {
+ authCounter.incSpeculativeAuthenticateSuccessful(mechanism);
+ result->append(auth::kSpeculativeAuthenticate, authResult.obj());
+ }
+} catch (...) {
+ // Treat failure like we never even got a speculative start.
+}
+
} // namespace mongo
diff --git a/src/mongo/db/commands/authentication_commands.h b/src/mongo/db/commands/authentication_commands.h
index d6193a8f4ac..c211b799ef2 100644
--- a/src/mongo/db/commands/authentication_commands.h
+++ b/src/mongo/db/commands/authentication_commands.h
@@ -30,11 +30,16 @@
#pragma once
#include "mongo/base/string_data.h"
+#include "mongo/bson/bsonobj.h"
+#include "mongo/bson/bsonobjbuilder.h"
namespace mongo {
+class OperationContext;
constexpr StringData kX509AuthMechanism = "MONGODB-X509"_sd;
void disableAuthMechanism(StringData authMechanism);
+void doSpeculativeAuthenticate(OperationContext* opCtx, BSONObj isMaster, BSONObjBuilder* result);
+
} // namespace mongo
diff --git a/src/mongo/db/commands/server_status_servers.cpp b/src/mongo/db/commands/server_status_servers.cpp
index e85301b7b7a..a3d23061823 100644
--- a/src/mongo/db/commands/server_status_servers.cpp
+++ b/src/mongo/db/commands/server_status_servers.cpp
@@ -95,7 +95,6 @@ public:
} network;
-#ifdef MONGO_CONFIG_SSL
class Security : public ServerStatusSection {
public:
Security() : ServerStatusSection("security") {}
@@ -106,15 +105,23 @@ public:
BSONObj generateSection(OperationContext* opCtx,
const BSONElement& configElement) const override {
- BSONObj result;
+ BSONObjBuilder result;
+
+ BSONObjBuilder auth;
+ authCounter.append(&auth);
+ result.append("authentication", auth.obj());
+
+#ifdef MONGO_CONFIG_SSL
if (getSSLManager()) {
- result = getSSLManager()->getSSLConfiguration().getServerStatusBSON();
+ getSSLManager()->getSSLConfiguration().getServerStatusBSON(&result);
}
+#endif
- return result;
+ return result.obj();
}
} security;
+#ifdef MONGO_CONFIG_SSL
/**
* Status section of which tls versions connected to MongoDB and completed an SSL handshake.
* Note: Clients are only not counted if they try to connect to the server with a unsupported TLS
diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript
index 6efb849fc51..204e8042cd2 100644
--- a/src/mongo/db/repl/SConscript
+++ b/src/mongo/db/repl/SConscript
@@ -1128,6 +1128,8 @@ env.Library(
'replica_set_messages',
],
LIBDEPS_PRIVATE=[
+ '$BUILD_DIR/mongo/db/auth/authservercommon',
+ '$BUILD_DIR/mongo/db/commands/authentication_commands',
'$BUILD_DIR/mongo/db/commands/server_status',
'$BUILD_DIR/mongo/db/stats/counters',
'$BUILD_DIR/mongo/transport/message_compressor',
diff --git a/src/mongo/db/repl/replication_info.cpp b/src/mongo/db/repl/replication_info.cpp
index 1f1d071e29a..6d65ffb2bd4 100644
--- a/src/mongo/db/repl/replication_info.cpp
+++ b/src/mongo/db/repl/replication_info.cpp
@@ -36,8 +36,11 @@
#include "mongo/bson/util/bson_extract.h"
#include "mongo/client/connpool.h"
#include "mongo/client/dbclient_connection.h"
+#include "mongo/db/auth/sasl_command_constants.h"
+#include "mongo/db/auth/sasl_commands.h"
#include "mongo/db/auth/sasl_mechanism_registry.h"
#include "mongo/db/client.h"
+#include "mongo/db/commands/authentication_commands.h"
#include "mongo/db/commands/server_status.h"
#include "mongo/db/db_raii.h"
#include "mongo/db/dbhelpers.h"
@@ -516,6 +519,30 @@ public:
}
}
+ if (auto sae = cmdObj[auth::kSpeculativeAuthenticate]; !sae.eoo()) {
+ uassert(ErrorCodes::BadValue,
+ str::stream() << "isMaster." << auth::kSpeculativeAuthenticate
+ << " must be an Object",
+ sae.type() == Object);
+ auto specAuth = sae.Obj();
+
+ uassert(ErrorCodes::BadValue,
+ str::stream() << "isMaster." << auth::kSpeculativeAuthenticate
+ << " must be a non-empty Object",
+ !specAuth.isEmpty());
+ auto specCmd = specAuth.firstElementFieldNameStringData();
+
+ if (specCmd == saslStartCommandName) {
+ doSpeculativeSaslStart(opCtx, specAuth, &result);
+ } else if (specCmd == auth::kAuthenticateCommand) {
+ doSpeculativeAuthenticate(opCtx, specAuth, &result);
+ } else {
+ uasserted(51769,
+ str::stream() << "isMaster." << auth::kSpeculativeAuthenticate
+ << " unknown command: " << specCmd);
+ }
+ }
+
return true;
}
} cmdismaster;
diff --git a/src/mongo/db/s/SConscript b/src/mongo/db/s/SConscript
index 976282cf4c3..d7bf39ae0d9 100644
--- a/src/mongo/db/s/SConscript
+++ b/src/mongo/db/s/SConscript
@@ -352,6 +352,7 @@ env.CppUnitTest(
'balancer/balancer_chunk_selection_policy_test.cpp',
'balancer/balancer_policy_test.cpp',
'balancer/cluster_statistics_test.cpp',
+ 'balancer/core_options_stub.cpp',
'balancer/migration_manager_test.cpp',
'balancer/migration_test_fixture.cpp',
'balancer/scoped_migration_request_test.cpp',
diff --git a/src/mongo/db/s/balancer/core_options_stub.cpp b/src/mongo/db/s/balancer/core_options_stub.cpp
new file mode 100644
index 00000000000..2b9c5d8b837
--- /dev/null
+++ b/src/mongo/db/s/balancer/core_options_stub.cpp
@@ -0,0 +1,42 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/base/init.h"
+#include "mongo/base/status.h"
+
+namespace mongo {
+namespace {
+MONGO_INITIALIZER_GENERAL(CoreOptions_Store,
+ ("BeginStartupOptionStorage"),
+ ("EndStartupOptionStorage"))
+(InitializerContext* context) {
+ return Status::OK();
+}
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/db/stats/counters.cpp b/src/mongo/db/stats/counters.cpp
index 6cc06589d78..8e4458db7a7 100644
--- a/src/mongo/db/stats/counters.cpp
+++ b/src/mongo/db/stats/counters.cpp
@@ -33,6 +33,7 @@
#include "mongo/db/stats/counters.h"
+#include "mongo/client/authenticate.h"
#include "mongo/db/jsobj.h"
#include "mongo/util/log.h"
@@ -168,7 +169,65 @@ void NetworkCounter::append(BSONObjBuilder& b) {
b.append("tcpFastOpen", tfo.obj());
}
+void AuthCounter::initializeMechanismMap(const std::vector<std::string>& mechanisms) {
+ invariant(_mechanisms.empty());
+
+ for (const auto& mech : mechanisms) {
+ _mechanisms.emplace(
+ std::piecewise_construct, std::forward_as_tuple(mech), std::forward_as_tuple());
+ }
+}
+
+void AuthCounter::incSpeculativeAuthenticateReceived(const std::string& mechanism) try {
+ _mechanisms.at(mechanism).speculativeAuthenticate.received.fetchAndAddRelaxed(1);
+} catch (const std::out_of_range&) {
+ uasserted(51767,
+ str::stream() << "Received " << auth::kSpeculativeAuthenticate << " for mechanism "
+ << mechanism << " which is unknown or not enabled");
+}
+
+void AuthCounter::incSpeculativeAuthenticateSuccessful(const std::string& mechanism) try {
+ _mechanisms.at(mechanism).speculativeAuthenticate.successful.fetchAndAddRelaxed(1);
+} catch (const std::out_of_range&) {
+ // Should never actually occur since it'd mean we succeeded at a mechanism
+ // we're not configured for.
+ uasserted(51768,
+ str::stream() << "Unexpectedly succeeded at " << auth::kSpeculativeAuthenticate
+ << " for " << mechanism << " which is not enabled");
+}
+
+/**
+ * authentication: {
+ * "mechanisms": {
+ * "SCRAM-SHA-256": {
+ * "speculativeAuthenticate": { received: ###, successful: ### },
+ * },
+ * "MONGODB-X509": {
+ * "speculativeAuthenticate": { received: ###, successful: ### },
+ * },
+ * },
+ * }
+ */
+void AuthCounter::append(BSONObjBuilder* b) {
+ BSONObjBuilder mechsBuilder(b->subobjStart("mechanisms"));
+
+ for (const auto& it : _mechanisms) {
+ const auto received = it.second.speculativeAuthenticate.received.load();
+ const auto successful = it.second.speculativeAuthenticate.successful.load();
+
+ BSONObjBuilder mechBuilder(mechsBuilder.subobjStart(it.first));
+ BSONObjBuilder specAuthBuilder(mechBuilder.subobjStart(auth::kSpeculativeAuthenticate));
+ specAuthBuilder.append("received", received);
+ specAuthBuilder.append("successful", successful);
+ specAuthBuilder.done();
+ mechBuilder.done();
+ }
+
+ mechsBuilder.done();
+}
+
OpCounters globalOpCounters;
OpCounters replOpCounters;
NetworkCounter networkCounter;
+AuthCounter authCounter;
} // namespace mongo
diff --git a/src/mongo/db/stats/counters.h b/src/mongo/db/stats/counters.h
index 0afb3c6756c..edc17eee2bd 100644
--- a/src/mongo/db/stats/counters.h
+++ b/src/mongo/db/stats/counters.h
@@ -29,6 +29,8 @@
#pragma once
+#include <map>
+
#include "mongo/db/jsobj.h"
#include "mongo/platform/atomic_word.h"
#include "mongo/platform/basic.h"
@@ -165,4 +167,29 @@ private:
};
extern NetworkCounter networkCounter;
+
+class AuthCounter {
+public:
+ void incSpeculativeAuthenticateReceived(const std::string& mechanism);
+ void incSpeculativeAuthenticateSuccessful(const std::string& mechanism);
+
+ void append(BSONObjBuilder*);
+
+ void initializeMechanismMap(const std::vector<std::string>&);
+
+private:
+ struct MechanismData {
+ struct {
+ AtomicWord<long long> received;
+ AtomicWord<long long> successful;
+ } speculativeAuthenticate;
+ };
+ using MechanismMap = std::map<std::string, MechanismData>;
+
+ // Mechanism maps are initialized at startup to contain all
+ // mechanisms known to authenticationMechanisms setParam.
+ // After that they are kept to a fixed size.
+ MechanismMap _mechanisms;
+};
+extern AuthCounter authCounter;
} // namespace mongo