summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/core/views/views_all_commands.js3
-rw-r--r--jstests/ssl/sni_name_advertisement.js76
-rw-r--r--src/mongo/db/commands/SConscript1
-rw-r--r--src/mongo/db/commands/whats_my_sni_command.cpp82
-rw-r--r--src/mongo/dbtests/querytests.cpp11
-rw-r--r--src/mongo/embedded/mongo_embedded/mongo_embedded_test.cpp123
-rw-r--r--src/mongo/shell/shardingtest.js4
-rw-r--r--src/mongo/util/net/ssl/detail/impl/engine_apple.ipp13
-rw-r--r--src/mongo/util/net/ssl/detail/impl/engine_openssl.ipp13
-rw-r--r--src/mongo/util/net/ssl_manager_apple.cpp6
-rw-r--r--src/mongo/util/net/ssl_manager_openssl.cpp15
11 files changed, 274 insertions, 73 deletions
diff --git a/jstests/core/views/views_all_commands.js b/jstests/core/views/views_all_commands.js
index f1d07b67d79..ae1c882cac2 100644
--- a/jstests/core/views/views_all_commands.js
+++ b/jstests/core/views/views_all_commands.js
@@ -526,7 +526,8 @@ let viewsCommandTests = {
voteCommitIndexBuild: {skip: isUnrelated},
voteCommitTransaction: {skip: isUnrelated},
voteAbortTransaction: {skip: isUnrelated},
- whatsmyuri: {skip: isUnrelated}
+ whatsmyuri: {skip: isUnrelated},
+ whatsmysni: {skip: isUnrelated}
};
/**
diff --git a/jstests/ssl/sni_name_advertisement.js b/jstests/ssl/sni_name_advertisement.js
new file mode 100644
index 00000000000..9e8f012c1cf
--- /dev/null
+++ b/jstests/ssl/sni_name_advertisement.js
@@ -0,0 +1,76 @@
+/*
+ * Tests that SNI names are advertised if and only if they are a URL, and NOT an IP address.
+ */
+
+(function() {
+'use strict';
+
+let path = "jstests/libs/";
+let pemKeyFile = path + "server.pem";
+let caFile = path + "ca.pem";
+let testURL = "local.10gen.cc";
+let testIP = "127.0.0.1";
+
+let params = {
+ tlsCertificateKeyFile: pemKeyFile,
+ tlsCAFile: caFile,
+ tlsMode: "preferTLS",
+ bind_ip: testURL,
+ tlsAllowInvalidHostnames: ""
+};
+
+/* we will have two test server configurations: one that is bound to a URL, and one that is bound to
+ * an IP address
+ * The bind_ip here is only to confirm that mongod and the shell are on the same page. bind_ip is
+ * not what is used for testing SNI advertisement. That is the IP address supplied to the shell. */
+let ipParams = Object.merge(params, {bind_ip: testIP});
+let urlParams = params;
+
+// returns the result of command "whatsmysni" from a regular mongod
+function getSNI(params) {
+ let mongod = MongoRunner.runMongod(params);
+ let m = new Mongo(params.bind_ip + ":" + mongod.port);
+ let db = m.getDB("admin");
+
+ const sni = assert.commandWorked(db.runCommand({whatsmysni: 1}))["sni"];
+ MongoRunner.stopMongod(mongod);
+
+ return sni;
+}
+
+// returns the result of command "whatsmysni" performed between nodes of a sharded cluster
+function getSNISharded(params) {
+ let s = new ShardingTest({
+ name: "shard",
+ shards: 2,
+ useHostname: true,
+ host: params.bind_ip,
+ other: {configOptions: params, mongosOptions: params, shardOptions: params}
+ });
+ let db = s.getDB("admin");
+
+ // sort of have to fish out the value from deep within the output of multicast...
+ const multicastData =
+ assert.commandWorked(db.runCommand({multicast: {whatsmysni: 1}}))["hosts"];
+ const hostName = Object.keys(multicastData)[0];
+ const sni = multicastData[hostName]["data"]["sni"];
+
+ s.stop();
+
+ return sni;
+}
+
+// TODO SERVER-41045 remove if-statement once SNI is supported on Windows
+if (!_isWindows()) {
+ jsTestLog("Testing mongod bound to URL " + testURL);
+ assert.eq(testURL, getSNI(urlParams), "URL host is not advertised as SNI name in basic mongod");
+ assert.eq(testURL,
+ getSNISharded(urlParams),
+ "URL host is not advertised as SNI name in sharded mongod");
+
+ jsTestLog("Testing mongod bound to IP " + testIP);
+ assert.eq(false, getSNI(ipParams), "IP host is advertised as SNI name in basic mongod");
+ assert.eq(
+ false, getSNISharded(ipParams), "IP host is advertised as SNI name in sharded mongod");
+}
+})(); \ No newline at end of file
diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript
index 7e0ed44fa10..61c5901ac47 100644
--- a/src/mongo/db/commands/SConscript
+++ b/src/mongo/db/commands/SConscript
@@ -271,6 +271,7 @@ env.Library(
"run_aggregate.cpp",
"sleep_command.cpp",
"validate.cpp",
+ "whats_my_sni_command.cpp",
"write_commands/write_commands.cpp",
env.Idlc('create.idl')[0],
env.Idlc('enable_coordinator_for_create_indexes_command.idl')[0],
diff --git a/src/mongo/db/commands/whats_my_sni_command.cpp b/src/mongo/db/commands/whats_my_sni_command.cpp
new file mode 100644
index 00000000000..0b758235c8b
--- /dev/null
+++ b/src/mongo/db/commands/whats_my_sni_command.cpp
@@ -0,0 +1,82 @@
+/**
+ * Copyright (C) 2019-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * 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
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * 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 Server Side 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::kCommand
+
+#include "mongo/db/commands.h"
+#include "mongo/util/log.h"
+
+namespace mongo {
+
+/* for diagnostic / testing purposes. Enabled via command line. */
+class CmdWhatsMySNI : public BasicCommand {
+public:
+ CmdWhatsMySNI() : BasicCommand("whatsmysni") {}
+
+ std::string help() const override {
+ return "internal testing command. Returns an object containing the connection's "
+ "advertised SNI name, if any, and false if none is being advertised";
+ }
+
+ bool run(OperationContext* opCtx,
+ const std::string& ns,
+ const BSONObj& cmdObj,
+ BSONObjBuilder& result) override {
+ auto sniName = opCtx->getClient()->getSniNameForSession();
+ // if no SNI name is advertised, output is false
+ if (!sniName) {
+ result.append("sni", false);
+ } else {
+ result.append("sni", *sniName);
+ }
+
+ result.append("ok", 1);
+
+ return true;
+ }
+
+ bool supportsWriteConcern(const BSONObj& cmd) const override {
+ return false;
+ }
+
+ bool adminOnly() const override {
+ return true;
+ }
+
+ AllowedOnSecondary secondaryAllowed(ServiceContext*) const override {
+ return AllowedOnSecondary::kAlways;
+ }
+
+ void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) const override {}
+};
+
+MONGO_REGISTER_TEST_COMMAND(CmdWhatsMySNI)
+} // namespace mongo
diff --git a/src/mongo/dbtests/querytests.cpp b/src/mongo/dbtests/querytests.cpp
index 044448eaabc..9792bfcb7b4 100644
--- a/src/mongo/dbtests/querytests.cpp
+++ b/src/mongo/dbtests/querytests.cpp
@@ -1717,6 +1717,16 @@ public:
}
};
+class WhatsMySni : public CollectionBase {
+public:
+ WhatsMySni() : CollectionBase("whatsmysni") {}
+ void run() {
+ BSONObj result;
+ _client.runCommand("admin", BSON("whatsmysni" << 1), result);
+ ASSERT_EQUALS("", result["sni"].str());
+ }
+};
+
class QueryByUuid : public CollectionBase {
public:
QueryByUuid() : CollectionBase("QueryByUuid") {}
@@ -1912,6 +1922,7 @@ public:
add<QueryReadsAll>();
add<queryobjecttests::names1>();
add<OrderingTest>();
+ add<WhatsMySni>();
}
};
diff --git a/src/mongo/embedded/mongo_embedded/mongo_embedded_test.cpp b/src/mongo/embedded/mongo_embedded/mongo_embedded_test.cpp
index a46909dc903..9bfab7ef5dc 100644
--- a/src/mongo/embedded/mongo_embedded/mongo_embedded_test.cpp
+++ b/src/mongo/embedded/mongo_embedded/mongo_embedded_test.cpp
@@ -562,67 +562,68 @@ TEST_F(MongodbCAPITest, InsertAndUpdate) {
TEST_F(MongodbCAPITest, RunListCommands) {
auto client = createClient();
- std::vector<std::string> whitelist = {
- "_hashBSONElement",
- "aggregate",
- "buildInfo",
- "collMod",
- "collStats",
- "configureFailPoint",
- "count",
- "create",
- "createIndexes",
- "currentOp",
- "dataSize",
- "dbStats",
- "delete",
- "distinct",
- "drop",
- "dropDatabase",
- "dropIndexes",
- "echo",
- "endSessions",
- "explain",
- "find",
- "findAndModify",
- "getLastError",
- "getMore",
- "getParameter",
- "httpClientRequest",
- "insert",
- "isMaster",
- "killCursors",
- "killOp",
- "killSessions",
- "killAllSessions",
- "killAllSessionsByPattern",
- "listCollections",
- "listCommands",
- "listDatabases",
- "listIndexes",
- "lockInfo",
- "ping",
- "planCacheClear",
- "planCacheClearFilters",
- "planCacheListFilters",
- "planCacheListPlans",
- "planCacheListQueryShapes",
- "planCacheSetFilter",
- "reIndex",
- "refreshLogicalSessionCacheNow",
- "refreshSessions",
- "renameCollection",
- "repairDatabase",
- "resetError",
- "serverStatus",
- "setBatteryLevel",
- "setParameter",
- "sleep",
- "startSession",
- "trimMemory",
- "update",
- "validate",
- };
+ std::vector<std::string> whitelist = {"_hashBSONElement",
+ "aggregate",
+ "buildInfo",
+ "collMod",
+ "collStats",
+ "configureFailPoint",
+ "count",
+ "create",
+ "createIndexes",
+ "currentOp",
+ "dataSize",
+ "dbStats",
+ "delete",
+ "distinct",
+ "drop",
+ "dropDatabase",
+ "dropIndexes",
+ "echo",
+ "endSessions",
+ "explain",
+ "find",
+ "findAndModify",
+ "getLastError",
+ "getMore",
+ "getParameter",
+ "httpClientRequest",
+ "insert",
+ "isMaster",
+ "killCursors",
+ "killOp",
+ "killSessions",
+ "killAllSessions",
+ "killAllSessionsByPattern",
+ "listCollections",
+ "listCommands",
+ "listDatabases",
+ "listIndexes",
+ "lockInfo",
+ "ping",
+ "planCacheClear",
+ "planCacheClearFilters",
+ "planCacheListFilters",
+ "planCacheListPlans",
+ "planCacheListQueryShapes",
+ "planCacheSetFilter",
+ "reIndex",
+ "refreshLogicalSessionCacheNow",
+ "refreshSessions",
+ "renameCollection",
+ "repairDatabase",
+ "resetError",
+ "serverStatus",
+ "setBatteryLevel",
+ "setParameter",
+ "sleep",
+ "startSession",
+ "trimMemory",
+ "twoPhaseCreateIndexes",
+ "update",
+ "validate",
+ "whatsmysni"};
+
std::sort(whitelist.begin(), whitelist.end());
mongo::BSONObj listCommandsObj = mongo::fromjson("{ listCommands: 1 }");
diff --git a/src/mongo/shell/shardingtest.js b/src/mongo/shell/shardingtest.js
index d31bab4ebee..6a855c4b8b6 100644
--- a/src/mongo/shell/shardingtest.js
+++ b/src/mongo/shell/shardingtest.js
@@ -1133,7 +1133,7 @@ var ShardingTest = function(params) {
}
var keyFile = otherParams.keyFile;
- var hostName = getHostName();
+ var hostName = otherParams.host === undefined ? getHostName() : otherParams.host;
this._testName = testName;
this._otherParams = otherParams;
@@ -1265,6 +1265,7 @@ var ShardingTest = function(params) {
var rs = new ReplSetTest({
name: setName,
nodes: numReplicas,
+ host: hostName,
useHostName: otherParams.useHostname,
useBridge: otherParams.useBridge,
bridgeOptions: otherParams.bridgeOptions,
@@ -1394,6 +1395,7 @@ var ShardingTest = function(params) {
// Using replica set for config servers
var rstOptions = {
useHostName: otherParams.useHostname,
+ host: hostName,
useBridge: otherParams.useBridge,
bridgeOptions: otherParams.bridgeOptions,
keyFile: keyFile,
diff --git a/src/mongo/util/net/ssl/detail/impl/engine_apple.ipp b/src/mongo/util/net/ssl/detail/impl/engine_apple.ipp
index 154f08707aa..031260b8945 100644
--- a/src/mongo/util/net/ssl/detail/impl/engine_apple.ipp
+++ b/src/mongo/util/net/ssl/detail/impl/engine_apple.ipp
@@ -37,6 +37,9 @@
#include "asio/detail/push_options.hpp"
#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
+#include "asio/ip/address.hpp"
+
+#include <arpa/inet.h>
#include "mongo/util/log.h"
#include "mongo/util/net/ssl/apple.hpp"
@@ -186,9 +189,13 @@ bool engine::_initSSL(stream_base::handshake_type type, asio::error_code& ec) {
status = ::SSLSetSessionOption(_ssl.get(), ::kSSLSessionOptionBreakOnClientAuth, true);
}
- if (!_remoteHostName.empty() && (status == ::errSecSuccess)) {
- status =
- ::SSLSetPeerDomainName(_ssl.get(), _remoteHostName.c_str(), _remoteHostName.size());
+ if (!_remoteHostName.empty() && (status == ::errSecSuccess) {
+ error_code ec;
+ ip::make_address(_remoteHostName, ec);
+ if (ec) {
+ status =
+ ::SSLSetPeerDomainName(_ssl.get(), _remoteHostName.c_str(), _remoteHostName.size());
+ }
}
if (status != ::errSecSuccess) {
diff --git a/src/mongo/util/net/ssl/detail/impl/engine_openssl.ipp b/src/mongo/util/net/ssl/detail/impl/engine_openssl.ipp
index fc0ff1128b7..def2a7e7936 100644
--- a/src/mongo/util/net/ssl/detail/impl/engine_openssl.ipp
+++ b/src/mongo/util/net/ssl/detail/impl/engine_openssl.ipp
@@ -16,6 +16,7 @@
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
+#include "asio/ip/address.hpp"
#include "asio/detail/throw_error.hpp"
#include "asio/error.hpp"
@@ -211,9 +212,15 @@ int engine::do_accept(void*, std::size_t) {
int engine::do_connect(void*, std::size_t) {
if (!_remoteHostName.empty()) {
- int ret = ::SSL_set_tlsext_host_name(ssl_, _remoteHostName.c_str());
- if (ret != 1)
- return ret;
+ error_code ec;
+ ip::make_address(_remoteHostName, ec);
+ // only have TLS advertise _remoteHostName as an SNI if it is not an IP address
+ if (ec) {
+ int ret = ::SSL_set_tlsext_host_name(ssl_, _remoteHostName.c_str());
+ if (ret != 1) {
+ return ret;
+ }
+ }
_remoteHostName.clear();
}
diff --git a/src/mongo/util/net/ssl_manager_apple.cpp b/src/mongo/util/net/ssl_manager_apple.cpp
index 2b6a1daa106..463ae07f864 100644
--- a/src/mongo/util/net/ssl_manager_apple.cpp
+++ b/src/mongo/util/net/ssl_manager_apple.cpp
@@ -55,6 +55,8 @@
#include "mongo/util/net/ssl_options.h"
#include "mongo/util/net/ssl_parameters_gen.h"
+#include <arpa/inet.h>
+
using asio::ssl::apple::CFUniquePtr;
/* This API appears in the Security framework even though
@@ -1108,7 +1110,9 @@ public:
uassertOSStatusOK(::SSLSetProtocolVersionMin(_ssl.get(), ctx->protoMin));
uassertOSStatusOK(::SSLSetProtocolVersionMax(_ssl.get(), ctx->protoMax));
- if (!hostname.empty()) {
+ std::array<uint8_t, INET6_ADDRSTRLEN> unusedBuf;
+ if (!hostname.empty() && (inet_pton(AF_INET, hostname.c_str(), unusedBuf.data()) == 0) &&
+ (inet_pton(AF_INET6, hostname.c_str(), unusedBuf.data()) == 0)) {
uassertOSStatusOK(
::SSLSetPeerDomainName(_ssl.get(), hostname.c_str(), hostname.size()));
}
diff --git a/src/mongo/util/net/ssl_manager_openssl.cpp b/src/mongo/util/net/ssl_manager_openssl.cpp
index e6fdd202bb3..2ba1e1a56d1 100644
--- a/src/mongo/util/net/ssl_manager_openssl.cpp
+++ b/src/mongo/util/net/ssl_manager_openssl.cpp
@@ -68,6 +68,7 @@
#ifndef _WIN32
#include <netinet/in.h>
#endif
+#include <arpa/inet.h>
#include <openssl/asn1.h>
#include <openssl/asn1t.h>
#include <openssl/dh.h>
@@ -1441,9 +1442,17 @@ SSLConnectionInterface* SSLManagerOpenSSL::connect(Socket* socket) {
_clientContext.get(), socket, (const char*)nullptr, 0);
const auto undotted = removeFQDNRoot(socket->remoteAddr().hostOrIp());
- int ret = ::SSL_set_tlsext_host_name(sslConn->ssl, undotted.c_str());
- if (ret != 1)
- _handleSSLError(sslConn.get(), ret);
+
+ // only have TLS advertise host name if it is not an IP address
+ int ret;
+ std::array<uint8_t, INET6_ADDRSTRLEN> unusedBuf;
+ if ((inet_pton(AF_INET, undotted.c_str(), unusedBuf.data()) == 0) &&
+ (inet_pton(AF_INET6, undotted.c_str(), unusedBuf.data()) == 0)) {
+ ret = ::SSL_set_tlsext_host_name(sslConn->ssl, undotted.c_str());
+ if (ret != 1) {
+ _handleSSLError(sslConn.get(), ret);
+ }
+ }
do {
ret = ::SSL_connect(sslConn->ssl);