// Check if this build supports the authenticationMechanisms startup parameter. load("jstests/libs/logv2_helpers.js"); const SERVER_CERT = "jstests/libs/server.pem"; const CA_CERT = "jstests/libs/ca.pem"; 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) { external = mongo.getDB("$external"); test = mongo.getDB("test"); // Add user using localhost exception external.createUser({ user: CLIENT_USER, roles: [ {'role': 'userAdminAnyDatabase', 'db': 'admin'}, {'role': 'readWriteAnyDatabase', 'db': 'admin'}, {'role': 'clusterMonitor', 'db': 'admin'}, ] }); // Localhost exception should not be in place anymore assert.throws(function() { test.foo.findOne(); }, [], "read without login"); assert(!external.auth({user: INVALID_CLIENT_USER, mechanism: 'MONGODB-X509'}), "authentication with invalid user should fail"); assert(external.auth({user: CLIENT_USER, mechanism: 'MONGODB-X509'}), "authentication with valid user failed"); assert(external.auth({mechanism: 'MONGODB-X509'}), "authentication with valid client cert and no user field failed"); const withUserReply = assert.commandWorked( external.runCommand({authenticate: 1, mechanism: 'MONGODB-X509', user: CLIENT_USER}), "runCommand authentication with valid client cert and user field failed"); assert.eq(withUserReply.user, CLIENT_USER); assert.eq(withUserReply.dbname, '$external'); const noUserReply = assert.commandWorked( external.runCommand({authenticate: 1, mechanism: 'MONGODB-X509'}), "runCommand authentication with valid client cert and no user field failed"); assert.eq(noUserReply.user, CLIENT_USER); assert.eq(noUserReply.dbname, '$external'); // Check that there's a "Successfully authenticated" message that includes the client IP const log = assert.commandWorked(external.getSiblingDB("admin").runCommand({getLog: "global"})).log; if (isJsonLog(mongo)) { function checkAuthSuccess(element, index, array) { const logJson = JSON.parse(element); return logJson.id === 20429 && logJson.attr.user === CLIENT_USER && logJson.attr.db === "$external" && /(?:\d{1,3}\.){3}\d{1,3}:\d+/.test(logJson.attr.client); } assert(log.some(checkAuthSuccess)); } else { const successRegex = new RegExp(`Successfully authenticated as principal ${CLIENT_USER} on ` + `\\$external from client (?:\\d{1,3}\\.){3}\\d{1,3}:\\d+`); assert(log.some((line) => successRegex.test(line))); } 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'}]}); }; assert.throws( createServerUser, [], "Created user with same name as the server's x.509 subject"); 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'}]}); }; 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( {user: "test", pwd: "test", roles: [{'role': 'readWriteAnyDatabase', 'db': 'admin'}]}); test.foo.findOne(); external.logout(); assert.throws(function() { test.foo.findOne(); }, [], "read after logout"); } const x509_options = { sslMode: "requireSSL", sslPEMKeyFile: SERVER_CERT, sslCAFile: CA_CERT }; { print("1. Testing x.509 auth to mongod"); const mongo = MongoRunner.runMongod(Object.merge(x509_options, {auth: ""})); authAndTest(mongo); MongoRunner.stopMongod(mongo); } { print("2. Testing x.509 auth to mongos"); var st = new ShardingTest({ shards: 1, mongos: 1, other: { keyFile: 'jstests/libs/key1', configOptions: x509_options, mongosOptions: x509_options, shardOptions: x509_options, useHostname: false } }); authAndTest(new Mongo("localhost:" + st.s0.port)); st.stop(); }