diff options
author | Sara Golemon <sara.golemon@mongodb.com> | 2020-05-04 14:47:29 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-05-04 23:52:39 +0000 |
commit | ebf32d3a3e3f297d981053337b104fca4a32ac9e (patch) | |
tree | 8c001d209260191984dd422d01656e5092507355 | |
parent | 066c614a5672d63f4127752da2befc7477780320 (diff) | |
download | mongo-ebf32d3a3e3f297d981053337b104fca4a32ac9e.tar.gz |
SERVER-47908 Wire speculativeAuthenticate into mongos
(cherry picked from commit 845b52ae1c13f10d79993950888819347fac3aa3)
-rw-r--r-- | jstests/auth/speculative-auth-sharding.js | 54 | ||||
-rw-r--r-- | jstests/ssl/speculative-auth-sharding.js | 91 | ||||
-rw-r--r-- | src/mongo/db/repl/SConscript | 15 | ||||
-rw-r--r-- | src/mongo/db/repl/replication_info.cpp | 28 | ||||
-rw-r--r-- | src/mongo/db/repl/speculative_auth.cpp | 69 | ||||
-rw-r--r-- | src/mongo/db/repl/speculative_auth.h | 44 | ||||
-rw-r--r-- | src/mongo/s/commands/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/s/commands/cluster_command_test_fixture.cpp | 6 | ||||
-rw-r--r-- | src/mongo/s/commands/cluster_is_master_cmd.cpp | 3 |
9 files changed, 283 insertions, 28 deletions
diff --git a/jstests/auth/speculative-auth-sharding.js b/jstests/auth/speculative-auth-sharding.js new file mode 100644 index 00000000000..008eafac08d --- /dev/null +++ b/jstests/auth/speculative-auth-sharding.js @@ -0,0 +1,54 @@ +// Verify that clients can speculatively authenticate to mongos. +// @tags: [requires_sharding] + +(function() { +'use strict'; + +const fallbackMech = 'SCRAM-SHA-256'; +const keyfile = 'jstests/libs/key1'; +const st = new ShardingTest({ + mongos: 1, + keyFile: keyfile, + other: {mongosOptions: {auth: null}, configOptions: {auth: null}, shardOptions: {auth: null}} +}); + +const admin = st.s.getDB('admin'); +admin.createUser({user: 'admin', pwd: 'pwd', roles: ['root']}); +admin.auth('admin', 'pwd'); + +let lastStats = + assert.commandWorked(admin.runCommand({serverStatus: 1})).security.authentication.mechanisms; +jsTest.log('Inintial stats: ' + lastStats); + +function test(uri, incrMech) { + 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); + }); + lastStats = stats; +} + +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'); + +admin.logout(); +st.stop(); +}()); diff --git a/jstests/ssl/speculative-auth-sharding.js b/jstests/ssl/speculative-auth-sharding.js new file mode 100644 index 00000000000..56af5fddaca --- /dev/null +++ b/jstests/ssl/speculative-auth-sharding.js @@ -0,0 +1,91 @@ +// Verify that speculative auth works with mongos. +// @tags: [requires_sharding] + +(function() { +'use strict'; + +const CLIENT_NAME = 'CN=client,OU=KernelUser,O=MongoDB,L=New York City,ST=New York,C=US'; +const CLIENT_CERT = 'jstests/libs/client.pem'; +const SERVER_CERT = 'jstests/libs/server.pem'; +const CLUSTER_CERT = 'jstests/libs/cluster_cert.pem'; +const CA_CERT = 'jstests/libs/ca.pem'; + +const options = { + tlsMode: 'requireTLS', + tlsCertificateKeyFile: SERVER_CERT, + tlsCAFile: CA_CERT, + tlsClusterFile: CLUSTER_CERT, + tlsAllowInvalidHostnames: '', + clusterAuthMode: 'x509', +}; + +const st = new ShardingTest({ + shards: 1, + other: { + enableBalancer: true, + configOptions: options, + mongosOptions: options, + rsOptions: options, + shardOptions: options, + shardAsReplicaSet: false, + } +}); + +const admin = st.s.getDB('admin'); +admin.createUser({user: 'admin', pwd: 'pwd', roles: ['root']}); +assert(admin.auth('admin', 'pwd')); + +const external = st.s.getDB('$external'); +external.createUser({user: CLIENT_NAME, roles: [{role: '__system', db: 'admin'}]}); + +const initialStats = assert.commandWorked(admin.runCommand({serverStatus: 1})) + .security.authentication.mechanisms['MONGODB-X509']; +jsTest.log('Initial stats: ' + tojson(initialStats)); + +const uri = 'mongodb://' + st.s.host + '/admin?authMechanism=MONGODB-X509'; +jsTest.log('Connecting to: ' + uri); +assert.eq(runMongoProgram('mongo', + uri, + '--tls', + '--tlsCertificateKeyFile', + CLIENT_CERT, + '--tlsCAFile', + CA_CERT, + '--tlsAllowInvalidHostnames', + '--eval', + ';'), + 0); + +const authStats = assert.commandWorked(admin.runCommand({serverStatus: 1})) + .security.authentication.mechanisms['MONGODB-X509']; +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); + +// 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); + +///////////////////////////////////////////////////////////////////////////// + +jsTest.log('Shutting down'); + +// Authenticate csrs so ReplSetTest.stopSet() can do db hash check. +if (st.configRS) { + st.configRS.nodes.forEach((node) => { + node.getDB('admin').auth('admin', 'pwd'); + }); +} + +// Orphan checks needs a privileged user to auth as. +st.shard0.getDB('$external') + .createUser({user: CLIENT_NAME, roles: [{role: '__system', db: 'admin'}]}); + +st.stop(); +}()); diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript index 558e81835f9..3a47455aedd 100644 --- a/src/mongo/db/repl/SConscript +++ b/src/mongo/db/repl/SConscript @@ -1118,9 +1118,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/repl/speculative_authenticate', '$BUILD_DIR/mongo/db/stats/counters', '$BUILD_DIR/mongo/transport/message_compressor', 'replication_auth', @@ -1424,3 +1423,15 @@ env.Library( 'replica_set_messages', ], ) + +env.Library( + target='speculative_authenticate', + source=[ + 'speculative_auth.cpp', + ], + LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/auth/authservercommon', + '$BUILD_DIR/mongo/db/commands/authentication_commands', + ], +) diff --git a/src/mongo/db/repl/replication_info.cpp b/src/mongo/db/repl/replication_info.cpp index 9d79aed4f42..e105a3469d9 100644 --- a/src/mongo/db/repl/replication_info.cpp +++ b/src/mongo/db/repl/replication_info.cpp @@ -36,11 +36,8 @@ #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" @@ -56,6 +53,7 @@ #include "mongo/db/repl/replication_auth.h" #include "mongo/db/repl/replication_coordinator.h" #include "mongo/db/repl/replication_process.h" +#include "mongo/db/repl/speculative_auth.h" #include "mongo/db/repl/storage_interface.h" #include "mongo/db/storage/storage_options.h" #include "mongo/db/wire_version.h" @@ -547,29 +545,7 @@ 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); - } - } + handleIsMasterSpeculativeAuth(opCtx, cmdObj, &result); return true; } diff --git a/src/mongo/db/repl/speculative_auth.cpp b/src/mongo/db/repl/speculative_auth.cpp new file mode 100644 index 00000000000..292df012aa1 --- /dev/null +++ b/src/mongo/db/repl/speculative_auth.cpp @@ -0,0 +1,69 @@ +/** + * 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/db/repl/speculative_auth.h" + +#include "mongo/client/authenticate.h" +#include "mongo/db/auth/sasl_command_constants.h" +#include "mongo/db/auth/sasl_commands.h" +#include "mongo/db/commands/authentication_commands.h" + +namespace mongo { + +void handleIsMasterSpeculativeAuth(OperationContext* opCtx, + BSONObj cmdObj, + BSONObjBuilder* result) { + auto sae = cmdObj[auth::kSpeculativeAuthenticate]; + if (sae.eoo()) { + return; + } + + 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); + } +} + +} // namespace mongo diff --git a/src/mongo/db/repl/speculative_auth.h b/src/mongo/db/repl/speculative_auth.h new file mode 100644 index 00000000000..03f071f652e --- /dev/null +++ b/src/mongo/db/repl/speculative_auth.h @@ -0,0 +1,44 @@ +/** + * 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. + */ + +#pragma once + +#include "mongo/bson/bsonobj.h" +#include "mongo/bson/bsonobjbuilder.h" +#include "mongo/db/operation_context.h" + +namespace mongo { + +/** + * Check an isMaster sent to mongod in ReplSet mode or mongos for "speculativeAuthenticate". + * If present, dispatch to saslStart or authenticate commands as appropriate. + */ +void handleIsMasterSpeculativeAuth(OperationContext* opCtx, BSONObj cmdObj, BSONObjBuilder* result); + +} // namespace mongo diff --git a/src/mongo/s/commands/SConscript b/src/mongo/s/commands/SConscript index 323d81106a2..7495d50b2f2 100644 --- a/src/mongo/s/commands/SConscript +++ b/src/mongo/s/commands/SConscript @@ -122,6 +122,7 @@ env.Library( '$BUILD_DIR/mongo/db/shared_request_handling', '$BUILD_DIR/mongo/db/logical_session_cache_impl', '$BUILD_DIR/mongo/db/read_write_concern_defaults', + '$BUILD_DIR/mongo/db/repl/speculative_authenticate', '$BUILD_DIR/mongo/db/pipeline/aggregation', '$BUILD_DIR/mongo/db/query/command_request_response', '$BUILD_DIR/mongo/db/query/map_reduce_output_format', diff --git a/src/mongo/s/commands/cluster_command_test_fixture.cpp b/src/mongo/s/commands/cluster_command_test_fixture.cpp index 243888c115a..f3fe69feda8 100644 --- a/src/mongo/s/commands/cluster_command_test_fixture.cpp +++ b/src/mongo/s/commands/cluster_command_test_fixture.cpp @@ -43,6 +43,7 @@ #include "mongo/db/logical_time_validator.h" #include "mongo/s/cluster_last_error_info.h" #include "mongo/util/fail_point.h" +#include "mongo/util/options_parser/startup_option_init.h" #include "mongo/util/tick_source_mock.h" namespace mongo { @@ -304,4 +305,9 @@ void ClusterCommandTestFixture::appendTxnResponseMetadata(BSONObjBuilder& bob) { txnResponseMetadata.serialize(&bob); } +// Satisfies dependency from StoreSASLOPtions. +MONGO_STARTUP_OPTIONS_STORE(CoreOptions)(InitializerContext*) { + return Status::OK(); +} + } // namespace mongo diff --git a/src/mongo/s/commands/cluster_is_master_cmd.cpp b/src/mongo/s/commands/cluster_is_master_cmd.cpp index 08ba86ea777..dba324e4a56 100644 --- a/src/mongo/s/commands/cluster_is_master_cmd.cpp +++ b/src/mongo/s/commands/cluster_is_master_cmd.cpp @@ -37,6 +37,7 @@ #include "mongo/db/logical_session_id.h" #include "mongo/db/operation_context.h" #include "mongo/db/ops/write_ops.h" +#include "mongo/db/repl/speculative_auth.h" #include "mongo/db/wire_version.h" #include "mongo/logv2/log.h" #include "mongo/rpc/metadata/client_metadata.h" @@ -235,6 +236,8 @@ public: } } + handleIsMasterSpeculativeAuth(opCtx, cmdObj, &result); + return true; } |