Skip to content

Commit a63de1b

Browse files
committed
NODE-799 Allow passing in servername for TLS connections for SNI support.
1 parent c475758 commit a63de1b

File tree

4 files changed

+230
-3
lines changed

4 files changed

+230
-3
lines changed

lib/mongos.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ var release = os.release();
5353
, 'sslCA', 'sslCert', 'sslKey', 'sslPass', 'socketOptions', 'bufferMaxEntries'
5454
, 'store', 'auto_reconnect', 'autoReconnect', 'emitError'
5555
, 'keepAlive', 'noDelay', 'connectTimeoutMS', 'socketTimeoutMS'
56-
, 'loggerLevel', 'logger', 'reconnectTries', 'appname', 'domainsEnabled'];
56+
, 'loggerLevel', 'logger', 'reconnectTries', 'appname', 'domainsEnabled'
57+
, 'servername'];
5758

5859
/**
5960
* Creates a new Mongos instance
@@ -72,6 +73,7 @@ var release = os.release();
7273
* @param {(Buffer|string)} [options.sslCert=null] String or buffer containing the certificate we wish to present (needs to have a mongod server with ssl support, 2.4 or higher)
7374
* @param {(Buffer|string)} [options.sslKey=null] String or buffer containing the certificate private key we wish to present (needs to have a mongod server with ssl support, 2.4 or higher)
7475
* @param {(Buffer|string)} [options.sslPass=null] String or buffer containing the certificate password (needs to have a mongod server with ssl support, 2.4 or higher)
76+
* @param {string} [options.servername=null] String containing the server name requested via TLS SNI.
7577
* @param {object} [options.socketOptions=null] Socket options
7678
* @param {boolean} [options.socketOptions.noDelay=true] TCP Socket NoDelay option.
7779
* @param {number} [options.socketOptions.keepAlive=0] TCP KeepAlive on the socket with a X ms delay before start.

lib/replset.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ var legalOptionNames = ['ha', 'haInterval', 'replicaSet', 'rs_name', 'secondaryA
4848
, 'sslCA', 'sslCert', 'sslKey', 'sslPass', 'socketOptions', 'bufferMaxEntries'
4949
, 'store', 'auto_reconnect', 'autoReconnect', 'emitError'
5050
, 'keepAlive', 'noDelay', 'connectTimeoutMS', 'socketTimeoutMS', 'strategy', 'debug'
51-
, 'loggerLevel', 'logger', 'reconnectTries', 'appname', 'domainsEnabled'];
51+
, 'loggerLevel', 'logger', 'reconnectTries', 'appname', 'domainsEnabled'
52+
, 'servername'];
5253

5354
// Get package.json variable
5455
var driverVersion = require(__dirname + '/../package.json').version;
@@ -77,6 +78,7 @@ var release = os.release();
7778
* @param {(Buffer|string)} [options.sslCert=null] String or buffer containing the certificate we wish to present (needs to have a mongod server with ssl support, 2.4 or higher)
7879
* @param {(Buffer|string)} [options.sslKey=null] String or buffer containing the certificate private key we wish to present (needs to have a mongod server with ssl support, 2.4 or higher)
7980
* @param {(Buffer|string)} [options.sslPass=null] String or buffer containing the certificate password (needs to have a mongod server with ssl support, 2.4 or higher)
81+
* @param {string} [options.servername=null] String containing the server name requested via TLS SNI.
8082
* @param {object} [options.socketOptions=null] Socket options
8183
* @param {boolean} [options.socketOptions.noDelay=true] TCP Socket NoDelay option.
8284
* @param {number} [options.socketOptions.keepAlive=0] TCP KeepAlive on the socket with a X ms delay before start.

lib/server.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ var release = os.release();
5050
, 'store', 'auto_reconnect', 'autoReconnect', 'emitError'
5151
, 'keepAlive', 'noDelay', 'connectTimeoutMS', 'socketTimeoutMS'
5252
, 'loggerLevel', 'logger', 'reconnectTries', 'reconnectInterval', 'monitoring'
53-
, 'appname', 'domainsEnabled'];
53+
, 'appname', 'domainsEnabled'
54+
, 'servername'];
5455

5556
/**
5657
* Creates a new Server instance
@@ -67,6 +68,7 @@ var release = os.release();
6768
* @param {(Buffer|string)} [options.sslCert=null] String or buffer containing the certificate we wish to present (needs to have a mongod server with ssl support, 2.4 or higher)
6869
* @param {(Buffer|string)} [options.sslKey=null] String or buffer containing the certificate private key we wish to present (needs to have a mongod server with ssl support, 2.4 or higher)
6970
* @param {(Buffer|string)} [options.sslPass=null] String or buffer containing the certificate password (needs to have a mongod server with ssl support, 2.4 or higher)
71+
* @param {string} [options.servername=null] String containing the server name requested via TLS SNI.
7072
* @param {object} [options.socketOptions=null] Socket options
7173
* @param {boolean} [options.socketOptions.autoReconnect=true] Reconnect on error.
7274
* @param {boolean} [options.socketOptions.noDelay=true] TCP Socket NoDelay option.

test/functional/ssl_mongoclient_tests.js

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,61 @@ exports.shouldCorrectlyValidateServerCertificate = {
108108
}
109109
}
110110

111+
/**
112+
* @ignore
113+
*/
114+
exports['Should correctly pass down servername to connection for TLS SNI support'] = {
115+
metadata: { requires: { topology: 'ssl' } },
116+
117+
// The actual test we wish to run
118+
test: function(configuration, test) {
119+
var ServerManager = require('mongodb-topology-manager').Server
120+
, MongoClient = configuration.require.MongoClient;
121+
122+
// All inserted docs
123+
var docs = [];
124+
var errs = [];
125+
var insertDocs = [];
126+
127+
// Read the ca
128+
var ca = [fs.readFileSync(__dirname + "/ssl/ca.pem")];
129+
130+
// Start server
131+
var serverManager = new ServerManager('mongod', {
132+
journal:null
133+
, sslOnNormalPorts: null
134+
, sslPEMKeyFile: __dirname + "/ssl/server.pem"
135+
// EnsureUp options
136+
, dbpath: path.join(path.resolve('db'), f("data-%d", 27019))
137+
, bind_ip: 'server'
138+
, port: 27019
139+
});
140+
141+
serverManager.purge().then(function() {
142+
// Start the server
143+
serverManager.start().then(function() {
144+
setTimeout(function() {
145+
// Connect and validate the server certificate
146+
MongoClient.connect("mongodb://server:27019/test?ssl=true&maxPoolSize=1", {
147+
sslValidate:true
148+
, servername: 'server'
149+
, sslCA:ca
150+
}, function(err, db) {
151+
test.equal(null, err);
152+
test.ok(db != null);
153+
154+
db.close();
155+
156+
serverManager.stop().then(function() {
157+
test.done();
158+
});
159+
});
160+
}, 10000);
161+
});
162+
});
163+
}
164+
}
165+
111166
/**
112167
* @ignore
113168
*/
@@ -595,6 +650,172 @@ exports.shouldCorrectlySendCertificateToReplSetAndValidateServerCertificate = {
595650
}
596651
}
597652

