From 5cd7e9a0ca88583ad94243d00032486c0ee9052c Mon Sep 17 00:00:00 2001 From: John Chen Date: Tue, 3 Dec 2019 15:40:06 +0000 Subject: SERVER-37135: Track and report TLS 1.3 (cherry picked from commit cbb76539c47068f8836ed05283763e687cf126a7) (cherry picked from commit 8c1de7e08de30a38f3d878118248735e6e2ea72a) --- jstests/ssl/libs/ssl_helpers.js | 27 +++++++++++++++++++++ jstests/ssl/ssl_count_protocols.js | 39 ++++++++++++++++++++++++------- src/mongo/util/net/ssl_manager.cpp | 16 ++++++++++++- src/mongo/util/net/ssl_manager.h | 3 +++ src/mongo/util/net/ssl_manager_status.cpp | 2 ++ src/mongo/util/net/ssl_options.cpp | 3 +++ src/mongo/util/net/ssl_options.h | 22 +++++++++-------- 7 files changed, 93 insertions(+), 19 deletions(-) diff --git a/jstests/ssl/libs/ssl_helpers.js b/jstests/ssl/libs/ssl_helpers.js index 50463d8dec9..16657a15b64 100644 --- a/jstests/ssl/libs/ssl_helpers.js +++ b/jstests/ssl/libs/ssl_helpers.js @@ -156,3 +156,30 @@ function mixedShardTest(options1, options2, shouldSucceed) { } } } + +function detectDefaultTLSProtocol() { + const conn = MongoRunner.runMongod({ + sslMode: 'allowSSL', + sslPEMKeyFile: SERVER_CERT, + sslDisabledProtocols: 'none', + useLogFiles: true, + tlsLogVersions: "TLS1_0,TLS1_1,TLS1_2,TLS1_3", + }); + + const res = conn.getDB("admin").serverStatus().transportSecurity; + + MongoRunner.stopMongod(conn); + + // Verify that the default protocol is either TLS1.2 or TLS1.3. + // No supported platform should default to an older protocol version. + assert.eq(0, res["1.0"]); + assert.eq(0, res["1.1"]); + assert.eq(0, res["unknown"]); + assert.neq(res["1.2"], res["1.3"]); + + if (res["1.2"].tojson() != NumberLong(0).tojson()) { + return "TLS1_2"; + } else { + return "TLS1_3"; + } +} diff --git a/jstests/ssl/ssl_count_protocols.js b/jstests/ssl/ssl_count_protocols.js index 6ecc01bb1b6..dcde1b4b69d 100644 --- a/jstests/ssl/ssl_count_protocols.js +++ b/jstests/ssl/ssl_count_protocols.js @@ -2,28 +2,35 @@ (function() { 'use strict'; + load("jstests/ssl/libs/ssl_helpers.js"); + var SERVER_CERT = "jstests/libs/server.pem"; var CLIENT_CERT = "jstests/libs/client.pem"; var CA_CERT = "jstests/libs/ca.pem"; + const protocols = ["TLS1_0", "TLS1_1", "TLS1_2", "TLS1_3"]; + + // First, figure out what protocol our local TLS stack wants to speak. + // We're going to observe a connection of this type from the testrunner. + const expectedDefaultProtocol = detectDefaultTLSProtocol(); + print("Expected default protocol: " + expectedDefaultProtocol); + function runTestWithoutSubset(client) { - let disabledProtocols = ["TLS1_0", "TLS1_1", "TLS1_2"]; - let expectedCounts = [0, 0, 0]; - let clientIndex = 2; - if (getBuildInfo().buildEnvironment.target_os === "osx") { - clientIndex = 0; - } - expectedCounts[clientIndex] = 1; + print("Running test: " + client); + let disabledProtocols = protocols.slice(); + let expectedCounts = [0, 0, 0, 0, 0]; + expectedCounts[protocols.indexOf(expectedDefaultProtocol)] = 1; var index = disabledProtocols.indexOf(client); disabledProtocols.splice(index, 1); expectedCounts[index] += 1; + print(tojson(expectedCounts)); const conn = MongoRunner.runMongod({ sslMode: 'allowSSL', sslPEMKeyFile: SERVER_CERT, sslDisabledProtocols: 'none', useLogFiles: true, - tlsLogVersions: "TLS1_0,TLS1_1,TLS1_2", + tlsLogVersions: "TLS1_0,TLS1_1,TLS1_2,TLS1_3", }); print(disabledProtocols); @@ -48,8 +55,21 @@ 'a[one] = NumberLong(' + expectedCounts[0] + ');' + 'a["1.1"] = NumberLong(' + expectedCounts[1] + ');' + 'a["1.2"] = NumberLong(' + expectedCounts[2] + ');' + + 'a["1.3"] = NumberLong(' + expectedCounts[3] + ');' + + 'a["unknown"] = NumberLong(' + expectedCounts[4] + ');' + 'assert.eq(db.serverStatus().transportSecurity, a);'); + if (expectedDefaultProtocol === "TLS1_2" && client === "TLS1_3") { + // If the runtime environment does not support TLS 1.3, a client cannot connect to a + // server if TLS 1.3 is its only usable protocol version. + assert.neq( + 0, + exitStatus, + "A client which does not support TLS 1.3 should not be able to connect with it"); + MongoRunner.stopMongod(conn); + return; + } + assert.eq(0, exitStatus, ""); print(`Checking ${conn.fullOptions.logFile} for TLS version message`); @@ -76,6 +96,9 @@ } runTestWithoutSubset("TLS1_0"); + runTestWithoutSubset("TLS1_1"); + runTestWithoutSubset("TLS1_2"); + runTestWithoutSubset("TLS1_3"); // OpenSSL 0.9.8 on macOS only supports TLS 1.0 if (getBuildInfo().buildEnvironment.target_os !== "osx") { diff --git a/src/mongo/util/net/ssl_manager.cpp b/src/mongo/util/net/ssl_manager.cpp index f4e0292ee31..4946e68d66c 100644 --- a/src/mongo/util/net/ssl_manager.cpp +++ b/src/mongo/util/net/ssl_manager.cpp @@ -142,6 +142,9 @@ public: #ifndef SSL_OP_NO_TLSv1_2 #define SSL_OP_NO_TLSv1_2 0 #endif +#ifndef SSL_OP_NO_TLSv1_3 +#define SSL_OP_NO_TLSv1_3 0 +#endif namespace { @@ -854,6 +857,8 @@ Status SSLManager::initSSLContext(SSL_CTX* context, supportedProtocols |= SSL_OP_NO_TLSv1_1; } else if (protocol == SSLParams::Protocols::TLS1_2) { supportedProtocols |= SSL_OP_NO_TLSv1_2; + } else if (protocol == SSLParams::Protocols::TLS1_3) { + supportedProtocols |= SSL_OP_NO_TLSv1_3; } } } @@ -1568,9 +1573,18 @@ void recordTLSVersion(TLSVersion version, const HostAndPort& hostForLogging) { versionString = "1.2"_sd; } break; + case TLSVersion::kTLS13: + counts.tls13.addAndFetch(1); + if (std::find(sslGlobalParams.tlsLogVersions.cbegin(), + sslGlobalParams.tlsLogVersions.cend(), + SSLParams::Protocols::TLS1_3) != sslGlobalParams.tlsLogVersions.cend()) { + versionString = "1.3"_sd; + } + break; default: + counts.tlsUnknown.addAndFetch(1); if (!sslGlobalParams.tlsLogVersions.empty()) { - versionString = "unkown"_sd; + versionString = "unknown"_sd; } break; } diff --git a/src/mongo/util/net/ssl_manager.h b/src/mongo/util/net/ssl_manager.h index 74fe0686444..c9ec4ccd89b 100644 --- a/src/mongo/util/net/ssl_manager.h +++ b/src/mongo/util/net/ssl_manager.h @@ -105,9 +105,11 @@ const ASN1OID mongodbRolesOID("1.3.6.1.4.1.34601.2.1.1", * Counts of negogtiated version used by TLS connections. */ struct TLSVersionCounts { + AtomicInt64 tlsUnknown; AtomicInt64 tls10; AtomicInt64 tls11; AtomicInt64 tls12; + AtomicInt64 tls13; static TLSVersionCounts& get(); }; @@ -222,6 +224,7 @@ enum class TLSVersion { kTLS10, kTLS11, kTLS12, + kTLS13, }; /** diff --git a/src/mongo/util/net/ssl_manager_status.cpp b/src/mongo/util/net/ssl_manager_status.cpp index 559d06d4dd2..cd35e78720c 100644 --- a/src/mongo/util/net/ssl_manager_status.cpp +++ b/src/mongo/util/net/ssl_manager_status.cpp @@ -60,6 +60,8 @@ public: builder.append("1.0", counts.tls10.load()); builder.append("1.1", counts.tls11.load()); builder.append("1.2", counts.tls12.load()); + builder.append("1.3", counts.tls13.load()); + builder.append("unknown", counts.tlsUnknown.load()); return builder.obj(); } } tlsVersionStatus; diff --git a/src/mongo/util/net/ssl_options.cpp b/src/mongo/util/net/ssl_options.cpp index 659ab48b5d8..61e7c6e4518 100644 --- a/src/mongo/util/net/ssl_options.cpp +++ b/src/mongo/util/net/ssl_options.cpp @@ -148,6 +148,7 @@ Status storeTLSLogVersion(const std::string& loggedProtocols) { {"TLS1_0", SSLParams::Protocols::TLS1_0}, {"TLS1_1", SSLParams::Protocols::TLS1_1}, {"TLS1_2", SSLParams::Protocols::TLS1_2}, + {"TLS1_3", SSLParams::Protocols::TLS1_3}, }; // Map the tokens to their enum values, and push them onto the list of logged protocols. @@ -295,6 +296,7 @@ Status storeDisabledProtocols(const std::string& disabledProtocols, {"TLS1_0", SSLParams::Protocols::TLS1_0}, {"TLS1_1", SSLParams::Protocols::TLS1_1}, {"TLS1_2", SSLParams::Protocols::TLS1_2}, + {"TLS1_3", SSLParams::Protocols::TLS1_3}, }; // These noTLS* tokens exist for backwards compatibility. @@ -302,6 +304,7 @@ Status storeDisabledProtocols(const std::string& disabledProtocols, {"noTLS1_0", SSLParams::Protocols::TLS1_0}, {"noTLS1_1", SSLParams::Protocols::TLS1_1}, {"noTLS1_2", SSLParams::Protocols::TLS1_2}, + {"noTLS1_3", SSLParams::Protocols::TLS1_3}, }; // Map the tokens to their enum values, and push them onto the list of disabled protocols. diff --git a/src/mongo/util/net/ssl_options.h b/src/mongo/util/net/ssl_options.h index 5d912d1ecdb..b41fdb4d4fe 100644 --- a/src/mongo/util/net/ssl_options.h +++ b/src/mongo/util/net/ssl_options.h @@ -43,16 +43,18 @@ class Environment; namespace moe = mongo::optionenvironment; struct SSLParams { - enum class Protocols { TLS1_0, TLS1_1, TLS1_2 }; - AtomicInt32 sslMode; // --sslMode - the SSL operation mode, see enum SSLModes - std::string sslPEMKeyFile; // --sslPEMKeyFile - std::string sslPEMKeyPassword; // --sslPEMKeyPassword - std::string sslClusterFile; // --sslInternalKeyFile - std::string sslClusterPassword; // --sslInternalKeyPassword - std::string sslCAFile; // --sslCAFile - std::string sslClusterCAFile; // --sslClusterCAFile - std::string sslCRLFile; // --sslCRLFile - std::string sslCipherConfig; // --sslCipherConfig + enum class Protocols { TLS1_0, TLS1_1, TLS1_2, TLS1_3 }; + AtomicInt32 sslMode; // --sslMode - the SSL operation mode, see enum SSLModes + std::string sslPEMKeyFile; // --sslPEMKeyFile + std::string sslPEMKeyPassword; // --sslPEMKeyPassword + std::string sslPEMTempDHParam; // --setParameter OpenSSLDiffieHellmanParameters=file : PEM file + // with DH parameters. + std::string sslClusterFile; // --sslInternalKeyFile + std::string sslClusterPassword; // --sslInternalKeyPassword + std::string sslCAFile; // --sslCAFile + std::string sslClusterCAFile; // --sslClusterCAFile + std::string sslCRLFile; // --sslCRLFile + std::string sslCipherConfig; // --sslCipherConfig std::vector sslDisabledProtocols; // --sslDisabledProtocols std::vector tlsLogVersions; // --tlsLogVersion bool sslWeakCertificateValidation = false; // --sslWeakCertificateValidation -- cgit v1.2.1