diff options
author | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2018-07-23 16:40:12 -0400 |
---|---|---|
committer | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2018-07-23 16:40:12 -0400 |
commit | 0c532a429d4e6f1d8473b6b4f04bf21f6b6f76cb (patch) | |
tree | 214cfca89749cd5e236a16a8cc9e028623a08394 | |
parent | d9e4d82eb846637063c32fc8e32b337aff208f33 (diff) | |
download | mongo-0c532a429d4e6f1d8473b6b4f04bf21f6b6f76cb.tar.gz |
SERVER-34558 Add server status for transport security protocol versions
-rw-r--r-- | jstests/ssl/ssl_count_protocols.js | 55 | ||||
-rw-r--r-- | src/mongo/util/net/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_manager.cpp | 35 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_manager.h | 13 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_manager_apple.cpp | 35 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_manager_openssl.cpp | 30 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_manager_windows.cpp | 44 |
7 files changed, 211 insertions, 3 deletions
diff --git a/jstests/ssl/ssl_count_protocols.js b/jstests/ssl/ssl_count_protocols.js new file mode 100644 index 00000000000..b902b4532dd --- /dev/null +++ b/jstests/ssl/ssl_count_protocols.js @@ -0,0 +1,55 @@ +// Ensure the server counts the server TLS versions used +(function() { + 'use strict'; + + var SERVER_CERT = "jstests/libs/server.pem"; + var CLIENT_CERT = "jstests/libs/client.pem"; + var CA_CERT = "jstests/libs/ca.pem"; + + function runTestWithoutSubset(client) { + let disabledProtocols = ["TLS1_0", "TLS1_1", "TLS1_2"]; + let expectedCounts = [0, 0, 1]; + var index = disabledProtocols.indexOf(client); + disabledProtocols.splice(index, 1); + expectedCounts[index] += 1; + + const conn = MongoRunner.runMongod({ + sslMode: 'allowSSL', + sslPEMKeyFile: SERVER_CERT, + sslDisabledProtocols: 'none', + }); + + print(disabledProtocols); + const version_number = client.replace(/TLS/, "").replace(/_/, "."); + + const exitStatus = + runMongoProgram('mongo', + '--ssl', + '--sslAllowInvalidHostnames', + '--sslPEMKeyFile', + CLIENT_CERT, + '--sslCAFile', + CA_CERT, + '--port', + conn.port, + '--sslDisabledProtocols', + disabledProtocols.join(","), + '--eval', + // The Javascript string "1.0" is implicitly converted to the Number(1) + // Workaround this with parseFloat + 'one = Number.parseFloat(1).toPrecision(2); a = {};' + + 'a[one] = NumberLong(' + expectedCounts[0] + ');' + + 'a["1.1"] = NumberLong(' + expectedCounts[1] + ');' + + 'a["1.2"] = NumberLong(' + expectedCounts[2] + ');' + + 'assert.eq(db.serverStatus().transportSecurity, a);'); + + assert.eq(0, exitStatus, ""); + + MongoRunner.stopMongod(conn); + } + + runTestWithoutSubset("TLS1_0"); + runTestWithoutSubset("TLS1_1"); + runTestWithoutSubset("TLS1_2"); + +})(); diff --git a/src/mongo/util/net/SConscript b/src/mongo/util/net/SConscript index ef110ccf555..acdd618e5f8 100644 --- a/src/mongo/util/net/SConscript +++ b/src/mongo/util/net/SConscript @@ -120,8 +120,10 @@ env.Library( LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/crypto/sha_block_${MONGO_CRYPTO}', '$BUILD_DIR/mongo/db/auth/internal_user_auth', + '$BUILD_DIR/mongo/db/commands/server_status', '$BUILD_DIR/mongo/db/server_options_core', '$BUILD_DIR/mongo/db/server_parameters', + '$BUILD_DIR/mongo/db/service_context', '$BUILD_DIR/mongo/util/background_job', '$BUILD_DIR/mongo/util/winutil', ], diff --git a/src/mongo/util/net/ssl_manager.cpp b/src/mongo/util/net/ssl_manager.cpp index 682a415985d..c8346091346 100644 --- a/src/mongo/util/net/ssl_manager.cpp +++ b/src/mongo/util/net/ssl_manager.cpp @@ -39,6 +39,7 @@ #include "mongo/base/init.h" #include "mongo/bson/bsonobjbuilder.h" #include "mongo/config.h" +#include "mongo/db/commands/server_status.h" #include "mongo/db/server_parameters.h" #include "mongo/platform/overflow_arithmetic.h" #include "mongo/transport/session.h" @@ -227,8 +228,15 @@ std::string x509OidToShortName(const std::string& name) { return it->second; } #endif + +const auto getTLSVersionCounts = ServiceContext::declareDecoration<TLSVersionCounts>(); + } // namespace +TLSVersionCounts& TLSVersionCounts::get(ServiceContext* serviceContext) { + return getTLSVersionCounts(serviceContext); +} + MONGO_INITIALIZER_WITH_PREREQUISITES(SSLManagerLogger, ("SSLManager", "GlobalLogManager")) (InitializerContext*) { if (!isSSLServer || (sslGlobalParams.sslMode.load() != SSLParams::SSLMode_disabled)) { @@ -241,6 +249,7 @@ MONGO_INITIALIZER_WITH_PREREQUISITES(SSLManagerLogger, ("SSLManager", "GlobalLog LOG(1) << "Server Certificate Expiration: " << config.serverCertificateExpirationDate; } } + return Status::OK(); } @@ -719,6 +728,32 @@ std::string escapeRfc2253(StringData str) { return ret; } +/** + * Status section of which tls versions connected to MongoDB and completed an SSL handshake. + * Note: Clients are only not counted if they try to connect to the server with a unsupported TLS + * version. They are still counted if the server rejects them for certificate issues in + * parseAndValidatePeerCertificate. + */ +class TLSVersionSatus : public ServerStatusSection { +public: + TLSVersionSatus() : ServerStatusSection("transportSecurity") {} + + bool includeByDefault() const final { + return true; + } + + BSONObj generateSection(OperationContext* opCtx, const BSONElement& configElement) const final { + auto& counts = TLSVersionCounts::get(opCtx->getServiceContext()); + + BSONObjBuilder builder; + builder.append("1.0", counts.tls10.load()); + builder.append("1.1", counts.tls11.load()); + builder.append("1.2", counts.tls12.load()); + return builder.obj(); + } +} tlsVersionStatus; + + #endif } // namespace mongo diff --git a/src/mongo/util/net/ssl_manager.h b/src/mongo/util/net/ssl_manager.h index 1826629bfc8..990aca6e2d9 100644 --- a/src/mongo/util/net/ssl_manager.h +++ b/src/mongo/util/net/ssl_manager.h @@ -38,6 +38,8 @@ #include "mongo/base/disallow_copying.h" #include "mongo/base/string_data.h" #include "mongo/bson/bsonobj.h" +#include "mongo/db/service_context.h" +#include "mongo/platform/atomic_word.h" #include "mongo/util/decorable.h" #include "mongo/util/net/sock.h" #include "mongo/util/net/ssl/apple.hpp" @@ -114,6 +116,17 @@ const ASN1OID mongodbRolesOID("1.3.6.1.4.1.34601.2.1.1", "MongoRoles", "Sequence of MongoDB Database Roles"); +/** + * Counts of negogtiated version used by TLS connections. + */ +struct TLSVersionCounts { + AtomicInt64 tls10; + AtomicInt64 tls11; + AtomicInt64 tls12; + + static TLSVersionCounts& get(ServiceContext* serviceContext); +}; + class SSLManagerInterface : public Decorable<SSLManagerInterface> { public: static std::unique_ptr<SSLManagerInterface> create(const SSLParams& params, bool isServer); diff --git a/src/mongo/util/net/ssl_manager_apple.cpp b/src/mongo/util/net/ssl_manager_apple.cpp index 9c604f80f1f..ed7106c66d8 100644 --- a/src/mongo/util/net/ssl_manager_apple.cpp +++ b/src/mongo/util/net/ssl_manager_apple.cpp @@ -1244,9 +1244,44 @@ SSLPeerInfo SSLManagerApple::parseAndValidatePeerCertificateDeprecated( return swPeerSubjectName.getValue().get_value_or(SSLPeerInfo()); } +void recordTLSVersion(::SSLContextRef ssl) { + ::SSLProtocol protocol; + + uassertOSStatusOK(::SSLGetNegotiatedProtocolVersion(ssl, &protocol)); + + auto& counts = mongo::TLSVersionCounts::get(getGlobalServiceContext()); + switch (protocol) { + case kTLSProtocol1: + counts.tls10.addAndFetch(1); + break; + case kTLSProtocol11: + counts.tls11.addAndFetch(1); + break; + case kTLSProtocol12: + counts.tls12.addAndFetch(1); + break; + // case kTLSProtocol13: + // counts.tls13.addAndFetch(1); + // break; + case kSSLProtocolUnknown: + case kSSLProtocol2: + case kSSLProtocol3: + case kSSLProtocol3Only: + case kTLSProtocol1Only: + case kSSLProtocolAll: + case kDTLSProtocol1: + // Do nothing + break; + } +} + + StatusWith<boost::optional<SSLPeerInfo>> SSLManagerApple::parseAndValidatePeerCertificate( ::SSLContextRef ssl, const std::string& remoteHost) { + // Record TLS version stats + recordTLSVersion(ssl); + /* While we always have a system CA via the Keychain, * we'll pretend not to in terms of validation if the server * was started using a PEM file (legacy mode). diff --git a/src/mongo/util/net/ssl_manager_openssl.cpp b/src/mongo/util/net/ssl_manager_openssl.cpp index 9247cdad32d..6a10bb7d387 100644 --- a/src/mongo/util/net/ssl_manager_openssl.cpp +++ b/src/mongo/util/net/ssl_manager_openssl.cpp @@ -64,6 +64,7 @@ #include <openssl/asn1.h> #include <openssl/asn1t.h> #include <openssl/evp.h> +#include <openssl/ssl.h> #include <openssl/x509_vfy.h> #include <openssl/x509v3.h> #if defined(_WIN32) @@ -1235,8 +1236,37 @@ SSLConnectionInterface* SSLManagerOpenSSL::accept(Socket* socket, return sslConn.release(); } + +void recordTLSVersion(const SSL* conn) { + int protocol = SSL_version(conn); + + auto& counts = mongo::TLSVersionCounts::get(getGlobalServiceContext()); + switch (protocol) { + case TLS1_VERSION: + counts.tls10.addAndFetch(1); + break; + case TLS1_1_VERSION: + counts.tls11.addAndFetch(1); + break; + case TLS1_2_VERSION: + counts.tls12.addAndFetch(1); + break; +#ifdef TLS1_3_VERSION + case TLS1_3_VERSION: + counts.tls13.addAndFetch(1); + break; +#endif + default: + // Do nothing + break; + } +} + StatusWith<boost::optional<SSLPeerInfo>> SSLManagerOpenSSL::parseAndValidatePeerCertificate( SSL* conn, const std::string& remoteHost) { + + recordTLSVersion(conn); + if (!_sslConfiguration.hasCA && isSSLServer) return {boost::none}; diff --git a/src/mongo/util/net/ssl_manager_windows.cpp b/src/mongo/util/net/ssl_manager_windows.cpp index fd159c7c7e3..c5dcc4c865e 100644 --- a/src/mongo/util/net/ssl_manager_windows.cpp +++ b/src/mongo/util/net/ssl_manager_windows.cpp @@ -436,7 +436,7 @@ StatusWith<UniqueCertChainEngine> initChainEngine(CERT_CHAIN_ENGINE_CONFIG* chai << errnoWithDescription(gle)); } - return chainEngine; + return {chainEngine}; } Status SSLManagerWindows::_initChainEngines(bool hasCAFile) { @@ -873,8 +873,7 @@ StatusWith<UniqueCertificateWithPrivateKey> readCertPEMFile(StringData fileName, << errnoWithDescription(gle)); } - return std::move( - UniqueCertificateWithPrivateKey(std::move(certHolder), std::move(cryptProvider))); + return UniqueCertificateWithPrivateKey(std::move(certHolder), std::move(cryptProvider)); } Status readCAPEMFile(HCERTSTORE certStore, StringData fileName) { @@ -1633,9 +1632,48 @@ Status validatePeerCertificate(const std::string& remoteHost, return Status::OK(); } +Status recordTLSVersion(PCtxtHandle ssl) { + SecPkgContext_ConnectionInfo connInfo; + + SECURITY_STATUS ss = QueryContextAttributes(ssl, SECPKG_ATTR_CONNECTION_INFO, &connInfo); + + if (ss != SEC_E_OK) { + return Status(ErrorCodes::SSLHandshakeFailed, + str::stream() << "QueryContextAttributes for connection info failed with" + << ss); + } + + auto& counts = mongo::TLSVersionCounts::get(getGlobalServiceContext()); + switch (connInfo.dwProtocol) { + case SP_PROT_TLS1_CLIENT: + case SP_PROT_TLS1_SERVER: + counts.tls10.addAndFetch(1); + break; + case SP_PROT_TLS1_1_CLIENT: + case SP_PROT_TLS1_1_SERVER: + counts.tls11.addAndFetch(1); + break; + case SP_PROT_TLS1_2_CLIENT: + case SP_PROT_TLS1_2_SERVER: + counts.tls12.addAndFetch(1); + break; + default: + // Do nothing + break; + } + + return Status::OK(); +} + StatusWith<boost::optional<SSLPeerInfo>> SSLManagerWindows::parseAndValidatePeerCertificate( PCtxtHandle ssl, const std::string& remoteHost) { PCCERT_CONTEXT cert; + + auto countStatus = recordTLSVersion(ssl); + if (!countStatus.isOK()) { + return countStatus; + } + if (!_sslConfiguration.hasCA && isSSLServer) return {boost::none}; |