diff options
-rw-r--r-- | jstests/ssl/x509_client.js | 52 | ||||
-rw-r--r-- | jstests/ssl/x509_enforce_user_cluster_separation.js | 205 | ||||
-rw-r--r-- | src/mongo/db/auth/SConscript | 12 | ||||
-rw-r--r-- | src/mongo/db/auth/auth_options.idl | 6 | ||||
-rw-r--r-- | src/mongo/db/commands/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/db/commands/authentication_commands.cpp | 101 | ||||
-rw-r--r-- | src/mongo/db/commands/user_management_commands.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/commands/user_management_commands.idl | 9 | ||||
-rw-r--r-- | src/mongo/shell/shardingtest.js | 40 | ||||
-rw-r--r-- | src/mongo/util/net/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_options_server.cpp | 7 |
11 files changed, 327 insertions, 109 deletions
diff --git a/jstests/ssl/x509_client.js b/jstests/ssl/x509_client.js index 725006f37e0..dd867a1c134 100644 --- a/jstests/ssl/x509_client.js +++ b/jstests/ssl/x509_client.js @@ -18,12 +18,10 @@ MongoRunner.stopMongod(conn); const SERVER_CERT = "jstests/libs/server.pem"; const CA_CERT = "jstests/libs/ca.pem"; -const SERVER_USER = "C=US,ST=New York,L=New York City,O=MongoDB,OU=Kernel,CN=server"; -const INTERNAL_USER = "C=US,ST=New York,L=New York City,O=MongoDB,OU=Kernel,CN=internal"; const CLIENT_USER = "CN=client,OU=KernelUser,O=MongoDB,L=New York City,ST=New York,C=US"; const INVALID_CLIENT_USER = "C=US,ST=New York,L=New York City,O=MongoDB,OU=KernelUser,CN=invalid"; -function authAndTest(mongo, {clusterUserSeparationOveride = false} = {}) { +function authAndTest(mongo) { external = mongo.getDB("$external"); test = mongo.getDB("test"); @@ -61,21 +59,23 @@ function authAndTest(mongo, {clusterUserSeparationOveride = false} = {}) { assert(log.some((line) => successRegex.test(line))); - const overrideDependentTester = - clusterUserSeparationOveride ? assert.doesNotThrow : assert.throws; - // It should be impossible to create users with the same name as the server's subject, - // unless guardrails are explicitly overridden - overrideDependentTester(function() { + let createServerUser = function() { + // It should be impossible to create users with the same name as the server's subject, + // unless guardrails are explicitly overridden external.createUser( {user: SERVER_USER, roles: [{'role': 'userAdminAnyDatabase', 'db': 'admin'}]}); - }, [], "Created user with same name as the server's x.509 subject"); + }; + assert.throws( + createServerUser, [], "Created user with same name as the server's x.509 subject"); - // It should be impossible to create users with names recognized as cluster members, - // unless guardrails are explicitly overridden - overrideDependentTester(function() { + let createInternalUser = function() { + // It should be impossible to create users with names recognized as cluster members, + // unless guardrails are explicitly overridden external.createUser( {user: INTERNAL_USER, roles: [{'role': 'userAdminAnyDatabase', 'db': 'admin'}]}); - }, [], "Created user which would be recognized as a cluster member"); + }; + assert.throws( + createInternalUser, [], "Created user which would be recognized as a cluster member"); // Check that we can add a user and read data test.createUser( @@ -101,14 +101,6 @@ const x509_options = { authAndTest(mongo); MongoRunner.stopMongod(mongo); } -{ - print("1.5. Testing x.509 auth to mongod with cluster/user separation disabled"); - const mongo = MongoRunner.runMongod(Object.merge( - x509_options, {auth: "", setParameter: {enforceUserClusterSeparation: false}})); - - authAndTest(mongo, {clusterUserSeparationOveride: true}); - MongoRunner.stopMongod(mongo); -} { print("2. Testing x.509 auth to mongos"); @@ -127,21 +119,3 @@ const x509_options = { authAndTest(new Mongo("localhost:" + st.s0.port)); st.stop(); } -{ - print("2.5 Testing x.509 auth to mongos with cluster/user separation disabled"); - var st = new ShardingTest({ - shards: 1, - mongos: 1, - other: { - keyFile: 'jstests/libs/key1', - configOptions: - Object.merge(x509_options, {setParameter: {enforceUserClusterSeparation: false}}), - mongosOptions: x509_options, - shardOptions: x509_options, - useHostname: false - } - }); - - authAndTest(new Mongo("localhost:" + st.s0.port), {clusterUserSeparationOveride: true}); - st.stop(); -} diff --git a/jstests/ssl/x509_enforce_user_cluster_separation.js b/jstests/ssl/x509_enforce_user_cluster_separation.js new file mode 100644 index 00000000000..46d3938bc99 --- /dev/null +++ b/jstests/ssl/x509_enforce_user_cluster_separation.js @@ -0,0 +1,205 @@ +// Check if this build supports the authenticationMechanisms startup parameter. + +const SERVER_CERT = "jstests/libs/server.pem"; +const SERVER_SAN_CERT = "jstests/libs/server_SAN.pem"; +const CLIENT_CERT = "jstests/libs/client.pem"; +const CA_CERT = "jstests/libs/ca.pem"; + +const SERVER_USER = "CN=server,OU=Kernel,O=MongoDB,L=New York City,ST=New York,C=US"; +const SERVER_SAN_USER = + "CN=Kernel Client Peer Role,OU=Kernel,O=MongoDB,L=New York City,ST=New York,C=US"; +const CLIENT_USER = "CN=client,OU=KernelUser,O=MongoDB,L=New York City,ST=New York,C=US"; + +function authAndTest(cert, user) { + const INVALID_USER = "C=US,ST=New York,L=New York City,O=MongoDB,OU=KernelUser,CN=invalid"; + + let external = db.getSiblingDB("$external"); + let test = db.getSiblingDB("test"); + + assert(!external.auth({user: INVALID_USER, mechanism: 'MONGODB-X509'}), + "authentication with invalid user should fail"); + assert(external.auth({user: user, mechanism: 'MONGODB-X509'}), + "authentication with valid user failed"); + assert(external.auth({mechanism: 'MONGODB-X509'}), + "authentication with valid cert and no user field failed"); + assert(external.runCommand({authenticate: 1, mechanism: 'MONGODB-X509', user: user}).ok, + "runCommand authentication with valid cert and user field failed"); + assert(external.runCommand({authenticate: 1, mechanism: 'MONGODB-X509'}).ok, + "runCommand authentication with valid cert and no user field failed"); + // Smoke our current user with a find. + test.foo.findOne(); + + // Check that we can add a user and read data. + test.createUser( + {user: "test", pwd: "test", roles: [{'role': 'readWriteAnyDatabase', 'db': 'admin'}]}); + test.foo.findOne(); + + // Reads are not allowed after logout. + external.logout(); + assert.throws(function() { + test.foo.findOne(); + }, [], "read after logout"); +} + +function runSubShell(conn, cert, user, func) { + const args = [ + 'mongo', + '--tls', + `--tlsCAFile=${CA_CERT}`, + `--tlsCertificateKeyFile=${cert}`, + '--tlsAllowInvalidHostnames', + '--authenticationDatabase=$external', + '--authenticationMechanism=MONGODB-X509', + `mongodb://${conn.host}`, + '--eval', + `(${func.toString()})('${cert}', '${user}');` + ]; + const ret = _runMongoProgram(...args); + assert(ret == ErrorCodes.OK, 'subshell did not succeed'); +} + +function initUser(conn, user) { + const external = conn.getDB("$external"); + external.createUser({ + user: user, + roles: [ + {'role': 'userAdminAnyDatabase', 'db': 'admin'}, + {'role': 'readWriteAnyDatabase', 'db': 'admin'}, + {'role': 'clusterMonitor', 'db': 'admin'}, + ] + }); + + // Localhost exception should not be in place anymore + const test = conn.getDB("test"); + assert.throws(function() { + test.foo.findOne(); + }, [], "read without login"); +} + +const x509_options = { + sslMode: "requireSSL", + sslPEMKeyFile: SERVER_CERT, + sslCAFile: CA_CERT +}; + +const mongodOptions = + Object.merge(x509_options, {auth: "", setParameter: {enforceUserClusterSeparation: false}}); + +const mongosOptions = + Object.merge(x509_options, {setParameter: {enforceUserClusterSeparation: false}}); + +function runMongodTest(desc, func) { + print(desc); + const mongo = MongoRunner.runMongod(mongodOptions); + func(mongo); + + MongoRunner.stopMongod(mongo); +} + +function runMongodFailTest(desc, options) { + print(desc); + const mongo = MongoRunner.runMongod(Object.merge(mongodOptions, options)); + assert(!mongo, "MongoD started successfully with bad options"); +} + +function runMongosTest(desc, func) { + print(desc); + const st = new ShardingTest({ + shards: 1, + mongos: 1, + other: { + keyFile: 'jstests/libs/key1', + configOptions: mongodOptions, + mongosOptions: mongosOptions, + shardOptions: x509_options, + useHostname: false + } + }); + + const mongo = new Mongo(`localhost:${st.s0.port}`); + func(mongo); + st.stop(); +} + +function runMongosFailTest(desc, options) { + print(desc); + // We start the ShardingTest cleanly first because it throws and fails to clean up after itself. + const st = new ShardingTest({ + config: 1, + shards: 1, + mongos: 1, + other: { + keyFile: 'jstests/libs/key1', + configOptions: mongodOptions, + mongosOptions: mongosOptions, + shardOptions: x509_options, + useHostname: false + } + }); + + const failOptions = Object.merge(mongosOptions, options); + print(`Fail options: ${tojson(failOptions)}`); + + assert.throws(function() { + // Start a new mongos with bad options. + st.restartMongos(0, failOptions); + }, [], "MongoS restarted successfully with bad options"); + + // Avoid st.stop() because it will throw when it attempts to stop the second mongos. + st.stopAllShards(); + st.stopAllConfigServers(); +} + +runMongodTest("1a. Testing x.509 auth to mongod with a client user/cert", function(conn) { + initUser(conn, CLIENT_USER); + + runSubShell(conn, CLIENT_CERT, CLIENT_USER, authAndTest); +}); + +runMongodTest("1b. Testing x.509 auth to mongod with the server user/cert", function(conn) { + initUser(conn, SERVER_USER); + + runSubShell(conn, SERVER_CERT, SERVER_USER, authAndTest); +}); + +runMongodTest("1c. Testing x.509 auth to mongod with a cluster user/cert", function(conn) { + initUser(conn, SERVER_SAN_USER); + + runSubShell(conn, SERVER_SAN_CERT, SERVER_SAN_USER, authAndTest); +}); + +runMongodFailTest('1d. Testing x.509 cluster auth on mongod with "x509" option', + {clusterAuthMode: "x509"}); + +runMongodFailTest('1e. Testing x.509 cluster auth on mongod with "sendX509" option', + {clusterAuthMode: "sendX509"}); + +runMongodFailTest('1e. Testing x.509 cluster auth on mongod with "sendKeyFile" option', + {clusterAuthMode: "sendKeyFile"}); + +runMongosTest("2a. Testing x.509 auth to mongos with a client user/cert", function(conn) { + initUser(conn, CLIENT_USER); + + runSubShell(conn, CLIENT_CERT, CLIENT_USER, authAndTest); +}); + +runMongosTest("2b. Testing x.509 auth to mongos with the server user/cert", function(conn) { + initUser(conn, SERVER_USER); + + runSubShell(conn, SERVER_CERT, SERVER_USER, authAndTest); +}); + +runMongosTest("2c. Testing x.509 auth to mongos with a cluster user/cert", function(conn) { + initUser(conn, SERVER_SAN_USER); + + runSubShell(conn, SERVER_SAN_CERT, SERVER_SAN_USER, authAndTest); +}); + +runMongosFailTest('2d. Testing x.509 cluster auth on mongos with "x509" option', + {restart: true, clusterAuthMode: "x509"}); + +runMongosFailTest('2e. Testing x.509 cluster auth on mongos with "sendX509" option', + {clusterAuthMode: "sendX509"}); + +runMongosFailTest('2f. Testing x.509 cluster auth on mongos with "sendKeyFile" option', + {clusterAuthMode: "sendKeyFile"}); diff --git a/src/mongo/db/auth/SConscript b/src/mongo/db/auth/SConscript index d8c5ac1ee2e..6bd7207d9d9 100644 --- a/src/mongo/db/auth/SConscript +++ b/src/mongo/db/auth/SConscript @@ -139,13 +139,23 @@ env.Library( ) env.Library( + target='auth_options', + source=[ + env.Idlc("auth_options.idl")[0], + ], + LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/idl/server_parameter', + ], +) + +env.Library( target='role_graph_update', source=[ 'role_graph_update.cpp', - env.Idlc("auth_options.idl")[0], ], LIBDEPS=[ 'auth', + 'auth_options', 'role_graph', '$BUILD_DIR/mongo/db/update/update_driver', ], diff --git a/src/mongo/db/auth/auth_options.idl b/src/mongo/db/auth/auth_options.idl index 488e311ee75..a0d5139938a 100644 --- a/src/mongo/db/auth/auth_options.idl +++ b/src/mongo/db/auth/auth_options.idl @@ -42,3 +42,9 @@ server_parameters: cpp_vartype: bool cpp_varname: roleGraphInvalidationIsFatal test_only: true + enforceUserClusterSeparation: + description: "Prevents creation of users whose names would be interpreted as cluster members" + set_at: startup + cpp_varname: "gEnforceUserClusterSeparation" + cpp_vartype: bool + default: true diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript index e7bf9e16d93..4cd84cf27bb 100644 --- a/src/mongo/db/commands/SConscript +++ b/src/mongo/db/commands/SConscript @@ -184,6 +184,7 @@ env.Library( ], LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/db/audit', + '$BUILD_DIR/mongo/db/auth/auth_options', '$BUILD_DIR/mongo/db/auth/sasl_options', '$BUILD_DIR/mongo/db/auth/user_document_parser', '$BUILD_DIR/mongo/db/commands', @@ -366,6 +367,7 @@ env.Library( '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/client/clientdriver_minimal', '$BUILD_DIR/mongo/db/auth/auth', + '$BUILD_DIR/mongo/db/auth/auth_options', '$BUILD_DIR/mongo/db/auth/authprivilege', '$BUILD_DIR/mongo/db/auth/role_graph', '$BUILD_DIR/mongo/db/auth/sasl_options', diff --git a/src/mongo/db/commands/authentication_commands.cpp b/src/mongo/db/commands/authentication_commands.cpp index 02a3505e92b..3a64955228b 100644 --- a/src/mongo/db/commands/authentication_commands.cpp +++ b/src/mongo/db/commands/authentication_commands.cpp @@ -42,6 +42,7 @@ #include "mongo/client/sasl_client_authenticate.h" #include "mongo/config.h" #include "mongo/db/audit.h" +#include "mongo/db/auth/auth_options_gen.h" #include "mongo/db/auth/authorization_session.h" #include "mongo/db/auth/privilege.h" #include "mongo/db/auth/sasl_options.h" @@ -68,71 +69,81 @@ namespace { static bool _isX509AuthDisabled; static constexpr auto kX509AuthenticationDisabledMessage = "x.509 authentication is disabled."_sd; +constexpr auto kExternalDB = "$external"_sd; + #ifdef MONGO_CONFIG_SSL Status _authenticateX509(OperationContext* opCtx, const UserName& user, const BSONObj& cmdObj) { if (!getSSLManager()) { return Status(ErrorCodes::ProtocolError, "SSL support is required for the MONGODB-X509 mechanism."); } - if (user.getDB() != "$external") { - return Status(ErrorCodes::ProtocolError, - "X.509 authentication must always use the $external database."); - } - - Client* client = Client::getCurrent(); - AuthorizationSession* authorizationSession = AuthorizationSession::get(client); - auto clientName = SSLPeerInfo::forSession(client->session()).subjectName; - uassert(ErrorCodes::AuthenticationFailed, - "No verified subject name available from client", - !clientName.empty()); if (!getSSLManager()->getSSLConfiguration().hasCA) { return Status(ErrorCodes::AuthenticationFailed, "Unable to verify x.509 certificate, as no CA has been provided."); - } else if (user.getUser() != clientName.toString()) { + } + + Client* client = opCtx->getClient(); + auto clientName = SSLPeerInfo::forSession(client->session()).subjectName; + if (clientName.empty()) { + return Status(ErrorCodes::AuthenticationFailed, + "No verified subject name available from client"); + } + + if (user.getDB() != kExternalDB) { + return Status(ErrorCodes::ProtocolError, + "X.509 authentication must always use the $external database."); + } + + if (user.getUser() != clientName.toString()) { return Status(ErrorCodes::AuthenticationFailed, "There is no x.509 client certificate matching the user."); - } else { + } + + AuthorizationSession* authorizationSession = AuthorizationSession::get(client); + + auto isInternalClient = [&]() -> bool { + return opCtx->getClient()->session()->getTags() & transport::Session::kInternalClient; + }; + + auto authorizeExternalUser = [&]() -> Status { + if (_isX509AuthDisabled) { + return Status(ErrorCodes::BadValue, kX509AuthenticationDisabledMessage); + } + return authorizationSession->addAndAuthorizeUser(opCtx, user); + }; + + if (getSSLManager()->getSSLConfiguration().isClusterMember(clientName)) { // Handle internal cluster member auth, only applies to server-server connections - if (getSSLManager()->getSSLConfiguration().isClusterMember(clientName)) { - int clusterAuthMode = serverGlobalParams.clusterAuthMode.load(); - if (clusterAuthMode == ServerGlobalParams::ClusterAuthMode_undefined || - clusterAuthMode == ServerGlobalParams::ClusterAuthMode_keyFile) { - return Status(ErrorCodes::AuthenticationFailed, - "The provided certificate " - "can only be used for cluster authentication, not client " - "authentication. The current configuration does not allow " - "x.509 cluster authentication, check the --clusterAuthMode flag"); - } - auto& clientMetadata = - ClientMetadataIsMasterState::get(opCtx->getClient()).getClientMetadata(); - if (clientMetadata) { - auto clientMetadataDoc = clientMetadata->getDocument(); - auto driverName = clientMetadataDoc.getObjectField("driver"_sd) - .getField("name"_sd) - .checkAndGetStringData(); - if (!clientMetadata->getApplicationName().empty() || - (driverName != "MongoDB Internal Client" && - driverName != "NetworkInterfaceTL")) { + switch (serverGlobalParams.clusterAuthMode.load()) { + case ServerGlobalParams::ClusterAuthMode_undefined: + case ServerGlobalParams::ClusterAuthMode_keyFile: { + uassert(ErrorCodes::AuthenticationFailed, + "The provided certificate can only be used for cluster authentication, not " + "client authentication. The current configuration does not allow x.509 " + "cluster authentication, check the --clusterAuthMode flag", + !gEnforceUserClusterSeparation); + + return authorizeExternalUser(); + } break; + case ServerGlobalParams::ClusterAuthMode_sendKeyFile: + case ServerGlobalParams::ClusterAuthMode_sendX509: + case ServerGlobalParams::ClusterAuthMode_x509: { + if (!isInternalClient()) { warning() << "Client isn't a mongod or mongos, but is connecting with a " "certificate with cluster membership"; } - } - authorizationSession->grantInternalAuthorization(client); + authorizationSession->grantInternalAuthorization(client); + return Status::OK(); + } break; } + } else { // Handle normal client authentication, only applies to client-server connections - else { - if (_isX509AuthDisabled) { - return Status(ErrorCodes::BadValue, kX509AuthenticationDisabledMessage); - } - Status status = authorizationSession->addAndAuthorizeUser(opCtx, user); - if (!status.isOK()) { - return status; - } - } - return Status::OK(); + return authorizeExternalUser(); } + + MONGO_UNREACHABLE; } #endif // MONGO_CONFIG_SSL diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp index fd1809ae1da..1fc2293f2a8 100644 --- a/src/mongo/db/commands/user_management_commands.cpp +++ b/src/mongo/db/commands/user_management_commands.cpp @@ -47,6 +47,7 @@ #include "mongo/db/auth/action_set.h" #include "mongo/db/auth/action_type.h" #include "mongo/db/auth/address_restriction.h" +#include "mongo/db/auth/auth_options_gen.h" #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/authorization_session.h" #include "mongo/db/auth/privilege.h" diff --git a/src/mongo/db/commands/user_management_commands.idl b/src/mongo/db/commands/user_management_commands.idl index 7df3ab4acda..1ae9c9658c5 100644 --- a/src/mongo/db/commands/user_management_commands.idl +++ b/src/mongo/db/commands/user_management_commands.idl @@ -32,12 +32,3 @@ imports: - "mongo/idl/basic_types.idl" - "mongo/db/auth/auth_types.idl" - "mongo/db/auth/address_restriction.idl" - -server_parameters: - enforceUserClusterSeparation: - description: "Prevents creation of users whose names would be interpreted as cluster members" - set_at: startup - cpp_varname: "gEnforceUserClusterSeparation" - cpp_vartype: bool - default: true - diff --git a/src/mongo/shell/shardingtest.js b/src/mongo/shell/shardingtest.js index ff4f71bae09..1386c4c3d1d 100644 --- a/src/mongo/shell/shardingtest.js +++ b/src/mongo/shell/shardingtest.js @@ -380,17 +380,18 @@ var ShardingTest = function(params) { } }; - this.stopAllMongos = function(opts) { - for (var i = 0; i < this._mongos.length; i++) { - this.stopMongos(i, opts); + this.stopAllConfigServers = function(opts) { + if (this.configRS) { + this.configRS.stopSet(undefined, undefined, opts); + } else { + // Old style config triplet + for (var i = 0; i < this._configServers.length; i++) { + this.stopConfigServer(i, opts); + } } }; - this.stop = function(opts) { - this.checkUUIDsConsistentAcrossCluster(); - - this.stopAllMongos(opts); - + this.stopAllShards = function(opts) { for (var i = 0; i < this._connections.length; i++) { if (this._rs[i]) { this._rs[i].test.stopSet(15, undefined, opts); @@ -398,16 +399,25 @@ var ShardingTest = function(params) { this.stopMongod(i, opts); } } + }; - if (this.configRS) { - this.configRS.stopSet(undefined, undefined, opts); - } else { - // Old style config triplet - for (var i = 0; i < this._configServers.length; i++) { - this.stopConfigServer(i, opts); - } + this.stopAllMongos = function(opts) { + for (var i = 0; i < this._mongos.length; i++) { + this.stopMongos(i, opts); } + }; + + this.stop = function(opts) { + this.checkUUIDsConsistentAcrossCluster(); + + this.stopAllMongos(opts); + + let startTime = new Date(); // Measure the execution time of shutting down shards. + this.stopAllShards(opts); + print("ShardingTest stopped all shards, took " + (new Date() - startTime) + "ms for " + + this._connections.length + " shards."); + this.stopAllConfigServers(opts); if (!opts || !opts.noCleanData) { print("ShardingTest stop deleting all dbpaths"); for (var i = 0; i < _alldbpaths.length; i++) { diff --git a/src/mongo/util/net/SConscript b/src/mongo/util/net/SConscript index 24412313a97..1ed2c22ca26 100644 --- a/src/mongo/util/net/SConscript +++ b/src/mongo/util/net/SConscript @@ -68,6 +68,7 @@ env.Library( 'ssl_options', ], LIBDEPS_PRIVATE=[ + '$BUILD_DIR/mongo/db/auth/auth_options', '$BUILD_DIR/mongo/db/server_options_core', '$BUILD_DIR/mongo/util/options_parser/options_parser', ] diff --git a/src/mongo/util/net/ssl_options_server.cpp b/src/mongo/util/net/ssl_options_server.cpp index dba0e0f94f7..74a341a6e90 100644 --- a/src/mongo/util/net/ssl_options_server.cpp +++ b/src/mongo/util/net/ssl_options_server.cpp @@ -37,6 +37,7 @@ #include "mongo/base/status.h" #include "mongo/config.h" +#include "mongo/db/auth/auth_options_gen.h" #include "mongo/db/server_options.h" #include "mongo/util/log.h" #include "mongo/util/options_parser/startup_option_init.h" @@ -240,6 +241,12 @@ MONGO_STARTUP_OPTIONS_POST(SSLServerOptions)(InitializerContext*) { if (sslGlobalParams.sslMode.load() == SSLParams::SSLMode_disabled) { return {ErrorCodes::BadValue, "need to enable TLS via the tlsMode flag"}; } + + if (!gEnforceUserClusterSeparation) { + uasserted(ErrorCodes::BadValue, + "cannot have have x.509 cluster authentication while not enforcing user " + "cluster separation"); + } } if (sslGlobalParams.sslMode.load() == SSLParams::SSLMode_allowSSL) { |