0
votes

We're setting up mongodb on Mongo Cloud Manager which means that they are the dns-hostnames owners. For SSL connection I have created a self-authorized, self-signed certificates for all the servers in the replica set. When providing parse-server with the mongo connection string I am getting this:

error: Uncaught internal server error. { [MongoError: self signed certificate in certificate chain] name: 'MongoError', message: 'self signed certificate in certificate chain' } Error: self signed certificate in certificate chain at Error (native) at TLSSocket. (_tls_wrap.js:1013:38) at emitNone (events.js:67:13) at TLSSocket.emit (events.js:166:7) at TLSSocket._init.ssl.onclienthello.ssl.oncertcb.TLSSocket._finishInit (_tls_wrap.js:582:8) at TLSWrap.TLSSocket._init.ssl.onclienthello.ssl.oncertcb.ssl.onnewsession.ssl.onhandshakedone (_tls_wrap.js:424:38)

I am pretty sure (well, hoping) that if I could provide Parse's mongodb client somehow with my self-generated root certificate that should solve these problems. The question is - if it is possible to provide parse-server with a certificate for mongodb SSL connection, and if so - how?

1

1 Answers

0
votes

Well, I am in the habit of providing my own answers lately, so here's another one. The solution was to obtain/generate the client certificate properly so that you have the client.crt and client.key at hand, have the root certificate and any intermediate certificate and to set the replSet ssl settings. As you can see below I needed to set 'replSet' in 'databaseOptions'. It took some reverse-engineering on my side of parse-server. Note that if parse-server code changes in that area then this solution will stop working.

Anyway the following modified index.js from parse-server-example helped me fix the problem. My additions is around MONGODB_CERTIFICATE env variable.

// Example express application adding the parse-server module to expose Parse
// compatible API routes.

var express = require('express');
var ParseServer = require('parse-server').ParseServer;
var path = require('path');
var fs = require('fs');

var databaseUri = process.env.DATABASE_URI || process.env.MONGODB_URI;

if (!databaseUri) {
  console.log('DATABASE_URI not specified, falling back to localhost.');
}

var parseSettings = {
  databaseURI: databaseUri || 'mongodb://localhost:27017/dev',
  cloud: process.env.CLOUD_CODE_MAIN || __dirname + '/cloud/main.js',
  appId: process.env.APP_ID || 'myAppId',
  masterKey: process.env.MASTER_KEY || '', //Add your master key here. Keep it secret!
  serverURL: process.env.SERVER_URL || 'http://localhost:1337/parse',  // Don't forget to change to https if needed
  liveQuery: {
    classNames: ["Posts", "Comments"] // List of classes to support for query subscriptions
  }
}

// This allows to provide mongo client with certificates for mongodb replica set
// this is handy when you have your own self-authotized/signed certificates in mongo db
if (process.env.MONGODB_CRT_FOLDER) {
  // MONGODB_CRT_FOLDER - certificates folder e.g. /my/certificates
  // if the path is relative to the project just start it without  '/'
  // The folder is must contain 
  //   1. client.key (hard coded name)
  //   2. client.crt (hard coded name)
  //   3. one or more intermediate certificates and a root certificate for the certificate chain
  // MONGODB_CERTIFICATES - the names of the certificates in the certficate chain seperated by comma
  var crtFolder = process.env.MONGODB_CRT_FOLDER + '/';
  if (!process.env.MONGODB_CRT_FOLDER.startsWith('/'))  
    crtFolder = __dirname + '/' + crtFolder;

  var certificatesFiles = process.env.MONGODB_CERTIFICATES.split(',');
  var certificates = [];
  var i;
  for (i in certificatesFiles) {
    certificates.push(fs.readFileSync( crtFolder + '/' + certificatesFiles[i]))
  }

  parseSettings.databaseOptions = {
    replSet: {
      ssl: true,
      sslValidate: true,
      sslCA: certificates,
      sslCert: fs.readFileSync( crtFolder + 'client.crt'),
      sslKey: fs.readFileSync( crtFolder + 'client.key')
    }
  };
}


var api = new ParseServer(parseSettings);
// Client-keys like the javascript key or the .NET key are not necessary with parse-server
// If you wish you require them, you can set them as options in the initialization above:
// javascriptKey, restAPIKey, dotNetKey, clientKey

var app = express();

// Serve static assets from the /public folder
app.use('/public', express.static(path.join(__dirname, '/public')));

// Serve the Parse API on the /parse URL prefix
var mountPath = process.env.PARSE_MOUNT || '/parse';
app.use(mountPath, api);

// Parse Server plays nicely with the rest of your web routes
app.get('/', function(req, res) {
  res.status(200).send('Make sure to star the parse-server repo on GitHub!');
});

// There will be a test page available on the /test path of your server url
// Remove this before launching your app
app.get('/test', function(req, res) {
  res.sendFile(path.join(__dirname, '/public/test.html'));
});

var port = process.env.PORT || 1337;
var httpServer = require('http').createServer(app);
httpServer.listen(port, function() {
    console.log('parse-server-example running on port ' + port + '.');
});

// This will enable the Live Query real-time server
ParseServer.createLiveQueryServer(httpServer);

Note that you will have to npm install --save 'fs' and 'path'.