Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Meteor.js connection to Mongo using X509 certificate auth

I am trying to set up passwordless authentication between Meteor.js apps and Mongo server.

To do that, I need to present pem and crt files to the connection. MONGO_URL connection string takes only parameters about how to perform auth, but no references to files with certs. I assume, I need to pass in the cert file to connection as a parameter. Similar as described in here.

How to do it in Meteor.js?

Basically I want to achieve equivalent of doing:

mongo mongo.example.com/example -ssl -sslPEMKeyFile client.pem --sslCAFile server.crt

and then as described here

db.getSiblingDB("$external").auth(
  {
    mechanism: "MONGODB-X509",
    user: "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry"
  }
)

this works fine when using mogo client, but within Meteor I have so far only gotten to the point of understanding that I would most likely need to use connection string below (or something similar)

MONGO_URL=mongodb://mongo.example.com:27017/example?ssl=true&authSource=$external&authMechanism=MONGODB-X509

but question remains - how to pass certificates to the connection?

Update: there is answer that deals with the issue using native noddejs mongo driver. question is - how to port this to Meteor.

Update 2015-12-31: I have accepted the answer that points to using different connection object when defining the Collection. It is a hassle to do it for each collection separately, but it seems to be the only way this is doable right now. Also, if need arises, probably some MySslCollection can be created, that others can use to inherit connection details. This has not been tested.

like image 913
Martins Untals Avatar asked Dec 20 '15 17:12

Martins Untals


2 Answers

Updated Answer (version 1.4 & up)

As pointed out by another answer, the options here have changed as of version 1.4 and you can now call

Mongo.setConnectionOptions({ ... });

please see the documentation for more details.

Old Answer (pre version 1.4)

This was a fun rabbit hole... I think I have found one solution/workaround to your problem (there may be others).

The quick description of my workaround is to specify the mongodb server options when creating your collections. This must be done on the server code only to avoid handing out your ssl key/cert. Like so:

new Mongo.Collection("collection_name", {
    connection: DDP.connect("mongodb://mongo.example.com:27017/example?ssl=true&authSource=$external&authMechanism=MONGODB-X509", {
        server: {
            sslCert:[CERT_CONTENTS],
            sslKey:[KEY_CONTENTS],
            sslValidate:false
        }
    })
});

Hacky methods

Since you asked if there is a way to do this without adding the options to every collection created I decided to take another look. I have not tested these methods, but they seem sound, if very hacky.

1) Set up a function to run on the connection status using undocumented options argument passthru in the reconnect method. https://github.com/meteor/meteor/blob/master/packages/ddp-client/livedata_connection.js#L996

Tracker.autorun(function () {
  var status = Meteor.status();
  if(status == 'failed')
    Meteor.reconnect({
      server: {
        sslCert:[CERT_CONTENTS],
        sslKey:[KEY_CONTENTS],
        sslValidate:false
      }
    });
});

2) Monkey patch the default connection code (this I am less sure of working since I haven't taken the time to understand when the mongo internal code for this actually gets set in the first place). https://github.com/meteor/meteor/blob/dc3cd6eb92f2bdd1bb44000cdd6abd1e5d0285b1/packages/mongo/remote_collection_driver.js

MongoInternals.defaultRemoteCollectionDriver = _.once(function () {
  var connectionOptions = {
    server: {
      sslCert:[CERT_CONTENTS],
      sslKey:[KEY_CONTENTS],
      sslValidate:false
    }
  };

  var mongoUrl = process.env.MONGO_URL;

  if (process.env.MONGO_OPLOG_URL) {
    connectionOptions.oplogUrl = process.env.MONGO_OPLOG_URL;
  }

  if (! mongoUrl)
    throw new Error("MONGO_URL must be set in environment");

  return new MongoInternals.RemoteCollectionDriver(mongoUrl, connectionOptions);
});

The journey to these answers:

Starting from the answer you linked, I found where Meteor creates its MongoDB connection: https://github.com/meteor/meteor/blob/dc3cd6eb92f2bdd1bb44000cdd6abd1e5d0285b1/packages/mongo/mongo_driver.js#L173

Then I found where that was getting called: https://github.com/meteor/meteor/blob/3d2282d9ad0b570b913a70d215cd968019d912df/packages/mongo/remote_collection_driver.js#L4

Tracking down where that got called led me to the fact that Collections instantiate the connection: https://github.com/meteor/meteor/blob/15cdbca24888bfdff3ad43c1891a1719c09b3dc5/packages/mongo/collection.js#L102

I could see from there that some options can override the use of the default Mongo connnection that has no options specified. Looking in the Meteor docs for collections http://docs.meteor.com/#/full/mongo_collection shows:

Options

connection Object

The server connection that will manage this collection. Uses the default connection if not specified. Pass the return value of calling DDP.connect to specify a different server. Pass null to specify no connection. Unmanaged (name is null) collections cannot specify a connection.

So looking at the docs for DDP.connect http://docs.meteor.com/#/full/ddp_connect we reach a dead end since it only takes a url... but wait... after searching through the code on github I can see that DDP.connect does take an options param: https://github.com/meteor/meteor/blob/master/packages/ddp-client/livedata_connection.js#L1641

Thus concludes our quest.

like image 190
craigts Avatar answered Nov 02 '22 11:11

craigts


Both of the previous answers are correct (craigts, user1887896), however I'd like to add:

  1. I've put a very simple package up on Atmosphere (danwild:set-connection-options) to simplify this process (or at the very least provide a concrete example of how and where Mongo.setConnectionOptions might actually be used, as there is little documentation available).
  2. Just reiterating that if you use any packages which access Mongo in your project (e.g. accounts-base), you must ensure your call to Mongo.setConnectionOptions happens before they are loaded.
    • How to do this? Good question.
    • As far as I can see there is no official way to ensure your package loads before another (the other way around is of course well supported).
    • The simplest way I've seen to do this is, is by editing the package order in .meteor/packages, i.e. placing danwild:set-connection-options at the top of the file.
like image 34
danwild Avatar answered Nov 02 '22 13:11

danwild