summaryrefslogtreecommitdiff
path: root/src/mongo/util/net/ssl_options_server.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/util/net/ssl_options_server.cpp')
-rw-r--r--src/mongo/util/net/ssl_options_server.cpp473
1 files changed, 473 insertions, 0 deletions
diff --git a/src/mongo/util/net/ssl_options_server.cpp b/src/mongo/util/net/ssl_options_server.cpp
new file mode 100644
index 00000000000..930eccaf2c4
--- /dev/null
+++ b/src/mongo/util/net/ssl_options_server.cpp
@@ -0,0 +1,473 @@
+/**
+ * Copyright (C) 2018 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * 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 GNU Affero General 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.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kControl
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/util/net/ssl_options.h"
+
+#include <boost/filesystem/operations.hpp>
+
+#include "mongo/base/status.h"
+#include "mongo/config.h"
+#include "mongo/db/server_options.h"
+#include "mongo/util/log.h"
+#include "mongo/util/options_parser/startup_option_init.h"
+#include "mongo/util/options_parser/startup_options.h"
+
+#if MONGO_CONFIG_SSL_PROVIDER == MONGO_CONFIG_SSL_PROVIDER_OPENSSL
+#include <openssl/ssl.h>
+#endif
+
+namespace moe = mongo::optionenvironment;
+using std::string;
+
+// Export these to the process space for the sake of ssl_options_test.cpp
+// but don't provide a header because we don't want to encourage use from elsewhere.
+namespace mongo {
+Status addSSLServerOptions(moe::OptionSection* options) {
+ options
+ ->addOptionChaining("net.tls.tlsOnNormalPorts",
+ "tlsOnNormalPorts",
+ moe::Switch,
+ "Use TLS on configured ports",
+ {"net.ssl.sslOnNormalPorts"},
+ {"sslOnNormalPorts"})
+ .setSources(moe::SourceAllLegacy)
+ .incompatibleWith("net.tls.mode")
+ .incompatibleWith("net.ssl.mode");
+
+ options
+ ->addOptionChaining("net.tls.mode",
+ "tlsMode",
+ moe::String,
+ "Set the TLS operation mode (disabled|allowTLS|preferTLS|requireTLS)")
+ .incompatibleWith("net.ssl.mode");
+ options
+ ->addOptionChaining("net.ssl.mode",
+ "sslMode",
+ moe::String,
+ "Set the TLS operation mode (disabled|allowSSL|preferSSL|requireSSL)")
+ .incompatibleWith("net.tls.mode")
+ .hidden();
+
+ options->addOptionChaining("net.tls.PEMKeyFile",
+ "tlsPEMKeyFile",
+ moe::String,
+ "PEM file for TLS",
+ {"net.ssl.PEMKeyFile"},
+ {"sslPEMKeyFile"});
+
+ options
+ ->addOptionChaining("net.tls.PEMKeyPassword",
+ "tlsPEMKeyPassword",
+ moe::String,
+ "PEM file password",
+ {"net.ssl.PEMKeyPassword"},
+ {"sslPEMKeyPassword"})
+ .setImplicit(moe::Value(std::string("")));
+
+ options->addOptionChaining("net.tls.clusterFile",
+ "tlsClusterFile",
+ moe::String,
+ "Key file for internal TLS authentication",
+ {"net.ssl.clusterFile"},
+ {"sslClusterFile"});
+
+ options
+ ->addOptionChaining("net.tls.clusterPassword",
+ "tlsClusterPassword",
+ moe::String,
+ "Internal authentication key file password",
+ {"net.ssl.clusterPassword"},
+ {"sslClusterPassword"})
+ .setImplicit(moe::Value(std::string("")));
+
+ options->addOptionChaining("net.tls.CAFile",
+ "tlsCAFile",
+ moe::String,
+ "Certificate Authority file for TLS",
+ {"net.ssl.CAFile"},
+ {"sslCAFile"});
+
+ options->addOptionChaining("net.tls.CRLFile",
+ "tlsCRLFile",
+ moe::String,
+ "Certificate Revocation List file for TLS",
+ {"net.ssl.CRLFile"},
+ {"sslCRLFile"});
+
+ options
+ ->addOptionChaining("net.tls.tlsCipherConfig",
+ "tlsCipherConfig",
+ moe::String,
+ "OpenSSL cipher configuration string",
+ {"net.ssl.sslCipherConfig"},
+ {"sslCipherConfig"})
+ .hidden();
+
+ options->addOptionChaining(
+ "net.tls.disabledProtocols",
+ "tlsDisabledProtocols",
+ moe::String,
+ "Comma separated list of TLS protocols to disable [TLS1_0,TLS1_1,TLS1_2]",
+ {"net.ssl.disabledProtocols"},
+ {"sslDisabledProtocols"});
+
+ options->addOptionChaining("net.tls.weakCertificateValidation",
+ "tlsWeakCertificateValidation",
+ moe::Switch,
+ "Allow client to connect without presenting a certificate",
+ {"net.ssl.weakCertificateValidation"},
+ {"sslWeakCertificateValidation"});
+
+ // Alias for --tlsWeakCertificateValidation.
+ options->addOptionChaining("net.tls.allowConnectionsWithoutCertificates",
+ "tlsAllowConnectionsWithoutCertificates",
+ moe::Switch,
+ "Allow client to connect without presenting a certificate",
+ {"net.ssl.allowConnectionsWithoutCertificates"},
+ {"sslAllowConnectionsWithoutCertificates"});
+
+ options->addOptionChaining("net.tls.allowInvalidHostnames",
+ "tlsAllowInvalidHostnames",
+ moe::Switch,
+ "Allow server certificates to provide non-matching hostnames",
+ {"net.ssl.allowInvalidHostnames"},
+ {"sslAllowInvalidHostnames"});
+
+ options->addOptionChaining("net.tls.allowInvalidCertificates",
+ "tlsAllowInvalidCertificates",
+ moe::Switch,
+ "Allow connections to servers with invalid certificates",
+ {"net.ssl.allowInvalidCertificates"},
+ {"sslAllowInvalidCertificates"});
+
+ options->addOptionChaining("net.tls.FIPSMode",
+ "tlsFIPSMode",
+ moe::Switch,
+ "Activate FIPS 140-2 mode at startup",
+ {"net.ssl.FIPSMode"},
+ {"sslFIPSMode"});
+
+#ifdef MONGO_CONFIG_SSL_CERTIFICATE_SELECTORS
+ options
+ ->addOptionChaining("net.tls.certificateSelector",
+ "tlsCertificateSelector",
+ moe::String,
+ "TLS Certificate in system store",
+ {"net.ssl.certificateSelector"},
+ {"sslCertificateSelector"})
+ .incompatibleWith("net.tls.PEMKeyFile")
+ .incompatibleWith("net.tls.PEMKeyPassword");
+
+ options
+ ->addOptionChaining("net.tls.clusterCertificateSelector",
+ "tlsClusterCertificateSelector",
+ moe::String,
+ "SSL/TLS Certificate in system store for internal TLS authentication",
+ {"net.ssl.clusterCertificateSelector"},
+ {"sslClusterCertificateSelector"})
+ .incompatibleWith("net.tls.clusterFile")
+ .incompatibleWith("net.tls.clusterFilePassword");
+#endif
+
+ return Status::OK();
+}
+
+Status storeSSLServerOptions(const moe::Environment& params) {
+ if (params.count("net.tls.mode")) {
+ std::string sslModeParam = params["net.tls.mode"].as<string>();
+ 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);
+ } else {
+ return {ErrorCodes::BadValue, "unsupported value for tlsMode " + sslModeParam};
+ }
+ } else if (params.count("net.ssl.mode")) {
+ std::string sslModeParam = params["net.ssl.mode"].as<string>();
+ 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);
+ } else {
+ return {ErrorCodes::BadValue, "unsupported value for sslMode " + sslModeParam};
+ }
+ }
+
+ if (params.count("net.tls.PEMKeyFile")) {
+ sslGlobalParams.sslPEMKeyFile =
+ boost::filesystem::absolute(params["net.tls.PEMKeyFile"].as<string>()).generic_string();
+ }
+
+ if (params.count("net.tls.PEMKeyPassword")) {
+ sslGlobalParams.sslPEMKeyPassword = params["net.tls.PEMKeyPassword"].as<string>();
+ }
+
+ if (params.count("net.tls.clusterFile")) {
+ sslGlobalParams.sslClusterFile =
+ boost::filesystem::absolute(params["net.tls.clusterFile"].as<string>())
+ .generic_string();
+ }
+
+ if (params.count("net.tls.clusterPassword")) {
+ sslGlobalParams.sslClusterPassword = params["net.tls.clusterPassword"].as<string>();
+ }
+
+ if (params.count("net.tls.CAFile")) {
+ sslGlobalParams.sslCAFile =
+ boost::filesystem::absolute(params["net.tls.CAFile"].as<std::string>())
+ .generic_string();
+ }
+
+ if (params.count("net.tls.CRLFile")) {
+ sslGlobalParams.sslCRLFile =
+ boost::filesystem::absolute(params["net.tls.CRLFile"].as<std::string>())
+ .generic_string();
+ }
+
+ if (params.count("net.tls.tlsCipherConfig")) {
+ warning()
+ << "net.tls.tlsCipherConfig is deprecated. It will be removed in a future release.";
+ if (!sslGlobalParams.sslCipherConfig.empty()) {
+ return {ErrorCodes::BadValue,
+ "net.tls.tlsCipherConfig is incompatible with the openTLSCipherConfig "
+ "setParameter"};
+ }
+ sslGlobalParams.sslCipherConfig = params["net.tls.tlsCipherConfig"].as<string>();
+ }
+
+ if (params.count("net.tls.disabledProtocols")) {
+ const auto status =
+ storeSSLDisabledProtocols(params["net.tls.disabledProtocols"].as<string>(),
+ SSLDisabledProtocolsMode::kAcceptNegativePrefix);
+ if (!status.isOK()) {
+ return status;
+ }
+#if (MONGO_CONFIG_SSL_PROVIDER != MONGO_CONFIG_SSL_PROVIDER_OPENSSL) || \
+ (OPENSSL_VERSION_NUMBER >= 0x100000cf) /* 1.0.0l */
+ } else {
+ /* Disable TLS 1.0 by default on all platforms
+ * except on mongod/mongos which were built with an
+ * old version of OpenSSL (pre 1.0.0l)
+ * which does not support TLS 1.1 or later.
+ */
+ log() << "Automatically disabling TLS 1.0, to force-enable TLS 1.0 "
+ "specify --sslDisabledProtocols 'none'";
+ sslGlobalParams.sslDisabledProtocols.push_back(SSLParams::Protocols::TLS1_0);
+#endif
+ }
+
+ if (params.count("net.tls.weakCertificateValidation")) {
+ sslGlobalParams.sslWeakCertificateValidation =
+ params["net.tls.weakCertificateValidation"].as<bool>();
+ } else if (params.count("net.tls.allowConnectionsWithoutCertificates")) {
+ sslGlobalParams.sslWeakCertificateValidation =
+ params["net.tls.allowConnectionsWithoutCertificates"].as<bool>();
+ }
+
+ if (params.count("net.tls.allowInvalidHostnames")) {
+ sslGlobalParams.sslAllowInvalidHostnames =
+ params["net.tls.allowInvalidHostnames"].as<bool>();
+ }
+
+ if (params.count("net.tls.allowInvalidCertificates")) {
+ sslGlobalParams.sslAllowInvalidCertificates =
+ params["net.tls.allowInvalidCertificates"].as<bool>();
+ }
+
+ if (params.count("net.tls.FIPSMode")) {
+ sslGlobalParams.sslFIPSMode = params["net.tls.FIPSMode"].as<bool>();
+ }
+
+#ifdef MONGO_CONFIG_SSL_CERTIFICATE_SELECTORS
+ if (params.count("net.tls.certificateSelector")) {
+ const auto status =
+ parseCertificateSelector(&sslGlobalParams.sslCertificateSelector,
+ "net.tls.certificateSelector",
+ params["net.tls.certificateSelector"].as<std::string>());
+ if (!status.isOK()) {
+ return status;
+ }
+ }
+
+ if (params.count("net.tls.clusterCertificateSelector")) {
+ const auto status = parseCertificateSelector(
+ &sslGlobalParams.sslClusterCertificateSelector,
+ "net.tls.clusterCertificateSelector",
+ params["net.tls.clusterCertificateSelector"].as<std::string>());
+ if (!status.isOK()) {
+ return status;
+ }
+ }
+#endif
+
+ const int clusterAuthMode = serverGlobalParams.clusterAuthMode.load();
+ if (sslGlobalParams.sslMode.load() != SSLParams::SSLMode_disabled) {
+ bool usingCertifiateSelectors = params.count("net.tls.certificateSelector");
+ if (sslGlobalParams.sslPEMKeyFile.size() == 0 && !usingCertifiateSelectors) {
+ return {ErrorCodes::BadValue,
+ "need tlsPEMKeyFile or certificateSelector when TLS is enabled"};
+ }
+ if (!sslGlobalParams.sslCRLFile.empty() && sslGlobalParams.sslCAFile.empty()) {
+ return {ErrorCodes::BadValue, "need tlsCAFile with tlsCRLFile"};
+ }
+
+ std::string sslCANotFoundError(
+ "No TLS certificate validation can be performed since"
+ " no CA file has been provided; please specify an"
+ " tlsCAFile parameter");
+
+ // When using cetificate selectors, we use the local system certificate store for verifying
+ // X.509 certificates for auth instead of relying on a CA file.
+ if (sslGlobalParams.sslCAFile.empty() && !usingCertifiateSelectors &&
+ clusterAuthMode == ServerGlobalParams::ClusterAuthMode_x509) {
+ return {ErrorCodes::BadValue, sslCANotFoundError};
+ }
+ } else if (sslGlobalParams.sslPEMKeyFile.size() || sslGlobalParams.sslPEMKeyPassword.size() ||
+ sslGlobalParams.sslClusterFile.size() || sslGlobalParams.sslClusterPassword.size() ||
+ sslGlobalParams.sslCAFile.size() || sslGlobalParams.sslCRLFile.size() ||
+ sslGlobalParams.sslCipherConfig.size() ||
+ params.count("net.tls.disabledProtocols") ||
+#ifdef MONGO_CONFIG_SSL_CERTIFICATE_SELECTORS
+ params.count("net.tls.certificateSelector") ||
+ params.count("net.tls.clusterCertificateSelector") ||
+#endif
+ sslGlobalParams.sslWeakCertificateValidation) {
+ return {ErrorCodes::BadValue,
+ "need to enable TLS via the sslMode/tlsMode flag when "
+ "using TLS configuration parameters"};
+ }
+
+ if (clusterAuthMode == ServerGlobalParams::ClusterAuthMode_sendKeyFile ||
+ clusterAuthMode == ServerGlobalParams::ClusterAuthMode_sendX509 ||
+ clusterAuthMode == ServerGlobalParams::ClusterAuthMode_x509) {
+ if (sslGlobalParams.sslMode.load() == SSLParams::SSLMode_disabled) {
+ return {ErrorCodes::BadValue, "need to enable TLS via the tlsMode flag"};
+ }
+ }
+
+ if (sslGlobalParams.sslMode.load() == SSLParams::SSLMode_allowSSL) {
+ // allowSSL and x509 is valid only when we are transitioning to auth.
+ if (clusterAuthMode == ServerGlobalParams::ClusterAuthMode_sendX509 ||
+ (clusterAuthMode == ServerGlobalParams::ClusterAuthMode_x509 &&
+ !serverGlobalParams.transitionToAuth)) {
+ return {ErrorCodes::BadValue,
+ "cannot have x.509 cluster authentication in allowTLS mode"};
+ }
+ }
+ return Status::OK();
+}
+
+namespace {
+
+// Use module API to force this section to appear after core server options.
+MONGO_MODULE_STARTUP_OPTIONS_REGISTER(SSLServerOptions)(InitializerContext*) {
+ moe::OptionSection options("SSL options");
+
+ auto status = addSSLServerOptions(&options);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ return moe::startupOptions.addSection(options);
+}
+
+Status canonicalizeSSLServerOptions(moe::Environment* params) {
+ if (params->count("net.tls.tlsOnNormalPorts") &&
+ (*params)["net.tls.tlsOnNormalPorts"].as<bool>() == true) {
+ Status ret = params->set("net.tls.mode", moe::Value(std::string("requireTLS")));
+ if (!ret.isOK()) {
+ return ret;
+ }
+ ret = params->remove("net.tls.tlsOnNormalPorts");
+ if (!ret.isOK()) {
+ return ret;
+ }
+ }
+
+ return Status::OK();
+}
+
+MONGO_STARTUP_OPTIONS_VALIDATE(SSLServerOptions)(InitializerContext*) {
+ auto status = canonicalizeSSLServerOptions(&moe::startupOptionsParsed);
+ if (!status.isOK()) {
+ return status;
+ }
+
+#ifdef _WIN32
+ const auto& params = moe::startupOptionsParsed;
+
+ if (params.count("install") || params.count("reinstall")) {
+ if (params.count("net.tls.PEMKeyFile") &&
+ !boost::filesystem::path(params["net.tls.PEMKeyFile"].as<string>()).is_absolute()) {
+ return {ErrorCodes::BadValue,
+ "PEMKeyFile requires an absolute file path with Windows services"};
+ }
+
+ if (params.count("net.tls.clusterFile") &&
+ !boost::filesystem::path(params["net.tls.clusterFile"].as<string>()).is_absolute()) {
+ return {ErrorCodes::BadValue,
+ "clusterFile requires an absolute file path with Windows services"};
+ }
+
+ if (params.count("net.tls.CAFile") &&
+ !boost::filesystem::path(params["net.tls.CAFile"].as<string>()).is_absolute()) {
+ return {ErrorCodes::BadValue,
+ "CAFile requires an absolute file path with Windows services"};
+ }
+
+ if (params.count("net.tls.CRLFile") &&
+ !boost::filesystem::path(params["net.tls.CRLFile"].as<string>()).is_absolute()) {
+ return {ErrorCodes::BadValue,
+ "CRLFile requires an absolute file path with Windows services"};
+ }
+ }
+#endif
+
+ return Status::OK();
+}
+
+// storeSSLServerOptions depends on serverGlobalParams.clusterAuthMode
+// and therefore must run later.
+MONGO_STARTUP_OPTIONS_POST(SSLServerOptions)(InitializerContext*) {
+ return storeSSLServerOptions(moe::startupOptionsParsed);
+}
+
+} // namespace
+} // namespace mongo