MongoDB Security—quick notes

Russ Bateman
24 April 2013
last update:

Users in MongodDB databases
Setting up an administrator in MongoDB
Setting up users for databases
Copying to reduce the tedium
Kerberos authentication
Useful links...
Downloads
Java example (old form)
Java example (new form)
Appendix: 10 Ways to Improve Security

There are two aspects to this question. First, establishing that a MongoDB database has and uses usernames and password. Second, coding from Java (what I do) to connect to a database with such requirements.

Note that this MongoDB feature is not typically used. A good installation would consist of

  1. MongoDB running on a host in a locked room with minimum access by any users except database administrators.
  2. This database node running on a private network unreachable by any except the consuming application.

For this reason, and the relative weakness of the MongoDB username and password solution (easily worked around if you have access to the node itseld), it's usually of little interest to use this feature of MongoDB. I'm treating this subject only because I'm going to have to use it in the course of my work for cookie-cutter decision-makers who find what I've just said here irrelevant.


Users in MongoDB databases

By "database" is meant not MongoDB itself, but any database created in it, but not necessarily every database you create. Therefore, you can set up a user to control access to one database, but not others. Nevertheless, if you set up any authentication requirement for any database, you prohibit access to any and all databases belonging to that MongoDB instance without username and password.

Effectively, you deny access to anyone except for the administrator user you set up if you:

  1. Launch MongoDB (mongod) with option ‐-auth.
  2. Fail to set up users for each database explicitly.

You can set up more than one instance of MongoDB on your host by duplicating mongodb.conf and adding a new dbpath and logpath that are separate from any other, running instance. However, from your application you would have some difficulty connecting to these separate instances. Doing so is beyond the scope of this discussion.


Setting up an administrator in MongoDB

Access to individual MongoDB databases in a host installation is controlled by an administrator, which must be set up. Here are the steps to adding an admininstrator and password to a MongoDB database. This uses the MongoDB shell.

Note that show dbs does not reveal the existence of the admin database in MongoDB. Nevertheless, these steps require "using" that database.

    > use admin
    > db.addUser( { "user":"admin", "pwd":"Test123", "roles":[ "userAdminAnyDatabase"* ] })

* Other roles include the list below. You'll need this list when creating other database users.

read readWrite
dbAdmin userAdmin clusterAdmin
readAnyDatabase readWriteAnyDatabase
dbAdminAnyDatabase userAdminAnyDatabase

This sets up an administrator user, admin, that will have rights to manage users and passwords for other databases.

If you're doing this live in your database, you're now obliged to bounce MongoDB. You'll need to restart with a command-line argument ‐-auth. In order to have access to it, you'll now need to supply the username and password. Here are two ways.

From MongoDB shell command line
    $ mongo -u admin -p Test123
Within the MongoDB shell itself
    > use admin
    > db.auth( "admin", "Test123" )

Setting up users for databases

For each database for which you want users to have access, you must "use" that database and create one or more users, assign passwords and roles, etc. Here, we create a username that's really for an account management application we've written that is the primary consumer of database accountdb. Also, for variety in our example, we create another user, perhaps for a different application or a monitor, that has only read access.

    > use accountdb
    > db.addUser( { "user":"accountapp", "pwd":"Test123", "roles":[ "readWrite" ] } )
    > db.addUser( { "user":"monitor", "pwd":"asdf234#aa", "roles":[ "read" ] } )

You may wish to refer to documentation on User Privilege Roles in MongoDB as linked to in the table above. Listed with read, readWrite, etc. are the specific operations each role supports.


Copying to reduce the tedium

Of course, you've got a pile of databases and only wish to create the users, passwords and roles in one or two places without having to repeat the exercises multiple times.

There is a "copy" operation, in the person of userSource as a keyword (or left-side key). Assume the new user, password and role created for our account management application above.

> use newdb
> db.addUser( { "user":"accountapp", "userSource":"accountdb", "roles":[ "read" ] })
> db.addUser( { "user":"monitor", "userSource":"accountdb", "roles":[ "read" ] })

It's worth noting that, logically, if you copy over a user from another database, you endow that user with only the roles you specify with that copy. Hence, our account management application has read-only privileges over newdb


Kerberos authentication

Not covered here, it should be noted that, since version 2.4, MongoDB supports Kerberos authentication.


Useful links...


Downloads


Java example (old form)

This example uses MongoDB Java driver, version 2.11.x, hence the need to suppress warnings for the deprecated call to Mongo.

"Older" technology (not MongoClient-based) example of MongoDB authentication. The DB.authenticate() method, however, is still valid, so that approach can be mixed with MongoClient in place of using MongoCredential.

Before running this Java example, the following MongoDB shell experience must be conducted and MongoDB bounced to include the ‐‐auth option.

Warning: Unless you've already been running MongoDB in authentication mode, this exercise will make it stop working for whatever else you've been doing with it. To reverse the experiment, simply relaunch MongoDB without the ‐-auth option.

    > use admin
    > db.addUser( { "user":"admin", "pwd":"Test123", "roles":[ "userAdminAnyDatabase" ] } )
    > use test
    > db.addUser( { "user":"jack", "pwd":"Test123", "roles":[ "readWrite" ] } )
OldMongoDB.java:
package com.etretatlogiciels.mongodb;

