Tully

// BLOG

Building MongoDB with SSL

-

mongo SSL

Unless your data store is running on the same host as your application, you really should be concerned about the exposure sensitive communications (such as those containing user data) are subject to as they traverse any network—including the local networks of your hosting service.

Fortunately, MongoDB supports native SSL communication between itself and connecting clients. Unfortunately, the community versions of MongoDB are not built with such support, so one must either pay for the MongoDB Enterprise service or build MongoDB themselves.

There are, of course, less involved alternatives to building MongoDB yourself, the simplest of which would most likely be to tunnel communications with an SSH client as this often requires no more than a line of setup, e.g.:

ssh -L 1337:localhost:27017 user@host

Perhaps a better alternative would be to use something like SSH2 or stunnel; however, the consideration of alternatives is not the aim of this post and is left to the reader.

This guide will proceed in the context of a sudoer on a 64-bit Ubuntu 14.04 host.

Dependencies

  • git
  • SCons
  • build-essential
  • libssl-dev
  • boost libraries
    • libboost-filesystem-dev
    • libboost-program-options-dev
    • libboost-system-dev
    • libboost-thread-dev

This will install all the dependencies in one go:

sudo apt-get install git \
                     scons \
                     build-essential \
                     libssl-dev \
                     libboost-filesystem-dev \
                     libboost-program-options-dev \
                     libboost-system-dev \
                     libboost-thread-dev \
                     -y

Due to the recency of Ubuntu 14.04, most of the default APT packages satisfy the version requirements for new MongoDB builds already; however, this will most likely not be the case for older releases. If you are running 12.04, e.g., you may need to utilise various PPAs (and other means) such as the Ubuntu Toolchain PPA for installing gcc >= v4.8.1 (required by MongoDB >= 2.8) to get the required versions of dependencies.

The build process requires a good amount of memory and will consume a substantial chunk of disk space. To give you an idea, I had issues building all targets with much less than 2GB of memory and was left with a 25GB mongo/ directory upon successfully building all targets. Don't worry too much, however, once the build process is complete, you can simply install the binaries and delete this large directory.

Preparation

The working directory for the build can be arbitrarily located as you will most likely discard it afterwards anyway. One exception to be aware of is an issue that may occur if your installation target is a direct parent of the build/source location. With this in mind, I build in ~/mongo and install to /src.

Begin by cloning the mongo repository:

git clone https://github.com/mongodb/mongo.git 
cd mongo

Next you will need to checkout the version of MongoDB that you plan on building. Each release is tagged in the repository, so a list can be produced via:

git tag -l | grep -v rc

Typically, you will just want to build the latest production release who's version number you can get by checking: mongodb.org/downloads. At the time of writing this article (1/18/2015), the latest version is 2.6.7, so I will run:

git checkout r2.6.7

Building

Once you've checked out the appropriate version, it's time to use SCons to build the targets. You can build specific components who's ids are listed on the MongoDB build page or you can just build everything with the all argument. Since you will most likely just delete all the excess aftwards regardless, all is often the easiest route.

The following command will build all targets for a 64-bit system and will include SSL support. The j flag will allow multiple targets to be built in parallel which should result in total real time for the build being significantly reduced.

scons -j 4 --64 --ssl all

This should run for around 10-20 minutes depending on your environment and will hopefully result in something along the lines of: scons: done building targets. (i.e. not scons: building terminated because of errors).

A common error is that of scons depleting memory during the build. If you are building on a low available memory machine and the exit status doesn't point you in any clear direction, it may serve you to add more memory or create a generous swap partition before running the build again.

The next step is to install the built targets on your system. The --prefix flag allows you to specify a parent directory for the binaries. Here we install them using a /usr prefix meaning, e.g, the mongod command will be installed to /usr/bin/mongod (this is recommended as it means you wont need to alter the configuration scripts later).

sudo scons -j 4 --64 --ssl --prefix=/usr install

You should see a slew of chmod 755 /usr/bin/... lines as output and, again, hopefully a scons: done building targets. at the end with no errors. Running:

ls /usr/bin | grep mongo

should result in a list similar to this:

mongo
mongod
mongodump
mongoexport
mongofiles
mongoimport
mongooplog
mongoperf
mongorestore
mongos
mongostat
mongotop

Configuration

