From b5308fc30a1ec7405ccec6dcc4213cf5fb167a4e Mon Sep 17 00:00:00 2001 From: Spencer Jackson Date: Wed, 21 Nov 2018 17:53:58 -0500 Subject: SERVER-37962: Create tlsMode setParameter --- jstests/ssl/set_parameter_ssl.js | 60 +++++++++++----- src/mongo/util/net/ssl_options.cpp | 72 +++++++++++++++++++ src/mongo/util/net/ssl_options.h | 8 ++- src/mongo/util/net/ssl_options_server.cpp | 22 ++---- src/mongo/util/net/ssl_parameters.cpp | 115 ++++++++++++++++-------------- src/mongo/util/net/ssl_parameters.h | 2 + src/mongo/util/net/ssl_parameters.idl | 6 ++ 7 files changed, 196 insertions(+), 89 deletions(-) diff --git a/jstests/ssl/set_parameter_ssl.js b/jstests/ssl/set_parameter_ssl.js index 39b0e0040a9..ddb46df2309 100644 --- a/jstests/ssl/set_parameter_ssl.js +++ b/jstests/ssl/set_parameter_ssl.js @@ -3,14 +3,35 @@ var SERVER_CERT = "jstests/libs/server.pem"; var CA_CERT = "jstests/libs/ca.pem"; -function testSSLTransition(oldMode, newMode, shouldSucceed) { +class TransportMode { + constructor(sslName, tlsName) { + this.sslName = sslName; + this.tlsName = tlsName; + } + + get sslMode() { + return this.sslName; + } + + get tlsMode() { + return this.tlsName; + } +} + +const invalid = new TransportMode("invalid", "invalid"); +const disabled = new TransportMode("disabled", "disabled"); +const allowed = new TransportMode("allowSSL", "allowTLS"); +const prefered = new TransportMode("preferSSL", "preferTLS"); +const required = new TransportMode("requireSSL", "requireTLS"); + +function testTransportTransition(scheme, oldMode, newMode, shouldSucceed) { var conn = MongoRunner.runMongod({sslMode: oldMode, sslPEMKeyFile: SERVER_CERT, sslCAFile: CA_CERT}); var adminDB = conn.getDB("admin"); adminDB.createUser({user: "root", pwd: "pwd", roles: ['root']}); adminDB.auth("root", "pwd"); - var res = adminDB.runCommand({"setParameter": 1, "sslMode": newMode}); + var res = adminDB.runCommand({"setParameter": 1, [scheme]: newMode[scheme]}); assert(res["ok"] == shouldSucceed, tojson(res)); if (!shouldSucceed) { @@ -46,21 +67,26 @@ function testAuthModeTransition(oldMode, newMode, sslMode, shouldSucceed) { MongoRunner.stopMongod(conn); } -testSSLTransition("allowSSL", "invalid", false); -testSSLTransition("allowSSL", "disabled", false); -testSSLTransition("allowSSL", "allowSSL", false); -testSSLTransition("allowSSL", "preferSSL", true); -testSSLTransition("allowSSL", "requireSSL", false); -testSSLTransition("preferSSL", "invalid", false); -testSSLTransition("preferSSL", "disabled", false); -testSSLTransition("preferSSL", "allowSSL", false); -testSSLTransition("preferSSL", "preferSSL", false); -testSSLTransition("preferSSL", "requireSSL", true); -testSSLTransition("requireSSL", "invalid", false); -testSSLTransition("requireSSL", "disabled", false); -testSSLTransition("requireSSL", "allowSSL", false); -testSSLTransition("requireSSL", "preferSSL", false); -testSSLTransition("requireSSL", "requireSSL", false); +function testTransportTransitions(scheme) { + testTransportTransition(scheme, "allowSSL", invalid, false); + testTransportTransition(scheme, "allowSSL", disabled, false); + testTransportTransition(scheme, "allowSSL", allowed, false); + testTransportTransition(scheme, "allowSSL", prefered, true); + testTransportTransition(scheme, "allowSSL", required, false); + testTransportTransition(scheme, "preferSSL", invalid, false); + testTransportTransition(scheme, "preferSSL", disabled, false); + testTransportTransition(scheme, "preferSSL", allowed, false); + testTransportTransition(scheme, "preferSSL", prefered, false); + testTransportTransition(scheme, "preferSSL", required, true); + testTransportTransition(scheme, "requireSSL", invalid, false); + testTransportTransition(scheme, "requireSSL", disabled, false); + testTransportTransition(scheme, "requireSSL", allowed, false); + testTransportTransition(scheme, "requireSSL", prefered, false); + testTransportTransition(scheme, "requireSSL", required, false); +} + +testTransportTransitions("sslMode"); +testTransportTransitions("tlsMode"); testAuthModeTransition("sendKeyFile", "invalid", "requireSSL", false); testAuthModeTransition("sendKeyFile", "keyFile", "requireSSL", false); diff --git a/src/mongo/util/net/ssl_options.cpp b/src/mongo/util/net/ssl_options.cpp index cab1a47d05f..b86c4d0e130 100644 --- a/src/mongo/util/net/ssl_options.cpp +++ b/src/mongo/util/net/ssl_options.cpp @@ -162,4 +162,76 @@ Status parseCertificateSelector(SSLParams::CertificateSelector* selector, return Status::OK(); } +StatusWith SSLParams::sslModeParse(StringData strMode) { + if (strMode == "disabled") { + return SSLParams::SSLMode_disabled; + } else if (strMode == "allowSSL") { + return SSLParams::SSLMode_allowSSL; + } else if (strMode == "preferSSL") { + return SSLParams::SSLMode_preferSSL; + } else if (strMode == "requireSSL") { + return SSLParams::SSLMode_requireSSL; + } else { + return Status( + ErrorCodes::BadValue, + str::stream() + << "Invalid sslMode setting '" + << strMode + << "', expected one of: 'disabled', 'allowSSL', 'preferSSL', or 'requireSSL'"); + } +} + +StatusWith SSLParams::tlsModeParse(StringData strMode) { + if (strMode == "disabled") { + return SSLParams::SSLMode_disabled; + } else if (strMode == "allowTLS") { + return SSLParams::SSLMode_allowSSL; + } else if (strMode == "preferTLS") { + return SSLParams::SSLMode_preferSSL; + } else if (strMode == "requireTLS") { + return SSLParams::SSLMode_requireSSL; + } else { + return Status( + ErrorCodes::BadValue, + str::stream() + << "Invalid tlsMode setting '" + << strMode + << "', expected one of: 'disabled', 'allowTLS', 'preferTLS', or 'requireTLS'"); + } +} + + +std::string SSLParams::sslModeFormat(AtomicInt32::WordType mode) { + switch (mode) { + case SSLParams::SSLMode_disabled: + return "disabled"; + case SSLParams::SSLMode_allowSSL: + return "allowSSL"; + case SSLParams::SSLMode_preferSSL: + return "preferSSL"; + case SSLParams::SSLMode_requireSSL: + return "requireSSL"; + default: + // Default case because sslMode is an AtomicInt32 and not bound by enum rules. + return "unknown"; + } +} + +std::string SSLParams::tlsModeFormat(AtomicInt32::WordType mode) { + switch (mode) { + case SSLParams::SSLMode_disabled: + return "disabled"; + case SSLParams::SSLMode_allowSSL: + return "allowTLS"; + case SSLParams::SSLMode_preferSSL: + return "preferTLS"; + case SSLParams::SSLMode_requireSSL: + return "requireTLS"; + default: + // Default case because sslMode is an AtomicInt32 and not bound by enum rules. + return "unknown"; + } +} + + } // namespace mongo diff --git a/src/mongo/util/net/ssl_options.h b/src/mongo/util/net/ssl_options.h index 391c9a98bdb..a5f4d936cb6 100644 --- a/src/mongo/util/net/ssl_options.h +++ b/src/mongo/util/net/ssl_options.h @@ -34,6 +34,7 @@ #include #include "mongo/base/status.h" +#include "mongo/base/status_with.h" #include "mongo/config.h" namespace mongo { @@ -91,7 +92,7 @@ struct SSLParams { sslMode.store(SSLMode_disabled); } - enum SSLModes { + enum SSLModes : AtomicInt32::WordType { /** * Make unencrypted outgoing connections and do not accept incoming SSL-connections. */ @@ -112,6 +113,11 @@ struct SSLParams { */ SSLMode_requireSSL }; + + static StatusWith sslModeParse(StringData strMode); + static StatusWith tlsModeParse(StringData strMode); + static std::string sslModeFormat(AtomicInt32::WordType mode); + static std::string tlsModeFormat(AtomicInt32::WordType mode); }; extern SSLParams sslGlobalParams; diff --git a/src/mongo/util/net/ssl_options_server.cpp b/src/mongo/util/net/ssl_options_server.cpp index 0af940adf63..bcca608e8bf 100644 --- a/src/mongo/util/net/ssl_options_server.cpp +++ b/src/mongo/util/net/ssl_options_server.cpp @@ -248,27 +248,17 @@ Status storeTLSLogVersion(const std::string& loggedProtocols) { Status storeSSLServerOptions(const moe::Environment& params) { if (params.count("net.tls.mode")) { std::string sslModeParam = params["net.tls.mode"].as(); - if (sslModeParam == "disabled") { - sslGlobalParams.sslMode.store(SSLParams::SSLMode_disabled); - } else if (sslModeParam == "allowTLS") { - sslGlobalParams.sslMode.store(SSLParams::SSLMode_allowSSL); - } else if (sslModeParam == "preferTLS") { - sslGlobalParams.sslMode.store(SSLParams::SSLMode_preferSSL); - } else if (sslModeParam == "requireTLS") { - sslGlobalParams.sslMode.store(SSLParams::SSLMode_requireSSL); + auto swMode = SSLParams::tlsModeParse(sslModeParam); + if (swMode.isOK()) { + sslGlobalParams.sslMode.store(swMode.getValue()); } else { return {ErrorCodes::BadValue, "unsupported value for tlsMode " + sslModeParam}; } } else if (params.count("net.ssl.mode")) { std::string sslModeParam = params["net.ssl.mode"].as(); - if (sslModeParam == "disabled") { - sslGlobalParams.sslMode.store(SSLParams::SSLMode_disabled); - } else if (sslModeParam == "allowSSL") { - sslGlobalParams.sslMode.store(SSLParams::SSLMode_allowSSL); - } else if (sslModeParam == "preferSSL") { - sslGlobalParams.sslMode.store(SSLParams::SSLMode_preferSSL); - } else if (sslModeParam == "requireSSL") { - sslGlobalParams.sslMode.store(SSLParams::SSLMode_requireSSL); + auto swMode = SSLParams::sslModeParse(sslModeParam); + if (swMode.isOK()) { + sslGlobalParams.sslMode.store(swMode.getValue()); } else { return {ErrorCodes::BadValue, "unsupported value for sslMode " + sslModeParam}; } diff --git a/src/mongo/util/net/ssl_parameters.cpp b/src/mongo/util/net/ssl_parameters.cpp index e8cfc506b1c..2fe9e66cb43 100644 --- a/src/mongo/util/net/ssl_parameters.cpp +++ b/src/mongo/util/net/ssl_parameters.cpp @@ -28,53 +28,22 @@ * it in the license file. */ +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kNetwork + #include "mongo/platform/basic.h" #include "mongo/client/authenticate.h" #include "mongo/config.h" #include "mongo/db/auth/sasl_command_constants.h" #include "mongo/db/server_options.h" +#include "mongo/util/log.h" #include "mongo/util/net/ssl_options.h" #include "mongo/util/net/ssl_parameters.h" namespace mongo { namespace { -std::string sslModeStr() { - switch (sslGlobalParams.sslMode.load()) { - case SSLParams::SSLMode_disabled: - return "disabled"; - case SSLParams::SSLMode_allowSSL: - return "allowSSL"; - case SSLParams::SSLMode_preferSSL: - return "preferSSL"; - case SSLParams::SSLMode_requireSSL: - return "requireSSL"; - default: - // Default case because sslMode is an AtomicInt32 and not bound by enum rules. - return "unknown"; - } -} -StatusWith sslModeParse(StringData strMode) { - if (strMode == "disabled") { - return SSLParams::SSLMode_disabled; - } else if (strMode == "allowSSL") { - return SSLParams::SSLMode_allowSSL; - } else if (strMode == "preferSSL") { - return SSLParams::SSLMode_preferSSL; - } else if (strMode == "requireSSL") { - return SSLParams::SSLMode_requireSSL; - } else { - return Status( - ErrorCodes::BadValue, - str::stream() - << "Invalid sslMode setting '" - << strMode - << "', expected one of: 'disabled', 'allowSSL', 'preferSSL', or 'requireSSL'"); - } -} - -std::string clusterAuthModeStr() { +std::string clusterAuthModeFormat() { switch (serverGlobalParams.clusterAuthMode.load()) { case ServerGlobalParams::ClusterAuthMode_keyFile: return "keyFile"; @@ -107,6 +76,32 @@ StatusWith clusterAuthModeParse(StringData } } + +template +StatusWith checkTLSModeTransition(T modeToString, + U stringToMode, + StringData parameterName, + StringData strMode) { + auto mode = stringToMode(strMode); + if (!mode.isOK()) { + return mode.getStatus(); + } + auto oldMode = sslGlobalParams.sslMode.load(); + if ((mode == SSLParams::SSLMode_preferSSL) && (oldMode == SSLParams::SSLMode_allowSSL)) { + return mode; + } else if ((mode == SSLParams::SSLMode_requireSSL) && + (oldMode == SSLParams::SSLMode_preferSSL)) { + return mode; + } else { + return {ErrorCodes::BadValue, + str::stream() << "Illegal state transition for " << parameterName + << ", attempt to change from " + << modeToString(static_cast(oldMode)) + << " to " + << strMode}; + } +} + } // namespace } // namespace mongo @@ -143,7 +138,14 @@ mongo::Status mongo::onUpdateDisableNonTLSConnectionLogging(const bool&) { } void mongo::appendSSLModeToBSON(OperationContext*, BSONObjBuilder* builder, StringData fieldName) { - builder->append(fieldName, sslModeStr()); + warning() << "Use of deprecared server parameter 'sslMode', please use 'tlsMode' instead."; + builder->append(fieldName, SSLParams::sslModeFormat(sslGlobalParams.sslMode.load())); +} + +void mongo::appendTLSModeToBSON(OperationContext*, BSONObjBuilder* builder, StringData fieldName) { + builder->append( + fieldName, + SSLParams::tlsModeFormat(static_cast(sslGlobalParams.sslMode.load()))); } mongo::Status mongo::setSSLModeFromString(StringData strMode) { @@ -152,33 +154,36 @@ mongo::Status mongo::setSSLModeFromString(StringData strMode) { "Unable to set sslMode, SSL support is not compiled into server"}; #endif - auto swMode = sslModeParse(strMode); - if (!swMode.isOK()) { - return swMode.getStatus(); - } + warning() << "Use of deprecared server parameter 'sslMode', please use 'tlsMode' instead."; - auto mode = swMode.getValue(); - auto oldMode = sslGlobalParams.sslMode.load(); - if ((mode == SSLParams::SSLMode_preferSSL) && (oldMode == SSLParams::SSLMode_allowSSL)) { - sslGlobalParams.sslMode.store(mode); - } else if ((mode == SSLParams::SSLMode_requireSSL) && - (oldMode == SSLParams::SSLMode_preferSSL)) { - sslGlobalParams.sslMode.store(mode); - } else { - return {ErrorCodes::BadValue, - str::stream() << "Illegal state transition for sslMode, attempt to change from " - << sslModeStr() - << " to " - << strMode}; + auto swNewMode = checkTLSModeTransition( + SSLParams::sslModeFormat, SSLParams::sslModeParse, "sslMode", strMode); + if (!swNewMode.isOK()) { + return swNewMode.getStatus(); } + sslGlobalParams.sslMode.store(swNewMode.getValue()); + return Status::OK(); +} +mongo::Status mongo::setTLSModeFromString(StringData strMode) { +#ifndef MONGO_CONFIG_SSL + return {ErrorCodes::IllegalOperation, + "Unable to set tlsMode, TLS support is not compiled into server"}; +#endif + auto swNewMode = checkTLSModeTransition( + SSLParams::tlsModeFormat, SSLParams::tlsModeParse, "tlsMode", strMode); + if (!swNewMode.isOK()) { + return swNewMode.getStatus(); + } + sslGlobalParams.sslMode.store(swNewMode.getValue()); return Status::OK(); } + void mongo::appendClusterAuthModeToBSON(OperationContext*, BSONObjBuilder* builder, StringData fieldName) { - builder->append(fieldName, clusterAuthModeStr()); + builder->append(fieldName, clusterAuthModeFormat()); } mongo::Status mongo::setClusterAuthModeFromString(StringData strMode) { @@ -214,7 +219,7 @@ mongo::Status mongo::setClusterAuthModeFromString(StringData strMode) { } else { return {ErrorCodes::BadValue, str::stream() << "Illegal state transition for clusterAuthMode, change from " - << clusterAuthModeStr() + << clusterAuthModeFormat() << " to " << strMode}; } diff --git a/src/mongo/util/net/ssl_parameters.h b/src/mongo/util/net/ssl_parameters.h index bd1d6304f4d..e88c3908382 100644 --- a/src/mongo/util/net/ssl_parameters.h +++ b/src/mongo/util/net/ssl_parameters.h @@ -57,7 +57,9 @@ Status onUpdateDisableNonTLSConnectionLogging(const bool&); * Callbacks for setParameter 'sslMode' */ void appendSSLModeToBSON(OperationContext*, BSONObjBuilder*, StringData); +void appendTLSModeToBSON(OperationContext*, BSONObjBuilder*, StringData); Status setSSLModeFromString(StringData); +Status setTLSModeFromString(StringData); /** * Callbacks for setParameter 'clusterAuthMode' diff --git a/src/mongo/util/net/ssl_parameters.idl b/src/mongo/util/net/ssl_parameters.idl index 56121e285b0..a276c52b94c 100644 --- a/src/mongo/util/net/ssl_parameters.idl +++ b/src/mongo/util/net/ssl_parameters.idl @@ -72,6 +72,12 @@ server_parameters: append_bson: "appendSSLModeToBSON" from_string: "setSSLModeFromString" + tlsMode: + description: "Transition from allowTLS to preferTLS, or from preferTLS to requireTLS" + set_at: runtime + append_bson: "appendTLSModeToBSON" + from_string: "setTLSModeFromString" + clusterAuthMode: description: "Transition from sendKeyFile to sendX509, or sendX509 to x509 clusterAuthModes" set_at: runtime -- cgit v1.2.1