diff options
author | Mathias Stearn <mathias@10gen.com> | 2018-11-30 13:57:15 -0500 |
---|---|---|
committer | Mathias Stearn <mathias@10gen.com> | 2019-03-07 17:27:08 -0500 |
commit | 05299e4077bebdaaad0622f2fe3c611ba181b134 (patch) | |
tree | 3c5192bd4b149cd7f1f6274d9ea5408f56394467 | |
parent | 470c14ed7543d62a2a76476e6522823316ec36d3 (diff) | |
download | mongo-05299e4077bebdaaad0622f2fe3c611ba181b134.tar.gz |
SERVER-38319 Propagate URI options from DBClientRS through all codepaths
(cherry picked from commit 3691388900021034e338786116d60fd4d480a6e7)
-rw-r--r-- | jstests/ssl/mongo_uri_secondaries.js | 83 | ||||
-rw-r--r-- | src/mongo/client/dbclient_connection.cpp | 21 | ||||
-rw-r--r-- | src/mongo/client/dbclient_rs.cpp | 8 | ||||
-rw-r--r-- | src/mongo/client/mongo_uri.cpp | 94 | ||||
-rw-r--r-- | src/mongo/client/mongo_uri.h | 25 | ||||
-rw-r--r-- | src/mongo/client/mongo_uri_test.cpp | 182 | ||||
-rw-r--r-- | src/mongo/client/replica_set_monitor.cpp | 13 | ||||
-rw-r--r-- | src/mongo/client/replica_set_monitor.h | 7 | ||||
-rw-r--r-- | src/mongo/client/replica_set_monitor_internal.h | 4 | ||||
-rw-r--r-- | src/mongo/client/replica_set_monitor_manager.cpp | 8 |
10 files changed, 296 insertions, 149 deletions
diff --git a/jstests/ssl/mongo_uri_secondaries.js b/jstests/ssl/mongo_uri_secondaries.js new file mode 100644 index 00000000000..9512a3c23c3 --- /dev/null +++ b/jstests/ssl/mongo_uri_secondaries.js @@ -0,0 +1,83 @@ +// On OSX this test assumes that jstests/libs/trusted-ca.pem has been added as a trusted +// certificate to the login keychain of the evergreen user. See, +// https://github.com/10gen/buildslave-cookbooks/commit/af7cabe5b6e0885902ebd4902f7f974b64cc8961 +// for details. +// To install trusted-ca.pem for local testing on OSX, invoke the following at a console: +// security add-trusted-cert -d jstests/libs/trusted-ca.pem +(function() { + 'use strict'; + + const HOST_TYPE = getBuildInfo().buildEnvironment.target_os; + if (HOST_TYPE == "windows") { + // OpenSSL backed imports Root CA and intermediate CA + runProgram( + "certutil.exe", "-addstore", "-user", "-f", "CA", "jstests\\libs\\trusted-ca.pem"); + + // SChannel backed follows Windows rules and only trusts the Root store in Local Machine and + // Current User. + runProgram("certutil.exe", "-addstore", "-f", "Root", "jstests\\libs\\trusted-ca.pem"); + } + + const x509Options = { + sslMode: 'requireSSL', + sslPEMKeyFile: 'jstests/libs/trusted-server.pem', + sslCAFile: 'jstests/libs/trusted-ca.pem', + sslAllowInvalidCertificates: '', + sslWeakCertificateValidation: '', + }; + + const rst = new ReplSetTest({ + nodes: 2, + name: "sslSet", + useHostName: false, + nodeOptions: x509Options, + waitForKeys: false + }); + rst.startSet(); + rst.initiate(); + + const subShellCommand = function(hosts) { + var Ms = []; + for (var i = 0; i < 10; i++) { + Ms.push(new Mongo("mongodb://" + hosts[0] + "," + hosts[1] + + "/?ssl=true&replicaSet=sslSet")); + } + + for (var i = 0; i < 10; i++) { + var db = Ms[i].getDB("test"); + db.setSlaveOk(true); + db.col.find().readPref("secondary").toArray(); + } + }; + + const subShellCommandFormatter = function(replSet) { + var hosts = []; + replSet.nodes.forEach((node) => { + hosts.push("localhost:" + node.port); + }); + + let command = ` + (function () { + 'use strict'; + let command = ${subShellCommand.toString()}; + let hosts = ${tojson(hosts)}; + command(hosts); + }());`; + + return command; + }; + + const subShellArgs = [ + "env", + "SSL_CERT_FILE=jstests/libs/trusted-ca.pem", + './mongo', + '--nodb', + '--eval', + subShellCommandFormatter(rst) + ]; + + const retVal = _runMongoProgram(...subShellArgs); + assert.eq(retVal, 0, 'mongo shell did not succeed with exit code 0'); + + rst.stopSet(); +}()); diff --git a/src/mongo/client/dbclient_connection.cpp b/src/mongo/client/dbclient_connection.cpp index 6bc038447d0..67137931f39 100644 --- a/src/mongo/client/dbclient_connection.cpp +++ b/src/mongo/client/dbclient_connection.cpp @@ -303,25 +303,8 @@ Status DBClientConnection::connectSocketOnly(const HostAndPort& serverAddress) { << ", address resolved to 0.0.0.0"); } - transport::ConnectSSLMode sslMode = transport::kGlobalSSLMode; -#ifdef MONGO_CONFIG_SSL - // Prefer to get SSL mode directly from our URI, but if it is not set, fall back to - // checking global SSL params. DBClientConnections create through the shell will have a - // meaningful URI set, but DBClientConnections created from within the server may not. - auto options = _uri.getOptions(); - auto iter = options.find("ssl"); - if (iter != options.end()) { - if (iter->second == "true") { - sslMode = transport::kEnableSSL; - } else { - sslMode = transport::kDisableSSL; - } - } - -#endif - - auto tl = getGlobalServiceContext()->getTransportLayer(); - auto sws = tl->connect(serverAddress, sslMode, _socketTimeout.value_or(Milliseconds{5000})); + auto sws = getGlobalServiceContext()->getTransportLayer()->connect( + serverAddress, _uri.getSSLMode(), _socketTimeout.value_or(Milliseconds{5000})); if (!sws.isOK()) { return Status(ErrorCodes::HostUnreachable, str::stream() << "couldn't connect to server " << _serverAddress.toString() diff --git a/src/mongo/client/dbclient_rs.cpp b/src/mongo/client/dbclient_rs.cpp index 1360c705832..69a4ac6c1f5 100644 --- a/src/mongo/client/dbclient_rs.cpp +++ b/src/mongo/client/dbclient_rs.cpp @@ -297,11 +297,7 @@ DBClientConnection* DBClientReplicaSet::checkMaster() { _masterHost = h; - MongoURI masterUri; - if (!_uri.isValid()) - masterUri = MongoURI(ConnectionString(_masterHost)); - else - masterUri = _uri.cloneURIForServer(_masterHost); + MongoURI masterUri = _uri.cloneURIForServer(_masterHost); string errmsg; DBClientConnection* newConn = NULL; @@ -710,7 +706,7 @@ DBClientConnection* DBClientReplicaSet::selectNodeUsingTags( // callback. We should eventually not need this after we remove the // callback. DBClientConnection* newConn = dynamic_cast<DBClientConnection*>( - globalConnPool.get(_lastSlaveOkHost.toString(), _so_timeout)); + globalConnPool.get(_uri.cloneURIForServer(_lastSlaveOkHost), _so_timeout)); // Assert here instead of returning NULL since the contract of this method is such // that returning NULL means none of the nodes were good, which is not the case here. diff --git a/src/mongo/client/mongo_uri.cpp b/src/mongo/client/mongo_uri.cpp index ca61bc328ca..934d5f7540e 100644 --- a/src/mongo/client/mongo_uri.cpp +++ b/src/mongo/client/mongo_uri.cpp @@ -96,7 +96,7 @@ mongo::StatusWith<std::string> mongo::uriDecode(StringData toDecode) { if (swHex.isOK()) { out << swHex.getValue(); } else { - return Status(ErrorCodes::FailedToParse, + return Status(ErrorCodes::Error(51040), "The characters after the % do not form a hex value. Please escape " "the % or pass a valid hex value. "); } @@ -183,14 +183,11 @@ std::map<std::string, std::string> parseOptions(StringData options, StringData u << "Missing a key for key/value pair in the options for mongodb:// URL: " << url); } - const auto key = uriDecode(keyRaw); - if (!key.isOK()) { - uasserted( - ErrorCodes::FailedToParse, - str::stream() << "Key '" << keyRaw - << "' in options cannot properly be URL decoded for mongodb:// URL: " - << url); - } + const auto key = uassertStatusOKWithContext( + uriDecode(keyRaw), + str::stream() << "Key '" << keyRaw + << "' in options cannot properly be URL decoded for mongodb:// URL: " + << url); const auto valRaw = kvPair.second; if (valRaw.empty()) { uasserted(ErrorCodes::FailedToParse, @@ -198,16 +195,13 @@ std::map<std::string, std::string> parseOptions(StringData options, StringData u << "' in the options for mongodb:// URL: " << url); } - const auto val = uriDecode(valRaw); - if (!val.isOK()) { - uasserted( - ErrorCodes::FailedToParse, - str::stream() << "Value '" << valRaw << "' for key '" << keyRaw - << "' in options cannot properly be URL decoded for mongodb:// URL: " - << url); - } + const auto val = uassertStatusOKWithContext( + uriDecode(valRaw), + str::stream() << "Value '" << valRaw << "' for key '" << keyRaw + << "' in options cannot properly be URL decoded for mongodb:// URL: " + << url); - ret[key.getValue()] = val.getValue(); + ret[key] = val; } return ret; @@ -360,21 +354,14 @@ MongoURI MongoURI::parseImpl(const std::string& url) { } // Get the username and make sure it did not fail to decode - const auto usernameWithStatus = uriDecode(usernameSD); - if (!usernameWithStatus.isOK()) { - uasserted(ErrorCodes::FailedToParse, - str::stream() << "Username cannot properly be URL decoded for mongodb:// URL: " - << url); - } - const auto username = usernameWithStatus.getValue(); + const auto username = uassertStatusOKWithContext( + uriDecode(usernameSD), + str::stream() << "Username cannot properly be URL decoded for mongodb:// URL: " << url); // Get the password and make sure it did not fail to decode - const auto passwordWithStatus = uriDecode(passwordSD); - if (!passwordWithStatus.isOK()) - uasserted(ErrorCodes::FailedToParse, - str::stream() << "Password cannot properly be URL decoded for mongodb:// URL: " - << url); - const auto password = passwordWithStatus.getValue(); + const auto password = uassertStatusOKWithContext( + uriDecode(passwordSD), + str::stream() << "Password cannot properly be URL decoded for mongodb:// URL: " << url); // 4. Validate, split, and URL decode the host identifiers. const auto hostIdentifiersStr = hostIdentifiers.toString(); @@ -383,14 +370,10 @@ MongoURI MongoURI::parseImpl(const std::string& url) { boost::first_finder(",", boost::is_iequal())); i != std::remove_reference<decltype((i))>::type{}; ++i) { - const auto hostWithStatus = uriDecode(boost::copy_range<std::string>(*i)); - if (!hostWithStatus.isOK()) { - uasserted(ErrorCodes::FailedToParse, - str::stream() << "Host cannot properly be URL decoded for mongodb:// URL: " - << url); - } + const auto host = uassertStatusOKWithContext( + uriDecode(boost::copy_range<std::string>(*i)), + str::stream() << "Host cannot properly be URL decoded for mongodb:// URL: " << url); - const auto host = hostWithStatus.getValue(); if (host.empty()) { continue; } @@ -453,14 +436,11 @@ MongoURI MongoURI::parseImpl(const std::string& url) { } // 5. Decode the database name - const auto databaseWithStatus = uriDecode(databaseSD); - if (!databaseWithStatus.isOK()) { - uasserted(ErrorCodes::FailedToParse, - str::stream() << "Database name cannot properly be URL " - "decoded for mongodb:// URL: " - << url); - } - const auto database = databaseWithStatus.getValue(); + const auto database = + uassertStatusOKWithContext(uriDecode(databaseSD), + str::stream() << "Database name cannot properly be URL " + "decoded for mongodb:// URL: " + << url); // 6. Validate the database contains no prohibited characters // Prohibited characters: @@ -507,10 +487,28 @@ MongoURI MongoURI::parseImpl(const std::string& url) { } } + transport::ConnectSSLMode sslMode = transport::kGlobalSSLMode; + auto sslModeIter = options.find("ssl"); + if (sslModeIter != options.end()) { + const auto& val = sslModeIter->second; + if (val == "true") { + sslMode = transport::kEnableSSL; + } else if (val == "false") { + sslMode = transport::kDisableSSL; + } else { + uasserted(51041, str::stream() << "ssl must be either 'true' or 'false', not" << val); + } + } + ConnectionString cs( setName.empty() ? ConnectionString::MASTER : ConnectionString::SET, servers, setName); - return MongoURI( - std::move(cs), username, password, database, std::move(retryWrites), std::move(options)); + return MongoURI(std::move(cs), + username, + password, + database, + std::move(retryWrites), + sslMode, + std::move(options)); } StatusWith<MongoURI> MongoURI::parse(const std::string& url) try { diff --git a/src/mongo/client/mongo_uri.h b/src/mongo/client/mongo_uri.h index d05b8b1ab3a..e600522b463 100644 --- a/src/mongo/client/mongo_uri.h +++ b/src/mongo/client/mongo_uri.h @@ -41,6 +41,7 @@ #include "mongo/bson/util/builder.h" #include "mongo/client/connection_string.h" #include "mongo/stdx/mutex.h" +#include "mongo/transport/transport_layer.h" #include "mongo/util/assert_util.h" #include "mongo/util/net/hostandport.h" @@ -146,14 +147,10 @@ public: return _options; } - void addOption(std::string newKey, std::string newValue) { - _options[std::move(newKey)] = std::move(newValue); - } - void setOptionIfNecessary(std::string uriParamKey, std::string value) { const auto key = _options.find(uriParamKey); if (key == end(_options) && !value.empty()) { - addOption(uriParamKey, value); + _options[std::move(uriParamKey)] = std::move(value); } } @@ -210,24 +207,25 @@ public: return _retryWrites; } + transport::ConnectSSLMode getSSLMode() const { + return _sslMode; + } + // If you are trying to clone a URI (including its options/auth information) for a single // server (say a member of a replica-set), you can pass in its HostAndPort information to // get a new URI with the same info, except type() will be MASTER and getServers() will // be the single host you pass in. MongoURI cloneURIForServer(HostAndPort hostAndPort) const { - return MongoURI(ConnectionString(std::move(hostAndPort)), - _user, - _password, - _database, - _retryWrites, - _options); + auto out = *this; + out._connectString = ConnectionString(std::move(hostAndPort)); + return out; } ConnectionString::ConnectionType type() const { return _connectString.type(); } - explicit MongoURI(const ConnectionString& connectString) : _connectString(connectString){}; + explicit MongoURI(const ConnectionString& connectString) : _connectString(connectString) {} MongoURI() = default; @@ -241,12 +239,14 @@ private: const std::string& password, const std::string& database, boost::optional<bool> retryWrites, + transport::ConnectSSLMode sslMode, OptionsMap options) : _connectString(std::move(connectString)), _user(user), _password(password), _database(database), _retryWrites(std::move(retryWrites)), + _sslMode(sslMode), _options(std::move(options)) {} boost::optional<BSONObj> _makeAuthObjFromOptions(int maxWireVersion) const; @@ -258,6 +258,7 @@ private: std::string _password; std::string _database; boost::optional<bool> _retryWrites; + transport::ConnectSSLMode _sslMode = transport::kGlobalSSLMode; OptionsMap _options; }; diff --git a/src/mongo/client/mongo_uri_test.cpp b/src/mongo/client/mongo_uri_test.cpp index e044c817190..c06ea3f57a7 100644 --- a/src/mongo/client/mongo_uri_test.cpp +++ b/src/mongo/client/mongo_uri_test.cpp @@ -46,6 +46,11 @@ namespace mongo { namespace { +using transport::ConnectSSLMode; +using transport::ConnectSSLMode::kEnableSSL; +using transport::ConnectSSLMode::kDisableSSL; +using transport::ConnectSSLMode::kGlobalSSLMode; + struct URITestCase { std::string URI; std::string uname; @@ -55,14 +60,15 @@ struct URITestCase { size_t numservers; size_t numOptions; std::string database; + ConnectSSLMode sslMode; }; struct InvalidURITestCase { std::string URI; - boost::optional<Status> status; - InvalidURITestCase(std::string aURI, boost::optional<Status> aStatus = boost::none) { + boost::optional<ErrorCodes::Error> code; + InvalidURITestCase(std::string aURI, boost::optional<ErrorCodes::Error> aCode = boost::none) { URI = std::move(aURI); - status = std::move(aStatus); + code = std::move(aCode); } }; @@ -71,23 +77,39 @@ const ConnectionString::ConnectionType kSet = ConnectionString::SET; const URITestCase validCases[] = { - {"mongodb://user:pwd@127.0.0.1", "user", "pwd", kMaster, "", 1, 0, ""}, + {"mongodb://user:pwd@127.0.0.1", "user", "pwd", kMaster, "", 1, 0, "", kGlobalSSLMode}, - {"mongodb://user@127.0.0.1", "user", "", kMaster, "", 1, 0, ""}, + {"mongodb://user@127.0.0.1", "user", "", kMaster, "", 1, 0, "", kGlobalSSLMode}, - {"mongodb://localhost/?foo=bar", "", "", kMaster, "", 1, 1, ""}, + {"mongodb://localhost/?foo=bar", "", "", kMaster, "", 1, 1, "", kGlobalSSLMode}, - {"mongodb://localhost,/?foo=bar", "", "", kMaster, "", 1, 1, ""}, + {"mongodb://localhost,/?foo=bar", "", "", kMaster, "", 1, 1, "", kGlobalSSLMode}, - {"mongodb://user:pwd@127.0.0.1:1234", "user", "pwd", kMaster, "", 1, 0, ""}, + {"mongodb://user:pwd@127.0.0.1:1234", "user", "pwd", kMaster, "", 1, 0, "", kGlobalSSLMode}, - {"mongodb://user@127.0.0.1:1234", "user", "", kMaster, "", 1, 0, ""}, + {"mongodb://user@127.0.0.1:1234", "user", "", kMaster, "", 1, 0, "", kGlobalSSLMode}, - {"mongodb://127.0.0.1:1234/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName"}, + {"mongodb://127.0.0.1:1234/dbName?foo=a&c=b", + "", + "", + kMaster, + "", + 1, + 2, + "dbName", + kGlobalSSLMode}, - {"mongodb://127.0.0.1/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName"}, + {"mongodb://127.0.0.1/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName", kGlobalSSLMode}, - {"mongodb://user:pwd@127.0.0.1,/dbName?foo=a&c=b", "user", "pwd", kMaster, "", 1, 2, "dbName"}, + {"mongodb://user:pwd@127.0.0.1,/dbName?foo=a&c=b", + "user", + "pwd", + kMaster, + "", + 1, + 2, + "dbName", + kGlobalSSLMode}, {"mongodb://user:pwd@127.0.0.1,127.0.0.2/dbname?a=b&replicaSet=replName", "user", @@ -96,7 +118,8 @@ const URITestCase validCases[] = { "replName", 2, 2, - "dbname"}, + "dbname", + kGlobalSSLMode}, {"mongodb://needs%20encoding%25%23!%3C%3E:pwd@127.0.0.1,127.0.0.2/" "dbname?a=b&replicaSet=replName", @@ -106,7 +129,8 @@ const URITestCase validCases[] = { "replName", 2, 2, - "dbname"}, + "dbname", + kGlobalSSLMode}, {"mongodb://needs%20encoding%25%23!%3C%3E:pwd@127.0.0.1,127.0.0.2/" "db@name?a=b&replicaSet=replName", @@ -116,7 +140,8 @@ const URITestCase validCases[] = { "replName", 2, 2, - "db@name"}, + "db@name", + kGlobalSSLMode}, {"mongodb://user:needs%20encoding%25%23!%3C%3E@127.0.0.1,127.0.0.2/" "dbname?a=b&replicaSet=replName", @@ -126,7 +151,8 @@ const URITestCase validCases[] = { "replName", 2, 2, - "dbname"}, + "dbname", + kGlobalSSLMode}, {"mongodb://user:pwd@127.0.0.1,127.0.0.2/dbname?a=b&replicaSet=needs%20encoding%25%23!%3C%3E", "user", @@ -135,7 +161,8 @@ const URITestCase validCases[] = { "needs encoding%#!<>", 2, 2, - "dbname"}, + "dbname", + kGlobalSSLMode}, {"mongodb://user:pwd@127.0.0.1,127.0.0.2/needsencoding%40hello?a=b&replicaSet=replName", "user", @@ -144,7 +171,8 @@ const URITestCase validCases[] = { "replName", 2, 2, - "needsencoding@hello"}, + "needsencoding@hello", + kGlobalSSLMode}, {"mongodb://user:pwd@127.0.0.1,127.0.0.2/?replicaSet=replName", "user", @@ -153,7 +181,8 @@ const URITestCase validCases[] = { "replName", 2, 1, - ""}, + "", + kGlobalSSLMode}, {"mongodb://user@127.0.0.1,127.0.0.2/?replicaSet=replName", "user", @@ -162,7 +191,8 @@ const URITestCase validCases[] = { "replName", 2, 1, - ""}, + "", + kGlobalSSLMode}, {"mongodb://127.0.0.1,127.0.0.2/dbName?foo=a&c=b&replicaSet=replName", "", @@ -171,7 +201,8 @@ const URITestCase validCases[] = { "replName", 2, 3, - "dbName"}, + "dbName", + kGlobalSSLMode}, {"mongodb://user:pwd@127.0.0.1:1234,127.0.0.2:1234/?replicaSet=replName", "user", @@ -180,7 +211,8 @@ const URITestCase validCases[] = { "replName", 2, 1, - ""}, + "", + kGlobalSSLMode}, {"mongodb://user@127.0.0.1:1234,127.0.0.2:1234/?replicaSet=replName", "user", @@ -189,7 +221,8 @@ const URITestCase validCases[] = { "replName", 2, 1, - ""}, + "", + kGlobalSSLMode}, {"mongodb://127.0.0.1:1234,127.0.0.1:1234/dbName?foo=a&c=b&replicaSet=replName", "", @@ -198,19 +231,20 @@ const URITestCase validCases[] = { "replName", 2, 3, - "dbName"}, + "dbName", + kGlobalSSLMode}, - {"mongodb://user:pwd@[::1]", "user", "pwd", kMaster, "", 1, 0, ""}, + {"mongodb://user:pwd@[::1]", "user", "pwd", kMaster, "", 1, 0, "", kGlobalSSLMode}, - {"mongodb://user@[::1]", "user", "", kMaster, "", 1, 0, ""}, + {"mongodb://user@[::1]", "user", "", kMaster, "", 1, 0, "", kGlobalSSLMode}, - {"mongodb://[::1]/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName"}, + {"mongodb://[::1]/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName", kGlobalSSLMode}, - {"mongodb://user:pwd@[::1]:1234", "user", "pwd", kMaster, "", 1, 0, ""}, + {"mongodb://user:pwd@[::1]:1234", "user", "pwd", kMaster, "", 1, 0, "", kGlobalSSLMode}, - {"mongodb://user@[::1]:1234", "user", "", kMaster, "", 1, 0, ""}, + {"mongodb://user@[::1]:1234", "user", "", kMaster, "", 1, 0, "", kGlobalSSLMode}, - {"mongodb://[::1]:1234/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName"}, + {"mongodb://[::1]:1234/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName", kGlobalSSLMode}, {"mongodb://user:pwd@[::1],127.0.0.2/?replicaSet=replName", "user", @@ -219,9 +253,18 @@ const URITestCase validCases[] = { "replName", 2, 1, - ""}, + "", + kGlobalSSLMode}, - {"mongodb://user@[::1],127.0.0.2/?replicaSet=replName", "user", "", kSet, "replName", 2, 1, ""}, + {"mongodb://user@[::1],127.0.0.2/?replicaSet=replName", + "user", + "", + kSet, + "replName", + 2, + 1, + "", + kGlobalSSLMode}, {"mongodb://[::1],127.0.0.2/dbName?foo=a&c=b&replicaSet=replName", "", @@ -230,7 +273,8 @@ const URITestCase validCases[] = { "replName", 2, 3, - "dbName"}, + "dbName", + kGlobalSSLMode}, {"mongodb://user:pwd@[::1]:1234,127.0.0.2:1234/?replicaSet=replName", "user", @@ -239,7 +283,8 @@ const URITestCase validCases[] = { "replName", 2, 1, - ""}, + "", + kGlobalSSLMode}, {"mongodb://user@[::1]:1234,127.0.0.2:1234/?replicaSet=replName", "user", @@ -248,7 +293,8 @@ const URITestCase validCases[] = { "replName", 2, 1, - ""}, + "", + kGlobalSSLMode}, {"mongodb://[::1]:1234,[::1]:1234/dbName?foo=a&c=b&replicaSet=replName", "", @@ -257,19 +303,20 @@ const URITestCase validCases[] = { "replName", 2, 3, - "dbName"}, + "dbName", + kGlobalSSLMode}, - {"mongodb://user:pwd@[::1]", "user", "pwd", kMaster, "", 1, 0, ""}, + {"mongodb://user:pwd@[::1]", "user", "pwd", kMaster, "", 1, 0, "", kGlobalSSLMode}, - {"mongodb://user@[::1]", "user", "", kMaster, "", 1, 0, ""}, + {"mongodb://user@[::1]", "user", "", kMaster, "", 1, 0, "", kGlobalSSLMode}, - {"mongodb://[::1]/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName"}, + {"mongodb://[::1]/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName", kGlobalSSLMode}, - {"mongodb://user:pwd@[::1]:1234", "user", "pwd", kMaster, "", 1, 0, ""}, + {"mongodb://user:pwd@[::1]:1234", "user", "pwd", kMaster, "", 1, 0, "", kGlobalSSLMode}, - {"mongodb://user@[::1]:1234", "user", "", kMaster, "", 1, 0, ""}, + {"mongodb://user@[::1]:1234", "user", "", kMaster, "", 1, 0, "", kGlobalSSLMode}, - {"mongodb://[::1]:1234/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName"}, + {"mongodb://[::1]:1234/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName", kGlobalSSLMode}, {"mongodb://user:pwd@[::1],127.0.0.2/?replicaSet=replName", "user", @@ -278,9 +325,18 @@ const URITestCase validCases[] = { "replName", 2, 1, - ""}, + "", + kGlobalSSLMode}, - {"mongodb://user@[::1],127.0.0.2/?replicaSet=replName", "user", "", kSet, "replName", 2, 1, ""}, + {"mongodb://user@[::1],127.0.0.2/?replicaSet=replName", + "user", + "", + kSet, + "replName", + 2, + 1, + "", + kGlobalSSLMode}, {"mongodb://[::1],127.0.0.2/dbName?foo=a&c=b&replicaSet=replName", "", @@ -289,7 +345,8 @@ const URITestCase validCases[] = { "replName", 2, 3, - "dbName"}, + "dbName", + kGlobalSSLMode}, {"mongodb://user:pwd@[::1]:1234,127.0.0.2:1234/?replicaSet=replName", "user", @@ -298,7 +355,8 @@ const URITestCase validCases[] = { "replName", 2, 1, - ""}, + "", + kGlobalSSLMode}, {"mongodb://user@[::1]:1234,127.0.0.2:1234/?replicaSet=replName", "user", @@ -307,7 +365,8 @@ const URITestCase validCases[] = { "replName", 2, 1, - ""}, + "", + kGlobalSSLMode}, {"mongodb://[::1]:1234,[::1]:1234/dbName?foo=a&c=b&replicaSet=replName", "", @@ -316,7 +375,8 @@ const URITestCase validCases[] = { "replName", 2, 3, - "dbName"}, + "dbName", + kGlobalSSLMode}, {"mongodb://user:pwd@[::1]/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:foobar", "user", @@ -325,7 +385,8 @@ const URITestCase validCases[] = { "", 1, 2, - ""}, + "", + kGlobalSSLMode}, {"mongodb://user:pwd@[::1]/?authMechanism=GSSAPI&gssapiServiceName=foobar", "user", @@ -334,9 +395,10 @@ const URITestCase validCases[] = { "", 1, 2, - ""}, + "", + kGlobalSSLMode}, - {"mongodb://%2Ftmp%2Fmongodb-27017.sock", "", "", kMaster, "", 1, 0, ""}, + {"mongodb://%2Ftmp%2Fmongodb-27017.sock", "", "", kMaster, "", 1, 0, "", kGlobalSSLMode}, {"mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/?replicaSet=replName", "", @@ -345,7 +407,11 @@ const URITestCase validCases[] = { "replName", 2, 1, - ""}, + "", + kGlobalSSLMode}, + + {"mongodb://localhost/?ssl=true", "", "", kMaster, "", 1, 1, "", kEnableSSL}, + {"mongodb://localhost/?ssl=false", "", "", kMaster, "", 1, 1, "", kDisableSSL}, }; const InvalidURITestCase invalidCases[] = { @@ -364,10 +430,7 @@ const InvalidURITestCase invalidCases[] = { {"mongodb://localhost:27017localhost:27018"}, // % symbol in password must be escaped. - {"mongodb://localhost:pass%word@127.0.0.1:27017", - Status(ErrorCodes::FailedToParse, - "The characters after the % do not form a hex value. Please escape the % or pass a " - "valid hex value. ")}, + {"mongodb://localhost:pass%word@127.0.0.1:27017", ErrorCodes::duplicateCodeForTest(51040)}, // Domain sockets have to end in ".sock". {"mongodb://%2Fnotareal%2Fdomainsock"}, @@ -419,6 +482,9 @@ const InvalidURITestCase invalidCases[] = { // Missing an entire key-value pair {"mongodb://127.0.0.1:1234/dbName?foo=a&&c=b"}, + + // Illegal value for ssl. + {"mongodb://127.0.0.1:1234/dbName?ssl=blah", ErrorCodes::duplicateCodeForTest(51041)}, }; // Helper Method to take a filename for a json file and return the array of tests inside of it @@ -480,8 +546,8 @@ TEST(MongoURI, InvalidURIs) { unittest::log() << "Testing URI: " << testCase.URI << '\n'; auto cs_status = MongoURI::parse(testCase.URI); ASSERT_NOT_OK(cs_status); - if (testCase.status) { - ASSERT_EQUALS(testCase.status, cs_status.getStatus()); + if (testCase.code) { + ASSERT_EQUALS(*testCase.code, cs_status.getStatus()); } } } diff --git a/src/mongo/client/replica_set_monitor.cpp b/src/mongo/client/replica_set_monitor.cpp index 187b0c70462..306ce15e3ab 100644 --- a/src/mongo/client/replica_set_monitor.cpp +++ b/src/mongo/client/replica_set_monitor.cpp @@ -369,6 +369,11 @@ std::string ReplicaSetMonitor::getServerAddress() const { return _state->getConfirmedServerAddress(); } +const MongoURI& ReplicaSetMonitor::getOriginalUri() const { + // setUri is const so no need to lock. + return _state->setUri; +} + bool ReplicaSetMonitor::contains(const HostAndPort& host) const { stdx::lock_guard<stdx::mutex> lk(_state->mutex); return _state->seedNodes.count(host); @@ -996,13 +1001,14 @@ void Node::update(const IsMasterReply& reply) { lastWriteDateUpdateTime = Date_t::now(); } -SetState::SetState(StringData name, const std::set<HostAndPort>& seedNodes) +SetState::SetState(StringData name, const std::set<HostAndPort>& seedNodes, MongoURI uri) : name(name.toString()), consecutiveFailedScans(0), seedNodes(seedNodes), latencyThresholdMicros(serverGlobalParams.defaultLocalThresholdMillis * 1000), rand(int64_t(time(0))), roundRobin(0), + setUri(std::move(uri)), refreshPeriod(getDefaultRefreshPeriod()) { uassert(13642, "Replica set seed list can't be empty", !seedNodes.empty()); @@ -1024,9 +1030,8 @@ SetState::SetState(StringData name, const std::set<HostAndPort>& seedNodes) SetState::SetState(const MongoURI& uri) : SetState(uri.getSetName(), - std::set<HostAndPort>(uri.getServers().begin(), uri.getServers().end())) { - setUri = uri; -} + std::set<HostAndPort>(uri.getServers().begin(), uri.getServers().end()), + uri) {} HostAndPort SetState::getMatchingHost(const ReadPreferenceSetting& criteria) const { switch (criteria.pref) { diff --git a/src/mongo/client/replica_set_monitor.h b/src/mongo/client/replica_set_monitor.h index b394425810c..387b34eceb6 100644 --- a/src/mongo/client/replica_set_monitor.h +++ b/src/mongo/client/replica_set_monitor.h @@ -152,10 +152,17 @@ public: /** * Returns a std::string with the format name/server1,server2. * If name is empty, returns just comma-separated list of servers. + * It IS updated to reflect the current members of the set. */ std::string getServerAddress() const; /** + * Returns the URI that was used to construct this monitor. + * It IS NOT updated to reflect the current members of the set. + */ + const MongoURI& getOriginalUri() const; + + /** * Is server part of this set? Uses only cached information. */ bool contains(const HostAndPort& server) const; diff --git a/src/mongo/client/replica_set_monitor_internal.h b/src/mongo/client/replica_set_monitor_internal.h index ab3b3d02380..385d3f845d9 100644 --- a/src/mongo/client/replica_set_monitor_internal.h +++ b/src/mongo/client/replica_set_monitor_internal.h @@ -143,7 +143,7 @@ public: /** * seedNodes must not be empty */ - SetState(StringData name, const std::set<HostAndPort>& seedNodes); + SetState(StringData name, const std::set<HostAndPort>& seedNodes, MongoURI uri = {}); SetState(const MongoURI& uri); @@ -208,7 +208,7 @@ public: int64_t latencyThresholdMicros; mutable PseudoRandom rand; // only used for host selection to balance load mutable int roundRobin; // used when useDeterministicHostSelection is true - MongoURI setUri; // URI that may have constructed this + const MongoURI setUri; // URI that may have constructed this Seconds refreshPeriod; }; diff --git a/src/mongo/client/replica_set_monitor_manager.cpp b/src/mongo/client/replica_set_monitor_manager.cpp index 1204c33f656..be61056daf6 100644 --- a/src/mongo/client/replica_set_monitor_manager.cpp +++ b/src/mongo/client/replica_set_monitor_manager.cpp @@ -97,6 +97,12 @@ void ReplicaSetMonitorManager::_setupTaskExecutorInLock(const std::string& name) } } +namespace { +void uassertNotMixingSSL(transport::ConnectSSLMode a, transport::ConnectSSLMode b) { + uassert(51042, "Mixing ssl modes with a single replica set is disallowed", a == b); +} +} + shared_ptr<ReplicaSetMonitor> ReplicaSetMonitorManager::getOrCreateMonitor( const ConnectionString& connStr) { invariant(connStr.type() == ConnectionString::SET); @@ -106,6 +112,7 @@ shared_ptr<ReplicaSetMonitor> ReplicaSetMonitorManager::getOrCreateMonitor( auto setName = connStr.getSetName(); auto monitor = _monitors[setName].lock(); if (monitor) { + uassertNotMixingSSL(monitor->getOriginalUri().getSSLMode(), transport::kGlobalSSLMode); return monitor; } @@ -127,6 +134,7 @@ shared_ptr<ReplicaSetMonitor> ReplicaSetMonitorManager::getOrCreateMonitor(const const auto& setName = uri.getSetName(); auto monitor = _monitors[setName].lock(); if (monitor) { + uassertNotMixingSSL(monitor->getOriginalUri().getSSLMode(), uri.getSSLMode()); return monitor; } |