import java.net.UnknownHostException;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.Mongo;
import com.mongodb.ServerAddress;

public class OldMongoDB
{
    private Mongo mongo;

    @SuppressWarnings( "deprecation" )
    public OldMongoDB()
    {
        try
        {
            mongo = new Mongo( new ServerAddress( "localhost", 27017 ) );
        }
        catch( UnknownHostException e )
        {
            e.printStackTrace();
        }
    }

    public Mongo getMongo() { return this.mongo; }

    public static void main( String[] args )
    {
        OldMongoDB mongodb = new OldMongoDB();

        DB database = mongodb.getMongo().getDB( "test" );

        if( database.authenticate( "test", "Test123".toCharArray() ) )
        {
            DBCollection collection = database.getCollection( "fun" );

            BasicDBObject document = new BasicDBObject();
            document.put( "notice", "This is a document in an authenticated database." );

            collection.insert( document );
        }
    }
}

Java example (new form)

This example uses MongoDB Java driver, version 2.11.x.

"Newer" technology (MongoClient/MongoCredential) example of MongoDB authentication.

Before running this Java example, the following MongoDB shell experience must be conducted and MongoDB bounced to include the ‐‐auth option.

Warning: Unless you've already been running MongoDB in authentication mode, this exercise will make it stop working for whatever else you've been doing with it. To reverse the experiment, simply relaunch MongoDB without the ‐‐auth option.

    > use admin
    > db.addUser( { "user":"admin", "pwd":"Test123", "roles":[ "userAdminAnyDatabase" ] } )
    > use test
    > db.addUser( { "user":"jack", "pwd":"Test123", "roles":[ "readWrite" ] } )
MongoDB.java:
package com.etretatlogiciels.mongodb;

import java.net.UnknownHostException;
import java.util.Arrays;

import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.MongoClient;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;

public class MongoDB
{
    private MongoCredential credential;
    private MongoClient     client;

    public MongoDB()
    {
        try
        {
            credential = MongoCredential.createMongoCRCredential( "jack", "test", "Test123".toCharArray() );
            client     = new MongoClient( new ServerAddress( "localhost", 27017 ), Arrays.asList( credential ) );
        }
        catch( UnknownHostException e )
        {
            e.printStackTrace();
        }
    }

    public MongoClient getClient() { return this.client; }

    public static void main( String[] args )
    {
        MongoDB      mongodb    = new MongoDB();
        DBCollection collection = mongodb.getClient().getDB( "test" ).getCollection( "fun" );

        BasicDBObject document = new BasicDBObject();
        document.put( "notice", "This is a document in an authenticated database." );

        collection.insert( document );
    }
}

Appendix: 10 Ways to Improve Security

  1. Enable authentication: Even if you have deployed your servers in a trusted network, it's good security practice to enable authentication. It provides you "defense in depth" in case your network is compromised. Edit file /etc/mongodb.conf to enable using this setting:
  2. auth = true
    
  3. Don't expose your production database to the Internet: Restricting physical access to your database is an important aspect of security. If it isn't necessary, do not expose your production database to the Internet. In case of compromise, when an attacker cannot physically connect to your MongoDB server, your data is that much more secure. If you are on AWS you can place your databases in a VPC private subnet. Read the blog post Deploying MongoDB in a VPC for more information.
  4.  
  5. Use a firewall: Use firewalls to restrict which other entities are allowed to connect to your server. Best practice is to only allow your application servers access to the database. If you are hosted on AWS use security groups to restrict access. If you are hosted on a provider that does not support firewall constructs, you can easily configure it yourself using iptables Refer to the mongodb documentation to configure iptables for your scenario.
  6.  
  7. Use key files to setup the replica set: Specify a shared key file to enable communication between your MongoDB instances in a replica set. To enable this, add the keyfile parameter to the configuration file as below. The contents of the file need to be the same on all the machines. This requires MongoDB Enterprise, however.
  8. keyFile = /srv/mongodb/keyfile
    
  9. Disable HTTP status interface: By default, MongoDB provides an HTTP interface running on port 28017 which provides the home status page. This interface is not recommended for production use and is best disabled. Use nohttpinterface to disable it.
    nohttpinterface = true
    
  10. Disable the ReST interface: The MonogDB ReST interface is not recommended for production. It does not support any authentication. It is turned off by default. If you have turned it on in the configuration file, you should turn it off for production systems.
    rest = false
    
  11. Configure bind_ip: If your system has multiple network interfaces you can use this option to restrict your server to listen only on the interfaces that are relevant. By default, MongoDB will bind to all interfaces.
    bind_ip = 10.10.0.25,10.10.0.26
    
  12. Enable SSL: If you don't use SSL, your data is traveling between your client and server unencrypted and is susceptible to eavesdropping, tampering and man-in-the-middle attacks. This is especially important if you are connecting to your MongoDB server over unsecure networks like the Internet. This requires MongoDB Enterprise, however.
  13.  
  14. Role-based authorization: MongoDB supports role-based authentication to give you fine grained control over the actions that can be performed by each user. Use role-based constructs to restrict access instead of making all your users administrators. Refer to the roles documentation for more details.
  15.  
  16. Enterprise MongoDB and Kerberos: Enterprise MongoDB integrates with Kerberos for authentication. Refer to the documentation for more details. Username/password systems are inherently insecure—use Kerberos-based authentication if possible.