summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Cooper <adam.cooper@mongodb.com>2020-08-14 13:46:35 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-08-14 21:01:59 +0000
commit24dd72daae9e4cf59ad51910058bc111f20edbff (patch)
treef019be38662bc5a86c68ac1f4a006f479a408647
parent6b1a0403f17b688940eb9d86f41d95bdd943d3f4 (diff)
downloadmongo-24dd72daae9e4cf59ad51910058bc111f20edbff.tar.gz
SERVER-48693 Add network counter for cluster authentication
-rw-r--r--jstests/auth/auth-counters.js85
-rw-r--r--jstests/auth/speculative-auth-replset.js11
-rw-r--r--jstests/auth/speculative-auth-sharding.js45
-rw-r--r--jstests/auth/speculative-sasl-start.js34
-rw-r--r--jstests/ssl/auth-counters.js65
-rw-r--r--jstests/ssl/speculative-auth-replset.js11
-rw-r--r--jstests/ssl/speculative-auth-sharding.js25
-rw-r--r--jstests/ssl/speculative-authenticate.js32
-rw-r--r--src/mongo/client/authenticate.h1
-rw-r--r--src/mongo/db/auth/sasl_commands.cpp32
-rw-r--r--src/mongo/db/auth/sasl_mechanism_registry.h9
-rw-r--r--src/mongo/db/commands/authentication_commands.cpp65
-rw-r--r--src/mongo/db/stats/counters.cpp28
-rw-r--r--src/mongo/db/stats/counters.h7
14 files changed, 335 insertions, 115 deletions
diff --git a/jstests/auth/auth-counters.js b/jstests/auth/auth-counters.js
index bbb66a619ae..f3f2af6a758 100644
--- a/jstests/auth/auth-counters.js
+++ b/jstests/auth/auth-counters.js
@@ -3,9 +3,15 @@
(function() {
'use strict';
-const mongod = MongoRunner.runMongod({auth: ''});
-const admin = mongod.getDB('admin');
-const test = mongod.getDB('test');
+const keyfile = 'jstests/libs/key1';
+const badKeyfile = 'jstests/libs/key2';
+let replTest = new ReplSetTest({nodes: 1, keyFile: keyfile, nodeOptions: {auth: ""}});
+replTest.startSet();
+replTest.initiate();
+let primary = replTest.getPrimary();
+
+const admin = primary.getDB('admin');
+const test = primary.getDB('test');
admin.createUser({user: 'admin', pwd: 'pwd', roles: ['root'], mechanisms: ['SCRAM-SHA-256']});
admin.auth('admin', 'pwd');
@@ -15,21 +21,22 @@ test.createUser({user: 'user256', pwd: 'pwd', roles: [], mechanisms: ['SCRAM-SHA
test.createUser(
{user: 'user', pwd: 'pwd', roles: [], mechanisms: ['SCRAM-SHA-1', 'SCRAM-SHA-256']});
-// admin.auth() above provides an initial count for SCRAM-SHA-256
-const expected = {
- 'SCRAM-SHA-256': {
- received: 1,
- successful: 1,
- },
-};
+// Count the number of authentications performed during setup
+const expected =
+ assert.commandWorked(admin.runCommand({serverStatus: 1})).security.authentication.mechanisms;
function assertStats() {
const mechStats = assert.commandWorked(admin.runCommand({serverStatus: 1}))
.security.authentication.mechanisms;
Object.keys(expected).forEach(function(mech) {
try {
- assert.eq(mechStats[mech].authenticate.received, expected[mech].received);
- assert.eq(mechStats[mech].authenticate.successful, expected[mech].successful);
+ assert.eq(mechStats[mech].authenticate.received, expected[mech].authenticate.received);
+ assert.eq(mechStats[mech].authenticate.successful,
+ expected[mech].authenticate.successful);
+ assert.eq(mechStats[mech].clusterAuthenticate.received,
+ expected[mech].clusterAuthenticate.received);
+ assert.eq(mechStats[mech].clusterAuthenticate.successful,
+ expected[mech].clusterAuthenticate.successful);
} catch (e) {
print("Mechanism: " + mech);
print("mechStats: " + tojson(mechStats));
@@ -39,23 +46,42 @@ function assertStats() {
});
}
-function assertSuccess(creds, mech) {
- if (expected[mech] === undefined) {
- expected[mech] = {received: 0, successful: 0};
+function assertSuccess(creds, mech, db = test) {
+ assert.eq(db.auth(creds), true);
+ if (db !== admin) {
+ db.logout();
}
- assert.eq(test.auth(creds), true);
- test.logout();
- ++expected[mech].received;
- ++expected[mech].successful;
+ ++expected[mech].authenticate.received;
+ ++expected[mech].authenticate.successful;
assertStats();
}
-function assertFailure(creds, mech) {
- if (expected[mech] === undefined) {
- expected[mech] = {received: 0, successful: 0};
- }
- assert.eq(test.auth(creds), false);
- ++expected[mech].received;
+function assertFailure(creds, mech, db = test) {
+ assert.eq(db.auth(creds), false);
+ ++expected[mech].authenticate.received;
+ assertStats();
+}
+
+function assertSuccessInternal() {
+ const mech = "SCRAM-SHA-1";
+ // asCluster exiting cleanly indicates successful auth
+ assert.eq(authutil.asCluster(replTest.nodes, keyfile, () => true), true);
+ ++expected[mech].authenticate.received;
+ ++expected[mech].authenticate.successful;
+ ++expected[mech].clusterAuthenticate.received;
+ ++expected[mech].clusterAuthenticate.successful;
+ // we have to re-auth as admin to get stats, which are validated at the end of assertSuccess
+ assertSuccess({user: 'admin', pwd: 'pwd'}, 'SCRAM-SHA-256', admin);
+}
+
+function assertFailureInternal() {
+ const mech = "SCRAM-SHA-1";
+ // If asCluster fails, it explodes.
+ assert.throws(authutil.asCluster, [replTest.nodes, badKeyfile, () => true]);
+ ++expected[mech].authenticate.received;
+ ++expected[mech].clusterAuthenticate.received;
+ // we have to re-auth as admin to get stats, which are validated at the end of assertSuccess
+ assertSuccess({user: 'admin', pwd: 'pwd'}, 'SCRAM-SHA-256', admin);
assertStats();
}
@@ -86,9 +112,16 @@ assertFailure({user: 'user', pwd: 'haxx', mechanism: 'SCRAM-SHA-1'}, 'SCRAM-SHA-
assertFailure({user: 'user1', pwd: 'pwd', mechanism: 'SCRAM-SHA-256'}, 'SCRAM-SHA-256');
assertFailure({user: 'user256', pwd: 'pwd', mechanism: 'SCRAM-SHA-1'}, 'SCRAM-SHA-1');
+// Cluster auth counter checks.
+assertSuccessInternal();
+assertFailureInternal();
+
+// Need to auth as admin one more time to get final stats.
+admin.auth('admin', 'pwd');
+
const finalStats =
assert.commandWorked(admin.runCommand({serverStatus: 1})).security.authentication.mechanisms;
-MongoRunner.stopMongod(mongod);
+replTest.stopSet();
printjson(finalStats);
})();
diff --git a/jstests/auth/speculative-auth-replset.js b/jstests/auth/speculative-auth-replset.js
index dfa985321a3..d6f9a52c03a 100644
--- a/jstests/auth/speculative-auth-replset.js
+++ b/jstests/auth/speculative-auth-replset.js
@@ -39,14 +39,17 @@ const mechStats =
printjson(mechStats);
assert(mechStats['SCRAM-SHA-256'] !== undefined);
Object.keys(mechStats).forEach(function(mech) {
- const stats = mechStats[mech].speculativeAuthenticate;
+ const specStats = mechStats[mech].speculativeAuthenticate;
+ const clusterStats = mechStats[mech].clusterAuthenticate;
if (mech === 'SCRAM-SHA-256') {
- assert.gte(stats.received, 2);
+ assert.gte(specStats.received, 2);
+ assert.gte(clusterStats.received, 2);
} else {
- assert.eq(stats.received, 0);
+ assert.eq(specStats.received, 0);
}
- assert.eq(stats.received, stats.successful);
+ assert.eq(specStats.received, specStats.successful);
+ assert.eq(clusterStats.received, clusterStats.successful);
});
test(baseURI);
diff --git a/jstests/auth/speculative-auth-sharding.js b/jstests/auth/speculative-auth-sharding.js
index 008eafac08d..d8bcae94ed9 100644
--- a/jstests/auth/speculative-auth-sharding.js
+++ b/jstests/auth/speculative-auth-sharding.js
@@ -20,26 +20,37 @@ let lastStats =
assert.commandWorked(admin.runCommand({serverStatus: 1})).security.authentication.mechanisms;
jsTest.log('Inintial stats: ' + lastStats);
-function test(uri, incrMech) {
+function test(uri, incrMech, isClusterAuth = false) {
jsTest.log('Connecting to: ' + uri);
assert.eq(runMongoProgram('mongo', uri, '--eval', ';'), 0);
const stats = assert.commandWorked(admin.runCommand({serverStatus: 1}))
.security.authentication.mechanisms;
- assert.eq(Object.keys(lastStats).length, Object.keys(stats).length);
- Object.keys(lastStats).forEach(function(mech) {
- const inc = (mech == incrMech) ? 1 : 0;
-
- const specBefore = lastStats[mech].speculativeAuthenticate;
- const specAfter = stats[mech].speculativeAuthenticate;
- assert.eq(specAfter.received, specBefore.received + inc);
- assert.eq(specAfter.successful, specBefore.successful + inc);
-
- const allBefore = lastStats[mech].authenticate;
- const allAfter = stats[mech].authenticate;
- assert.eq(allAfter.received, allBefore.received + inc);
- assert.eq(allAfter.successful, allBefore.successful + inc);
- });
+ try {
+ assert.eq(Object.keys(lastStats).length, Object.keys(stats).length);
+ Object.keys(lastStats).forEach(function(mech) {
+ const inc = (mech === incrMech) ? 1 : 0;
+ const clusterInc = (mech === incrMech && isClusterAuth) ? 1 : 0;
+
+ const specBefore = lastStats[mech].speculativeAuthenticate;
+ const specAfter = stats[mech].speculativeAuthenticate;
+ assert.eq(specAfter.received, specBefore.received + inc);
+ assert.eq(specAfter.successful, specBefore.successful + inc);
+
+ const clusterBefore = lastStats[mech].clusterAuthenticate;
+ const clusterAfter = stats[mech].clusterAuthenticate;
+ assert.eq(clusterAfter.received, clusterBefore.received + clusterInc);
+ assert.eq(clusterAfter.successful, clusterBefore.successful + clusterInc);
+
+ const allBefore = lastStats[mech].authenticate;
+ const allAfter = stats[mech].authenticate;
+ assert.eq(allAfter.received, allBefore.received + inc);
+ assert.eq(allAfter.successful, allBefore.successful + inc);
+ });
+ } catch (e) {
+ print("Stats: " + tojson(stats));
+ throw e;
+ }
lastStats = stats;
}
@@ -48,6 +59,10 @@ const baseURI = 'mongodb://admin:pwd@' + st.s.host + '/admin';
test(baseURI, fallbackMech);
test(baseURI + '?authMechanism=SCRAM-SHA-1', 'SCRAM-SHA-1');
test(baseURI + '?authMechanism=SCRAM-SHA-256', 'SCRAM-SHA-256');
+const systemPass = cat(keyfile).replace(/\s/g, '');
+test('mongodb://__system:' + systemPass + '@' + st.s.host + '/admin?authMechanisms=SCRAM-SHA-256',
+ 'SCRAM-SHA-256',
+ true);
admin.logout();
st.stop();
diff --git a/jstests/auth/speculative-sasl-start.js b/jstests/auth/speculative-sasl-start.js
index 8db5d03dcb7..1518cceeb01 100644
--- a/jstests/auth/speculative-sasl-start.js
+++ b/jstests/auth/speculative-sasl-start.js
@@ -3,7 +3,8 @@
(function() {
'use strict';
-const mongod = MongoRunner.runMongod({auth: ''});
+const keyFile = 'jstests/libs/key1';
+const mongod = MongoRunner.runMongod({auth: '', keyFile: keyFile});
const admin = mongod.getDB('admin');
admin.createUser(
@@ -35,10 +36,22 @@ assertStats(function(mechStats) {
});
});
-function expectN(mechStats, mech, N, M) {
- const stats = mechStats[mech].speculativeAuthenticate;
- assert.eq(N, stats.received);
- assert.eq(M, stats.successful);
+// No "intra-cluster" auth attempts yet.
+assertStats(function(mechStats) {
+ Object.keys(mechStats).forEach(function(mech) {
+ const stats = mechStats[mech].clusterAuthenticate;
+ assert.eq(stats.received, 0);
+ assert.eq(stats.successful, 0);
+ });
+});
+
+function expectN(mechStats, mech, N1, M1, N2 = 0, M2 = 0) {
+ const specStats = mechStats[mech].speculativeAuthenticate;
+ const clusterStats = mechStats[mech].clusterAuthenticate;
+ assert.eq(N1, specStats.received);
+ assert.eq(M1, specStats.successful);
+ assert.eq(N2, clusterStats.received);
+ assert.eq(M2, clusterStats.successful);
}
const baseOKURI = 'mongodb://admin:pwd@localhost:' + mongod.port + '/admin';
@@ -97,5 +110,16 @@ mongod.getDB('test').createUser({user: 'alice', pwd: 'secret', roles: []});
test('mongodb://alice:secret@localhost:' + mongod.port + '/test', true);
assertStats((s) => expectN(s, 'SCRAM-SHA-256', 7, 3));
+// Test "intra-cluster" speculative authentication.
+const systemPass = cat(keyFile).replace(/\s/g, '');
+test('mongodb://__system:' + systemPass + '@localhost:' + mongod.port + '/admin' +
+ '?authMechanism=SCRAM-SHA-256',
+ true);
+assertStats((s) => expectN(s, 'SCRAM-SHA-256', 8, 4, 1, 1));
+test('mongodb://__system:hunter2@localhost:' + mongod.port + '/admin' +
+ '?authMechanism=SCRAM-SHA-256',
+ false);
+assertStats((s) => expectN(s, 'SCRAM-SHA-256', 9, 4, 3, 1));
+
MongoRunner.stopMongod(mongod);
})();
diff --git a/jstests/ssl/auth-counters.js b/jstests/ssl/auth-counters.js
index 6eaafa3735e..04274ef8578 100644
--- a/jstests/ssl/auth-counters.js
+++ b/jstests/ssl/auth-counters.js
@@ -3,11 +3,13 @@
(function() {
'use strict';
+const x509 = "MONGODB-X509";
const mongod = MongoRunner.runMongod({
auth: '',
tlsMode: 'requireTLS',
tlsCertificateKeyFile: 'jstests/libs/server.pem',
tlsCAFile: 'jstests/libs/ca.pem',
+ clusterAuthMode: "x509",
});
const admin = mongod.getDB('admin');
const external = mongod.getDB('$external');
@@ -20,46 +22,79 @@ external.createUser({user: X509USER, roles: []});
// This test ignores counters for SCRAM-SHA-*.
// For those, see jstests/auth/auth-counters.js
-const expected = {
- received: 0,
- successful: 0
-};
+const expected = assert.commandWorked(admin.runCommand({serverStatus: 1}))
+ .security.authentication.mechanisms[x509];
function assertStats() {
const mechStats = assert.commandWorked(admin.runCommand({serverStatus: 1}))
- .security.authentication.mechanisms['MONGODB-X509']
- .authenticate;
- assert.eq(mechStats.received, expected.received);
- assert.eq(mechStats.successful, expected.successful);
+ .security.authentication.mechanisms[x509];
+ try {
+ assert.eq(mechStats.authenticate.received, expected.authenticate.received);
+ assert.eq(mechStats.authenticate.successful, expected.authenticate.successful);
+ assert.eq(mechStats.clusterAuthenticate.received, expected.clusterAuthenticate.received);
+ assert.eq(mechStats.clusterAuthenticate.successful,
+ expected.clusterAuthenticate.successful);
+ } catch (e) {
+ print("mechStats: " + tojson(mechStats));
+ print("expected: " + tojson(expected));
+ throw e;
+ }
}
function assertSuccess(creds) {
assert.eq(external.auth(creds), true);
external.logout();
- ++expected.received;
- ++expected.successful;
+ ++expected.authenticate.received;
+ ++expected.authenticate.successful;
assertStats();
}
function assertFailure(creds) {
assert.eq(external.auth(creds), false);
- ++expected.received;
+ ++expected.authenticate.received;
+ assertStats();
+}
+
+function assertSuccessInternal() {
+ assert.eq(runMongoProgram("mongo",
+ "--tls",
+ "--port",
+ mongod.port,
+ "--tlsCertificateKeyFile",
+ "jstests/libs/server.pem",
+ "--tlsCAFile",
+ "jstests/libs/ca.pem",
+ "--authenticationDatabase",
+ "$external",
+ "--authenticationMechanism",
+ "MONGODB-X509",
+ "--eval",
+ ";"),
+ 0);
+ ++expected.authenticate.received;
+ ++expected.authenticate.successful;
+ ++expected.clusterAuthenticate.received;
+ ++expected.clusterAuthenticate.successful;
assertStats();
}
// User from certificate should work.
-assertSuccess({mechanism: 'MONGODB-X509'});
+assertSuccess({mechanism: x509});
// Explicitly named user.
-assertSuccess({user: X509USER, mechanism: 'MONGODB-X509'});
+assertSuccess({user: X509USER, mechanism: x509});
+
+// Cluster auth counter checks.
+// We can't test failures with the __system user without the handshake failing,
+// which won't increment the counters.
+assertSuccessInternal();
// Fails once the user no longer exists.
external.dropUser(X509USER);
-assertFailure({mechanism: 'MONGODB-X509'});
+assertFailure({mechanism: x509});
const finalStats =
assert.commandWorked(admin.runCommand({serverStatus: 1})).security.authentication.mechanisms;
MongoRunner.stopMongod(mongod);
-
printjson(finalStats);
})();
diff --git a/jstests/ssl/speculative-auth-replset.js b/jstests/ssl/speculative-auth-replset.js
index 3c10b53b678..d23ad32802b 100644
--- a/jstests/ssl/speculative-auth-replset.js
+++ b/jstests/ssl/speculative-auth-replset.js
@@ -37,13 +37,16 @@ const mechStats =
printjson(mechStats);
assert(mechStats['MONGODB-X509'] !== undefined);
Object.keys(mechStats).forEach(function(mech) {
- const stats = mechStats[mech].speculativeAuthenticate;
+ const specStats = mechStats[mech].speculativeAuthenticate;
+ const clusterStats = mechStats[mech].clusterAuthenticate;
if (mech === 'MONGODB-X509') {
- assert.gte(stats.received, 2);
+ assert.gte(specStats.received, 2);
+ assert.gte(clusterStats.received, 2);
} else {
- assert.eq(stats.received, 0);
+ assert.eq(specStats.received, 0);
}
- assert.eq(stats.received, stats.successful);
+ assert.eq(specStats.received, specStats.successful);
+ assert.eq(clusterStats.received, clusterStats.successful);
});
admin.logout();
diff --git a/jstests/ssl/speculative-auth-sharding.js b/jstests/ssl/speculative-auth-sharding.js
index 56af5fddaca..7a198c7983b 100644
--- a/jstests/ssl/speculative-auth-sharding.js
+++ b/jstests/ssl/speculative-auth-sharding.js
@@ -55,6 +55,17 @@ assert.eq(runMongoProgram('mongo',
'--eval',
';'),
0);
+assert.eq(runMongoProgram('mongo',
+ uri,
+ '--tls',
+ '--tlsCertificateKeyFile',
+ SERVER_CERT,
+ '--tlsCAFile',
+ CA_CERT,
+ '--tlsAllowInvalidHostnames',
+ '--eval',
+ ';'),
+ 0);
const authStats = assert.commandWorked(admin.runCommand({serverStatus: 1}))
.security.authentication.mechanisms['MONGODB-X509'];
@@ -63,14 +74,20 @@ jsTest.log('Authenticated stats: ' + tojson(authStats));
// Got and succeeded an additional speculation.
const initSpec = initialStats.speculativeAuthenticate;
const authSpec = authStats.speculativeAuthenticate;
-assert.eq(authSpec.received, initSpec.received + 1);
-assert.eq(authSpec.successful, initSpec.successful + 1);
+assert.eq(authSpec.received, initSpec.received + 2);
+assert.eq(authSpec.successful, initSpec.successful + 2);
// Got and succeeded an additional auth.
const initAuth = initialStats.authenticate;
const authAuth = authStats.authenticate;
-assert.eq(authAuth.received, initAuth.received + 1);
-assert.eq(authAuth.successful, initAuth.successful + 1);
+assert.eq(authAuth.received, initAuth.received + 2);
+assert.eq(authAuth.successful, initAuth.successful + 2);
+
+// Got and succeeded intra-cluster auth.
+const initCluster = initialStats.clusterAuthenticate;
+const authCluster = authStats.clusterAuthenticate;
+assert.eq(authCluster.received, initCluster.received + 1);
+assert.eq(authCluster.successful, initCluster.successful + 1);
/////////////////////////////////////////////////////////////////////////////
diff --git a/jstests/ssl/speculative-authenticate.js b/jstests/ssl/speculative-authenticate.js
index 41b7139230f..492469466df 100644
--- a/jstests/ssl/speculative-authenticate.js
+++ b/jstests/ssl/speculative-authenticate.js
@@ -8,6 +8,7 @@ const mongod = MongoRunner.runMongod({
tlsMode: 'requireTLS',
tlsCertificateKeyFile: 'jstests/libs/server.pem',
tlsCAFile: 'jstests/libs/ca.pem',
+ clusterAuthMode: "x509",
});
const admin = mongod.getDB('admin');
const external = mongod.getDB('$external');
@@ -32,6 +33,19 @@ function test(uri) {
assert.eq(0, x509);
}
+function testInternal(uri) {
+ const x509 = runMongoProgram('mongo',
+ '--tls',
+ '--tlsCAFile',
+ 'jstests/libs/ca.pem',
+ '--tlsCertificateKeyFile',
+ 'jstests/libs/server.pem',
+ uri,
+ '--eval',
+ ';');
+ assert.eq(0, x509);
+}
+
function assertStats(cb) {
const mechStats = assert.commandWorked(admin.runCommand({serverStatus: 1}))
.security.authentication.mechanisms;
@@ -64,5 +78,23 @@ assertStats(function(mechStats) {
assert.eq(stats.successful, 1);
});
+// We haven't done any cluster auth yet, so clusterAuthenticate counts should be 0
+assertStats(function(mechStats) {
+ const stats = mechStats['MONGODB-X509'].clusterAuthenticate;
+ assert.eq(stats.received, 0);
+ assert.eq(stats.successful, 0);
+});
+
+// Connect intra-cluster with speculation.
+testInternal(baseURI + '?authMechanism=MONGODB-X509');
+assertStats(function(mechStats) {
+ const specStats = mechStats['MONGODB-X509'].speculativeAuthenticate;
+ const clusterStats = mechStats['MONGODB-X509'].clusterAuthenticate;
+ assert.eq(specStats.received, 2);
+ assert.eq(specStats.successful, 2);
+ assert.eq(clusterStats.received, 1);
+ assert.eq(clusterStats.successful, 1);
+});
+
MongoRunner.stopMongod(mongod);
})();
diff --git a/src/mongo/client/authenticate.h b/src/mongo/client/authenticate.h
index ee50f8c8ade..44d90eae612 100644
--- a/src/mongo/client/authenticate.h
+++ b/src/mongo/client/authenticate.h
@@ -72,6 +72,7 @@ constexpr auto kMechanismMongoAWS = "MONGODB-AWS"_sd;
constexpr auto kInternalAuthFallbackMechanism = kMechanismScramSha1;
constexpr auto kSpeculativeAuthenticate = "speculativeAuthenticate"_sd;
+constexpr auto kClusterAuthenticate = "clusterAuthenticate"_sd;
constexpr auto kAuthenticateCommand = "authenticate"_sd;
/**
diff --git a/src/mongo/db/auth/sasl_commands.cpp b/src/mongo/db/auth/sasl_commands.cpp
index 7f4b1010462..81d784e64a3 100644
--- a/src/mongo/db/auth/sasl_commands.cpp
+++ b/src/mongo/db/auth/sasl_commands.cpp
@@ -335,18 +335,26 @@ bool runSaslStart(OperationContext* opCtx,
}
std::string principalName;
- auto swSession = doSaslStart(opCtx, db, cmdObj, &result, &principalName, speculative);
-
- if (!swSession.isOK() || swSession.getValue()->getMechanism().isSuccess()) {
- audit::logAuthentication(
- client, mechanismName, UserName(principalName, db), swSession.getStatus().code());
- uassertStatusOK(swSession.getStatus());
- if (swSession.getValue()->getMechanism().isSuccess()) {
+ try {
+ auto session =
+ uassertStatusOK(doSaslStart(opCtx, db, cmdObj, &result, &principalName, speculative));
+ const bool isClusterMember = session->getMechanism().isClusterMember();
+ if (isClusterMember) {
+ uassertStatusOK(authCounter.incClusterAuthenticateReceived(mechanismName));
+ }
+ if (session->getMechanism().isSuccess()) {
uassertStatusOK(authCounter.incAuthenticateSuccessful(mechanismName));
+ if (isClusterMember) {
+ uassertStatusOK(authCounter.incClusterAuthenticateSuccessful(mechanismName));
+ }
+ audit::logAuthentication(
+ client, mechanismName, UserName(principalName, db), Status::OK().code());
+ } else {
+ AuthenticationSession::swap(client, session);
}
- } else {
- auto session = std::move(swSession.getValue());
- AuthenticationSession::swap(client, session);
+ } catch (const AssertionException& ex) {
+ audit::logAuthentication(client, mechanismName, UserName(principalName, db), ex.code());
+ throw;
}
return true;
@@ -408,6 +416,10 @@ bool CmdSaslContinue::run(OperationContext* opCtx,
if (mechanism.isSuccess()) {
uassertStatusOK(
authCounter.incAuthenticateSuccessful(mechanism.mechanismName().toString()));
+ if (mechanism.isClusterMember()) {
+ uassertStatusOK(authCounter.incClusterAuthenticateSuccessful(
+ mechanism.mechanismName().toString()));
+ }
}
} else {
AuthenticationSession::swap(client, sessionGuard);
diff --git a/src/mongo/db/auth/sasl_mechanism_registry.h b/src/mongo/db/auth/sasl_mechanism_registry.h
index 98f2d8ddae9..0215328d9cb 100644
--- a/src/mongo/db/auth/sasl_mechanism_registry.h
+++ b/src/mongo/db/auth/sasl_mechanism_registry.h
@@ -155,6 +155,15 @@ public:
}
/**
+ * Provides logic for determining if a user is a cluster member or an actual client for SASL
+ * authentication mechanisms
+ */
+ bool isClusterMember() const {
+ return _principalName == internalSecurity.user->getName().getUser().toString() &&
+ getAuthenticationDatabase() == internalSecurity.user->getName().getDB();
+ };
+
+ /**
* Performs a single step of a SASL exchange. Takes an input provided by a client,
* and either returns an error, or a response to be sent back.
*/
diff --git a/src/mongo/db/commands/authentication_commands.cpp b/src/mongo/db/commands/authentication_commands.cpp
index 77f014207fb..c3ba51aef15 100644
--- a/src/mongo/db/commands/authentication_commands.cpp
+++ b/src/mongo/db/commands/authentication_commands.cpp
@@ -44,6 +44,7 @@
#include "mongo/client/sasl_client_authenticate.h"
#include "mongo/config.h"
#include "mongo/db/audit.h"
+#include "mongo/db/auth/authentication_session.h"
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/auth/privilege.h"
#include "mongo/db/auth/sasl_options.h"
@@ -292,48 +293,48 @@ bool CmdAuthenticate::run(OperationContext* opCtx,
user = internalSecurity.user->getName();
}
- Status status = authCounter.incAuthenticateReceived(mechanism);
- if (status.isOK()) {
- status = _authenticate(opCtx, mechanism, user, cmdObj);
- }
- audit::logAuthentication(Client::getCurrent(), mechanism, user, status.code());
+ try {
+ uassertStatusOK(authCounter.incAuthenticateReceived(mechanism));
+ const bool isClusterMember =
+ opCtx->getClient()->session()->getSSLConfiguration()->isClusterMember(user.getUser());
+ if (isClusterMember) {
+ uassertStatusOK(authCounter.incClusterAuthenticateReceived(mechanism));
+ }
+
+ uassertStatusOK(_authenticate(opCtx, mechanism, user, cmdObj));
+ audit::logAuthentication(opCtx->getClient(), mechanism, user, Status::OK().code());
- if (!status.isOK()) {
if (!serverGlobalParams.quiet.load()) {
- auto const client = opCtx->getClient();
+ LOGV2(20429,
+ "Successfully authenticated as principal {user} on {db} from client {client}",
+ "Successfully authenticated",
+ "user"_attr = user.getUser(),
+ "db"_attr = user.getDB(),
+ "client"_attr = opCtx->getClient()->session()->remote());
+ }
+
+ uassertStatusOK(authCounter.incAuthenticateSuccessful(mechanism));
+ if (isClusterMember) {
+ uassertStatusOK(authCounter.incClusterAuthenticateSuccessful(mechanism));
+ }
+
+ result.append("dbname", user.getDB());
+ result.append("user", user.getUser());
+ return true;
+ } catch (const AssertionException& ex) {
+ auto status = ex.toStatus();
+ auto const client = opCtx->getClient();
+ audit::logAuthentication(Client::getCurrent(), mechanism, user, status.code());
+ if (!serverGlobalParams.quiet.load()) {
LOGV2(20428,
- "Failed to authenticate {user} from client {client} with mechanism "
- "{mechanism}: {error}",
"Failed to authenticate",
"user"_attr = user,
"client"_attr = client->getRemote(),
"mechanism"_attr = mechanism,
"error"_attr = status);
}
- sleepmillis(saslGlobalParams.authFailedDelay.load());
- if (status.code() == ErrorCodes::AuthenticationFailed) {
- // Statuses with code AuthenticationFailed may contain messages we do not wish to
- // reveal to the user, so we return a status with the message "auth failed".
- uasserted(ErrorCodes::AuthenticationFailed, "auth failed");
- } else {
- uassertStatusOK(status);
- }
- return false;
+ throw;
}
-
- if (!serverGlobalParams.quiet.load()) {
- LOGV2(20429,
- "Successfully authenticated as principal {user} on {db} from client {client}",
- "Successfully authenticated",
- "user"_attr = user.getUser(),
- "db"_attr = user.getDB(),
- "client"_attr = opCtx->getClient()->session()->remote());
- }
-
- uassertStatusOK(authCounter.incAuthenticateSuccessful(mechanism));
- result.append("dbname", user.getDB());
- result.append("user", user.getUser());
- return true;
}
Status CmdAuthenticate::_authenticate(OperationContext* opCtx,
diff --git a/src/mongo/db/stats/counters.cpp b/src/mongo/db/stats/counters.cpp
index 63a0b6eb66d..4180275393b 100644
--- a/src/mongo/db/stats/counters.cpp
+++ b/src/mongo/db/stats/counters.cpp
@@ -244,6 +244,24 @@ Status AuthCounter::incAuthenticateSuccessful(const std::string& mechanism) try
<< " which is not enabled"};
}
+Status AuthCounter::incClusterAuthenticateReceived(const std::string& mechanism) try {
+ _mechanisms.at(mechanism).clusterAuthenticate.received.fetchAndAddRelaxed(1);
+ return Status::OK();
+} catch (const std::out_of_range&) {
+ return {ErrorCodes::BadValue,
+ str::stream() << "Received authentication for mechanism " << mechanism
+ << " which is unknown or not enabled"};
+}
+
+Status AuthCounter::incClusterAuthenticateSuccessful(const std::string& mechanism) try {
+ _mechanisms.at(mechanism).clusterAuthenticate.successful.fetchAndAddRelaxed(1);
+ return Status::OK();
+} catch (const std::out_of_range&) {
+ return {ErrorCodes::BadValue,
+ str::stream() << "Received authentication for mechanism " << mechanism
+ << " which is not enabled"};
+}
+
/**
* authentication: {
* "mechanisms": {
@@ -275,6 +293,16 @@ void AuthCounter::append(BSONObjBuilder* b) {
}
{
+ const auto received = it.second.clusterAuthenticate.received.load();
+ const auto successful = it.second.clusterAuthenticate.successful.load();
+
+ BSONObjBuilder clusterAuthBuilder(mechBuilder.subobjStart(auth::kClusterAuthenticate));
+ clusterAuthBuilder.append("received", received);
+ clusterAuthBuilder.append("successful", successful);
+ clusterAuthBuilder.done();
+ }
+
+ {
const auto received = it.second.authenticate.received.load();
const auto successful = it.second.authenticate.successful.load();
diff --git a/src/mongo/db/stats/counters.h b/src/mongo/db/stats/counters.h
index 9b10cb2a049..a202746be03 100644
--- a/src/mongo/db/stats/counters.h
+++ b/src/mongo/db/stats/counters.h
@@ -226,6 +226,9 @@ public:
Status incAuthenticateReceived(const std::string& mechanism);
Status incAuthenticateSuccessful(const std::string& mechanism);
+ Status incClusterAuthenticateReceived(const std::string& mechanism);
+ Status incClusterAuthenticateSuccessful(const std::string& mechanism);
+
void append(BSONObjBuilder*);
void initializeMechanismMap(const std::vector<std::string>&);
@@ -240,6 +243,10 @@ private:
AtomicWord<long long> received;
AtomicWord<long long> successful;
} authenticate;
+ struct {
+ AtomicWord<long long> received;
+ AtomicWord<long long> successful;
+ } clusterAuthenticate;
};
using MechanismMap = std::map<std::string, MechanismData>;