Securing MongoDB

Follow

Preventing External Connections

From the start, MongoDB installations were left open to listen to any interface. Thus, anyone (even from outside) could connect to your database.

Only after MongoDB 2.6+ does MongoDB have the default configuration which sets bindIp to localhost. An excerpt from the MongoDB configuration file appears as seen below:

# network interfaces
net:
  port: 27017
  bindIp: 127.0.0.1

This means MongoDB will only listen to connections from localhost, therefore it is closed to external or public access.

However, it is not uncommon to see configurations that set bindIp to 0.0.0.0 or comment it out. If bindIp is commented out or set anywhere, this will mean that (unless you have the necessary firewall configuration) your MongoDB instance will be publicly accessible.

In order to prevent this from happening, the following is recommended:

  • Set bindIp properly, as described here
  • If you have to set bindIp anywhere, ensure your firewall configuration blocks external or unwanted connections using a software firewall such as UFW
  • If you are hosting your Countly instance on a cloud provider, such as Google Cloud or AWS, set the firewall configuration under networking (for Google Cloud) or security groups (for AWS) to prevent outside access to your configured MongoDB port(s)

Adding Authentication for Your MongoDB Database

This is an additional and optional layer of security, assuming you leave your network interfaces open (or only specific IP address or additional blocking by firewall) and would like to ensure that there is an additional security measure.

Adding Authentication for your MongoDB database means that in order for anyone to connect to your database, they will need to provide a login and password to actually read and modify the data.

Credentials

For the purpose of this article, we will use the values test as the login and test123 as the password. Please use more secure values in your deployment.

Adding Root User

Before you enable authentication on MongoDB, it is suggested you create a root user account, so you can manage other users without the need to disable authentication.

# Enter mongo console
mongo

# Switch database to the admin database
use admin

# Add your user with root permission
db.createUser({user:"admin", pwd:"secure_password",roles: [{role:"root", db:"admin"}]})

Adding a Single User for All Countly Databases

This is the easiest and most robust way. You add one user to one database and then use that database to authenticate users on other databases as well.

# Enter mongo console
mongo

# Create user for access to any database
use admin
db.createUser({user: "test" , pwd: "test123", roles: [  "readWriteAnyDatabase" ]})

#or

# Add your user with read and write permission to each database separately
use countly
db.createUser({user:"test", pwd:"test123", roles: ["readWrite"]})
use countly_fs
db.createUser({user:"test", pwd:"test123", roles: ["readWrite"]})
use countly_out
db.createUser({user:"test", pwd:"test123", roles: ["readWrite"]})
use countly_drill
db.createUser({user:"test", pwd:"test123", roles: ["readWrite"]})

#or add users with auth source as admin db

# Switch database to the one we will use for authentication
use admin

# Add your user with read and write permission to specific databases
db.createUser({user:"test", pwd:"test123", roles: [{role:"readWrite", db:"countly"},{role:"readWrite", db:"countly_out"},{role:"readWrite", db:"countly_drill"},{role:"readWrite", db:"countly_fs"}]})

Adding Multiple Users for Each Countly Database Separately

If you would like more control over who does what, you may create separate users for each database.

This Step is Optional

This step is not needed if you go with a single user for all Countly databases.

Connect to your database server (may be the same server Countly is running on), and run:

# Enter mongo console
mongo

# Switch database to countly
use countly

# Add your user with read and write permission to this database
db.createUser({user:"test", pwd:"test123",roles: [{role:"readWrite", db:"countly"}]})

# Switch database to countly_fs
use countly_fs

# Add your user with read and write permission to this database
db.createUser({user:"test", pwd:"test123",roles: [{role:"readWrite", db:"countly_fs"}]})

# Switch database to countly_out
use countly_out

# Add your user with read and write permission to this database
db.createUser({user:"test", pwd:"test123",roles: [{role:"readWrite", db:"countly_out"}]})

# If you have Countly Enterprise, do the same for drill database
use countly_drill
db.createUser({user:"test", pwd:"test123",roles: [{role:"readWrite", db:"countly_drill"}]})

Force Authentication on Single Mongo Instances

At this point, there are users with credentials created in a single database or for each database (depending on which option you chose), but MongoDB is not yet forcing authentication, which means you may simply access data as you did before. The next step is to make MongoDB only allow authenticated access.

Ensure You Add Users to Your Databases

Ensure you have already added users to your databases, otherwise you will not be able to access your MongoDB data unless you switch off forcing authentication.

To force authentication, we will need to modify the MongoDB configuration file, which is usually located at /etc/mongod.conf.

Edit it by uncommenting (or adding if they are missing) these lines:

security:
  authorization: enabled

Force Authentication on MongoDB Replica Set

For replica sets, you will also need to ensure that the communication between instances is secure. To do so, you need to generate a key and copy this key to all MongoDB instances.

#generate the key
openssl rand -base64 741 > mongodb.key

#copy it to all mongodb instances and make sure it is in accessible for mongodb user place

#make sure file is owned by same user who owns mongodb process
chown mongodb:mongodb mongodb.key

#and that file has no global or group permissions, only user permissions
chmod 600 mongodb.key

Now modify the mongod.conf file to use that key.

security:
  authorization: enabled
  keyFile: /home/mongodb/mongodb.key

Verify Authentication Working

After you have made modifications to the mongod.conf file, you may restart your mongod process.

Default Upstart
systemctl restart mongod

After which you may try connecting to your database and view data.

mongo countly
show collections

It should throw out an unauthorized error.

Now try connecting using your credentials.

#connecting to any database with single admin db user
mongo countly -u test -p test123 --authenticationDatabase admin

#or

#connecting to specific database with its user
mongo countly -u test -p test123

show collections

It should correctly display database collections, which means your authentication is working.

Adding Authentication Information to Countly

The next step is to set up the authentication part on Countly, so it can connect to your database and use it properly.

First, let's encrypt the password value before saving it in the configuration file.

#should output encrypted value like 3a0330674fa208946f412870fd3a2245[CLY]_true
countly encrypt test123

Now we need to modify 2 configuration files to provide credentials (possibly 3 if you are using a separate configuration for Drill). You will need to modify:

  • countly/api/config.js
  • countly/frontend/express/config.js
  • countly/plugins/drill/config.js (if your authentication differs for countly_drill, you will need to create this file. Otherwise, it will reuse the first two configurations).

Your modification should look like this for the MongoDB clause, providing the user login and password:

mongodb: {
        host: "localhost",
        db: "countly",
        port: 27017,
        max_pool_size: 500,
        //providing username
        username: "test",
        //providing encrypted password
        password: "3a0330674fa208946f412870fd3a2245[CLY]_true",
        //mongos: false,
        //only providing this, if you are authenticating through other db
        dbOptions:{
            //name of authentication db in this example is admin
            //only needed if you are using one user for all databases
            authSource: "admin"
        },
        /*
        serverOptions:{
            //server options
            ssl:false
        }
        */
    }

Looking for help?