MongoDB operates most effectively (and securely) under a particular environmental setup. The default environment created for a new user on most Linux distributions is not conducive to optimal performance due to various security mechanisms including limitations on things such file sizes and open file limits, etc.

Similarly, there are security implications involved in running your MongoDB instance with either a standard user account or as root. As such, and before running your MongoDB server in any kind of production environment, you should ensure that it is set up to run in an appropriate environment.

The mongo repository includes some configuration scripts that effect most of the recommended settings for a production MongoDB environment on a Debian machine; these are located in the debian/ directory of the mongo repository.

The mongodb-org-server.postinst script will setup the appropriate user and group for mongod to run under. You can run it with:

cd debian
sudo sh mongodb-org-server.postinst configure

To set the recommended user limit values and create a mongod service that will start the daemon correctly, install the mongod.upstart script:

sudo cp ./debian/mongod.upstart /etc/init/mongod.conf

Finally, you will need to create a configuration file for the daemon. A default mongod.conf can be found in the debian/ directory aswell which you can install with:

sudo cp ./debian/mongod.conf /etc/mongod.conf

You can now delete the large ~/mongo directory if you wish.

Enabling SSL

First, you will need to create a .pem file containing both a certificate and its corresponding private key. These can be generated with the following command:

openssl req -newkey rsa:2048 -new -x509 -days 1024 -nodes -out mongodb.crt -keyout mongodb.key

Now combine the two output files into /etc/ssl/mongodb.pem:

sudo sh -c 'cat mongodb.crt mongodb.key > /etc/ssl/mongodb.pem'

Lastly, you need to enable SSL and give mongod the path to the newly created mongodb.pem. Do this by adding the following lines to your /etc/mongod.conf file:

sslMode=requireSSL
sslPEMKeyFile=/etc/ssl/mongodb.pem

This configuration will allow any clients using SSL to establish a connection while rejecting everything else. By default, a client will not need to present a certificate; it is assumed that your connecting clients are being vetted by some other means, e.g., with iptables. If you are looking to manage a more diverse cluster of nodes connecting to your data store, you may want to look into setting up a certificate validation system with the sslCAFile option.

Start the daemon and you should have a fully operational, SSL enabled, MongoDB server:

sudo service mongod start

Testing SSL

You can run a quick check to make sure that mongod is operating in SSL mode by attempting to spawn a mongo shell without the --ssl flag. Running mongo should throw a connect failed exception and yield no shell.

However, mongo --ssl should successfully spawn a MongoDB shell and allow you to start running commands.

If you're somewhat paranoid and need further convincing that your data is indeed being encrypted, it's always an idea to look at the actual packets themselves. Comparing the data packets of the SSL enabled connection with that of the disabled one should put it beyond doubt.

So, lets comment out the two lines we added to /etc/mongod.conf:

#sslMode=requireSSL
#sslPEMKeyFile=/etc/ssl/mongodb.pem

and restart the daemon to put the new settings into effect with:

sudo service mongod restart

Your daemon should now be running without SSL, i.e., you can spawn shells with plain mongo (no --ssl flag). Now run:

sudo tcpdump -X -i eth0:0 'port 27017'

where eth0:0 is the interface that your host (database server) is using to connect to the network. This will print all the packets (headers and data) that traverse port 27017. Have your client on the remote host (application server) connect and insert some super secret sensitive data into your database, e.g.:

db.test.insert({test: 'TACO TACO TACO TACO TACO TACO TACO TACO LOOMINATI'});

You should be able to see the plain text TACOs in the ASCII rendering on the far right of the output:

tcpdump

Now, re-enable SSL by uncommenting the two lines and restart the mongo daemon. Make sure that your client both supports SSL and has it enabled, e.g., the native Node.js client accepts a ssl: Boolen field in its server options object. Alternatively, you can simply pass the ?ssl=true parameter as part of your connection URL in most cases. Run the tcpdump command again and insert some sweet TACOs (because they are delicious). The output should now be garbled with no plain text TACOs or illuminati to be found.

All Done

Hopefully, you didn't run into too many problems and now have a SSL enabled MongoDB server up and running. If you did run into problems during the build, other than your traditional help channels, you may find some of the MongoDB communities of some use.

For issues connecting to the database, it's often useful to inspect the mongod log file that your upstart script created for you with:

sudo tail -n 20 /var/log/mongodb/mongod.log

Comments

comments powered by Disqus