653+
/**
654+
* @ignore
655+
*/
656+
exports['should correctly send SNI TLS servername to replicaset members'] = {
657+
metadata: { requires: { topology: 'ssl' } },
658+
659+
// The actual test we wish to run
660+
test: function(configuration, test) {
661+
var ReplSetManager = require('mongodb-topology-manager').ReplSet
662+
, MongoClient = configuration.require.MongoClient;
663+
// All inserted docs
664+
var docs = [];
665+
var errs = [];
666+
var insertDocs = [];
667+
668+
// Read the ca
669+
var ca = [fs.readFileSync(__dirname + "/ssl/ca.pem")];
670+
var cert = fs.readFileSync(__dirname + "/ssl/client.pem");
671+
var key = fs.readFileSync(__dirname + "/ssl/client.pem");
672+
673+
var replicasetManager = new ReplSetManager('mongod', [{
674+
options: {
675+
bind_ip: 'server', port: 31000,
676+
dbpath: f('%s/../db/31000', __dirname),
677+
sslPEMKeyFile: __dirname + "/ssl/server.pem", sslCAFile: __dirname + "/ssl/ca.pem",
678+
sslCRLFile: __dirname + "/ssl/crl.pem", sslMode: 'requireSSL'
679+
}
680+
}, {
681+
options: {
682+
bind_ip: 'server', port: 31001,
683+
dbpath: f('%s/../db/31001', __dirname),
684+
sslPEMKeyFile: __dirname + "/ssl/server.pem", sslCAFile: __dirname + "/ssl/ca.pem",
685+
sslCRLFile: __dirname + "/ssl/crl.pem", sslMode: 'requireSSL'
686+
}
687+
}, {
688+
options: {
689+
bind_ip: 'server', port: 31002,
690+
dbpath: f('%s/../db/31002', __dirname),
691+
sslPEMKeyFile: __dirname + "/ssl/server.pem", sslCAFile: __dirname + "/ssl/ca.pem",
692+
sslCRLFile: __dirname + "/ssl/crl.pem", sslMode: 'requireSSL'
693+
}
694+
}], {
695+
replSet: 'rs', ssl:true, rejectUnauthorized: false, key: cert, cert: cert, host: 'server'
696+
});
697+
698+
replicasetManager.purge().then(function() {
699+
// Start the server
700+
replicasetManager.start().then(function() {
701+
setTimeout(function() {
702+
// Connect and validate the server certificate
703+
MongoClient.connect("mongodb://server:31000/test?ssl=true&replicaSet=rs&maxPoolSize=1", {
704+
sslValidate:false
705+
, servername: 'server'
706+
, sslCA:ca
707+
, sslKey:key
708+
, sslCert:cert
709+
}, function(err, db) {
710+
if(err) console.dir(err);
711+
test.equal(null, err);
712+
test.ok(db != null);
713+
714+
db.close();
715+
716+
replicasetManager.stop().then(function() {
717+
test.done();
718+
});
719+
});
720+
}, 10000);
721+
}).catch(function(e) {
722+
console.dir(e)
723+
});
724+
});
725+
}
726+
}
727+
728+
/**
729+
* @ignore
730+
*/
731+
exports['should correctly send SNI TLS servername to replicaset members with restart'] = {
732+
metadata: { requires: { topology: 'ssl' } },
733+
734+
// The actual test we wish to run
735+
test: function(configuration, test) {
736+
var ReplSetManager = require('mongodb-topology-manager').ReplSet
737+
, MongoClient = configuration.require.MongoClient;
738+
// All inserted docs
739+
var docs = [];
740+
var errs = [];
741+
var insertDocs = [];
742+
743+
// Read the ca
744+
var ca = [fs.readFileSync(__dirname + "/ssl/ca.pem")];
745+
var cert = fs.readFileSync(__dirname + "/ssl/client.pem");
746+
var key = fs.readFileSync(__dirname + "/ssl/client.pem");
747+
748+
var replicasetManager = new ReplSetManager('mongod', [{
749+
options: {
750+
bind_ip: 'server', port: 31000,
751+
dbpath: f('%s/../db/31000', __dirname),
752+
sslPEMKeyFile: __dirname + "/ssl/server.pem", sslCAFile: __dirname + "/ssl/ca.pem",
753+
sslCRLFile: __dirname + "/ssl/crl.pem", sslMode: 'requireSSL'
754+
}
755+
}, {
756+
options: {
757+
bind_ip: 'server', port: 31001,
758+
dbpath: f('%s/../db/31001', __dirname),
759+
sslPEMKeyFile: __dirname + "/ssl/server.pem", sslCAFile: __dirname + "/ssl/ca.pem",
760+
sslCRLFile: __dirname + "/ssl/crl.pem", sslMode: 'requireSSL'
761+
}
762+
}, {
763+
options: {
764+
bind_ip: 'server', port: 31002,
765+
dbpath: f('%s/../db/31002', __dirname),
766+
sslPEMKeyFile: __dirname + "/ssl/server.pem", sslCAFile: __dirname + "/ssl/ca.pem",
767+
sslCRLFile: __dirname + "/ssl/crl.pem", sslMode: 'requireSSL'
768+
}
769+
}], {
770+
replSet: 'rs', ssl:true, rejectUnauthorized: false, key: cert, cert: cert, host: 'server'
771+
});
772+
773+
replicasetManager.purge().then(function() {
774+
// Start the server
775+
replicasetManager.start().then(function() {
776+
setTimeout(function() {
777+
// Connect and validate the server certificate
778+
MongoClient.connect("mongodb://server:31000/test?ssl=true&replicaSet=rs&maxPoolSize=1", {
779+
sslValidate:false
780+
, servername: 'server'
781+
, sslCA:ca
782+
, sslKey:key
783+
, sslCert:cert
784+
, haInterval: 2000
785+
}, function(err, db) {
786+
if(err) console.dir(err);
787+
test.equal(null, err);
788+
test.ok(db != null);
789+
790+
replicasetManager.primary().then(function(primary) {
791+
primary.stop().then(function() {
792+
// Restart the old master and wait for the sync to happen
793+
primary.start().then(function(result) {
794+
// Wait to allow haInterval to happen
795+
setTimeout(function() {
796+
db.close();
797+
var connections = db.serverConfig.connections();
798+
799+
for(var i = 0; i < connections.length; i++) {
800+
test.equal('server', connections[i].options.servername);
801+
}
802+
803+
replicasetManager.stop().then(function() {
804+
test.done();
805+
});
806+
}, 3000);
807+
});
808+
});
809+
});
810+
});
811+
}, 10000);
812+
}).catch(function(e) {
813+
console.dir(e)
814+
});
815+
});
816+
}
817+
}
818+
598819
/**
599820
* @ignore
600821
*/

0 commit comments

Comments
 (0)