summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Benvenuto <mark.benvenuto@mongodb.com>2018-07-23 16:40:12 -0400
committerMark Benvenuto <mark.benvenuto@mongodb.com>2018-07-23 16:40:12 -0400
commit0c532a429d4e6f1d8473b6b4f04bf21f6b6f76cb (patch)
tree214cfca89749cd5e236a16a8cc9e028623a08394
parentd9e4d82eb846637063c32fc8e32b337aff208f33 (diff)
downloadmongo-0c532a429d4e6f1d8473b6b4f04bf21f6b6f76cb.tar.gz
SERVER-34558 Add server status for transport security protocol versions
-rw-r--r--jstests/ssl/ssl_count_protocols.js55
-rw-r--r--src/mongo/util/net/SConscript2
-rw-r--r--src/mongo/util/net/ssl_manager.cpp35
-rw-r--r--src/mongo/util/net/ssl_manager.h13
-rw-r--r--src/mongo/util/net/ssl_manager_apple.cpp35
-rw-r--r--src/mongo/util/net/ssl_manager_openssl.cpp30
-rw-r--r--src/mongo/util/net/ssl_manager_windows.cpp44
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};