From 0cac5b8ebcbf1bf5e0abfa3235c6ad15d85f3934 Mon Sep 17 00:00:00 2001 From: Spencer Jackson Date: Wed, 20 Jan 2021 11:13:41 -0500 Subject: SERVER-50538 Control intracluster traffic in speculative auth tests --- jstests/auth/speculative-auth-replset.js | 87 ++++++++++++++--------- jstests/ssl/speculative-auth-replset.js | 76 +++++++++++++++----- src/mongo/db/commands/authentication_commands.cpp | 3 +- 3 files changed, 115 insertions(+), 51 deletions(-) diff --git a/jstests/auth/speculative-auth-replset.js b/jstests/auth/speculative-auth-replset.js index d6f9a52c03a..594d83988bf 100644 --- a/jstests/auth/speculative-auth-replset.js +++ b/jstests/auth/speculative-auth-replset.js @@ -5,56 +5,77 @@ (function() { 'use strict'; -const rst = new ReplSetTest({nodes: 3, keyFile: 'jstests/libs/key1'}); +const rst = new ReplSetTest({nodes: 1, keyFile: 'jstests/libs/key1'}); rst.startSet(); rst.initiate(); -rst.awaitSecondaryNodes(); const admin = rst.getPrimary().getDB('admin'); admin.createUser({user: 'admin', pwd: 'pwd', roles: ['root']}); admin.auth('admin', 'pwd'); -const baseURI = (function() { - let uri = 'mongodb://admin:pwd@'; - - for (let i = 0; i < rst.ports.length; ++i) { - if (i > 0) { - uri = uri + ','; - } - uri = uri + rst.host + ':' + rst.ports[i]; - } - - return uri + '/admin?replicaSet=' + rst.name; -})(); - -function test(uri) { - assert.eq(runMongoProgram('mongo', uri, '--eval', ';'), 0); +function getMechStats(db) { + return assert.commandWorked(db.runCommand({serverStatus: 1})) + .security.authentication.mechanisms; } +// Capture statistics after a fresh instantiation of a 1-node replica set. +const initialMechStats = getMechStats(admin); +printjson(initialMechStats); +assert(initialMechStats['SCRAM-SHA-256'] !== undefined); + // We've made no client connections for which speculation was possible, -// since this connection came in during localhost auth bypass. -// However we should have non-zero SCRAM-SHA-256 successes using internal auth. -const mechStats = - assert.commandWorked(admin.runCommand({serverStatus: 1})).security.authentication.mechanisms; -printjson(mechStats); -assert(mechStats['SCRAM-SHA-256'] !== undefined); -Object.keys(mechStats).forEach(function(mech) { - const specStats = mechStats[mech].speculativeAuthenticate; - const clusterStats = mechStats[mech].clusterAuthenticate; +// because we authenticated as `admin` using the shell helpers. +// Because of the simple cluster topology, we should have no intracluster authentication attempts. +Object.keys(initialMechStats).forEach(function(mech) { + const specStats = initialMechStats[mech].speculativeAuthenticate; + const clusterStats = initialMechStats[mech].clusterAuthenticate; if (mech === 'SCRAM-SHA-256') { - assert.gte(specStats.received, 2); - assert.gte(clusterStats.received, 2); - } else { - assert.eq(specStats.received, 0); + // It appears that replication helpers use SCRAM-SHA-1, preventing SCRAM-SHA-256 cluster + // stats from being incremented during test setup. + assert.eq(clusterStats.received, 0); } + + // No speculation has occured + assert.eq(specStats.received, 0); + + // Statistics should be consistent for all mechanisms assert.eq(specStats.received, specStats.successful); assert.eq(clusterStats.received, clusterStats.successful); }); -test(baseURI); -test(baseURI + '&authMechanism=SCRAM-SHA-1'); -test(baseURI + '&authMechanism=SCRAM-SHA-256'); +{ + // Add and remove a node to force intra-cluster traffic, and authentication attempts. + // Removal will require force-reconfig because the original node will not constitute a + // "majority" of the resulting two node replicaset. + const singleNodeConfig = rst.getReplSetConfigFromNode(); + + const newNode = rst.add({}); + rst.reInitiate(); + rst.waitForState(newNode, ReplSetTest.State.SECONDARY); + + rst.stop(newNode); + rst.remove(newNode); + admin.auth('admin', 'pwd'); + singleNodeConfig.version = rst.getReplSetConfigFromNode(0).version + 1; + assert.commandWorked(admin.runCommand({replSetReconfig: singleNodeConfig, force: true})); +} + +{ + // Capture new statistics, and assert that they're consistent. + const newMechStats = getMechStats(admin); + printjson(newMechStats); + assert.eq(newMechStats["SCRAM-SHA-256"].speculativeAuthenticate.received, + newMechStats["SCRAM-SHA-256"].speculativeAuthenticate.successful); + assert.eq(newMechStats["SCRAM-SHA-256"].clusterAuthenticate.received, + newMechStats["SCRAM-SHA-256"].clusterAuthenticate.successful); + + // Speculative and cluster statistics should be incremented by intracluster auth. + assert.gt(newMechStats["SCRAM-SHA-256"].speculativeAuthenticate.received, + initialMechStats["SCRAM-SHA-256"].speculativeAuthenticate.successful); + assert.gt(newMechStats["SCRAM-SHA-256"].clusterAuthenticate.received, + initialMechStats["SCRAM-SHA-256"].clusterAuthenticate.successful); +} admin.logout(); rst.stopSet(); diff --git a/jstests/ssl/speculative-auth-replset.js b/jstests/ssl/speculative-auth-replset.js index 51f9e2c8154..23930c4811a 100644 --- a/jstests/ssl/speculative-auth-replset.js +++ b/jstests/ssl/speculative-auth-replset.js @@ -13,7 +13,7 @@ const x509_options = { }; const rst = new ReplSetTest({ - nodes: 3, + nodes: 1, nodeOptions: x509_options, // ReplSetTest needs a keyFile present in order to know we want intracluster auth. @@ -24,31 +24,75 @@ const rst = new ReplSetTest({ rst.startSet(); rst.initiate(); -rst.awaitSecondaryNodes(); const admin = rst.getPrimary().getDB('admin'); admin.createUser({user: 'admin', pwd: 'pwd', roles: ['root']}); admin.auth('admin', 'pwd'); -// We should have non-zero MONGODB-X509 successes using internal auth. -// And we should have no other types of speculative authentications. -const mechStats = - assert.commandWorked(admin.runCommand({serverStatus: 1})).security.authentication.mechanisms; -printjson(mechStats); -assert(mechStats['MONGODB-X509'] !== undefined); -Object.keys(mechStats).forEach(function(mech) { - const specStats = mechStats[mech].speculativeAuthenticate; - const clusterStats = mechStats[mech].clusterAuthenticate; +function getMechStats(db) { + return assert.commandWorked(db.runCommand({serverStatus: 1})) + .security.authentication.mechanisms; +} + +// Capture statistics after a fresh instantiation of a 1-node replica set. +const initialMechStats = getMechStats(admin); +printjson(initialMechStats); +assert(initialMechStats['MONGODB-X509'] !== undefined); + +// We've made no client connections for which speculation was possible, +// because we authenticated as `admin` using the shell helpers with SCRAM. +// Because of the simple cluster topology, we should have no intracluster authentication attempts. +Object.keys(initialMechStats).forEach(function(mech) { + const specStats = initialMechStats[mech].speculativeAuthenticate; + const clusterStats = initialMechStats[mech].clusterAuthenticate; + if (mech === 'MONGODB-X509') { - assert.gte(specStats.received, 2); - assert.gte(clusterStats.received, 2); - } else { - assert.eq(specStats.received, 0); + // It appears that replication helpers use SCRAM-SHA-1, preventing SCRAM-SHA-256 cluster + // stats from being incremented during test setup. + assert.eq(clusterStats.received, 0); } + + // No speculation has occured + assert.eq(specStats.received, 0); + + // Statistics should be consistent for all mechanisms assert.eq(specStats.received, specStats.successful); - assert.gte(clusterStats.received, clusterStats.successful); + assert.eq(clusterStats.received, clusterStats.successful); }); +{ + // Add and remove a node to force intra-cluster traffic, and authentication attempts. + // Removal will require force-reconfig because the original node will not constitute a + // "majority" of the resulting two node replicaset. + const singleNodeConfig = rst.getReplSetConfigFromNode(); + + const newNode = rst.add(x509_options); + rst.reInitiate(); + rst.waitForState(newNode, ReplSetTest.State.SECONDARY); + + rst.stop(newNode); + rst.remove(newNode); + admin.auth('admin', 'pwd'); + singleNodeConfig.version = rst.getReplSetConfigFromNode(0).version + 1; + assert.commandWorked(admin.runCommand({replSetReconfig: singleNodeConfig, force: true})); +} + +{ + // Capture new statistics, and assert that they're consistent. + const newMechStats = getMechStats(admin); + printjson(newMechStats); + assert.eq(newMechStats["MONGODB-X509"].speculativeAuthenticate.received, + newMechStats["MONGODB-X509"].speculativeAuthenticate.successful); + assert.eq(newMechStats["MONGODB-X509"].clusterAuthenticate.received, + newMechStats["MONGODB-X509"].clusterAuthenticate.successful); + + // Speculative and cluster statistics should be incremented by intracluster auth. + assert.gt(newMechStats["MONGODB-X509"].speculativeAuthenticate.received, + initialMechStats["MONGODB-X509"].speculativeAuthenticate.successful); + assert.gt(newMechStats["MONGODB-X509"].clusterAuthenticate.received, + initialMechStats["MONGODB-X509"].clusterAuthenticate.successful); +} + admin.logout(); rst.stopSet(); }()); diff --git a/src/mongo/db/commands/authentication_commands.cpp b/src/mongo/db/commands/authentication_commands.cpp index a54060a68e0..485b292c7a2 100644 --- a/src/mongo/db/commands/authentication_commands.cpp +++ b/src/mongo/db/commands/authentication_commands.cpp @@ -244,9 +244,8 @@ void _authenticateX509(OperationContext* opCtx, UserName& user, StringData dbnam "Client isn't a mongod or mongos, but is connecting with a " "certificate with cluster membership"); } - - authCounter.getMechanismCounter("MONGODB-X509").incClusterAuthenticateSuccessful(); } + authCounter.getMechanismCounter("MONGODB-X509").incClusterAuthenticateSuccessful(); authorizationSession->grantInternalAuthorization(client); } else { -- cgit v1.2.1