How to make your MongoDB container more secure?

How to make your MongoDB container more secure?

Start it with docker

The most simple way to get MongoDB instance in your machine is using docker to run mongodb image on docker hub as below:

docker run --name mongodb -p 27017:27017 mongo

By specifying -p 27017:27017, you can access to it with the connection string such as mongodb://localhost:27017 or mongodb://0.0.0.0:27017, as same as installed MongoDB server.

This database instance is only available locally. In the case you want to access to it from another computer, you have to overwrite the default config file with a custom MongoDB configuration file.

Allow to connect from outside

The command looks like below:

docker run --name mongodb -p 27017:27017 -v /path/to/custom/mongod.conf:/etc/mongod.conf mongo

In which, the mongod.conf file has been customized to allow to connect from outside:

# mongod.conf

# other settings

# network interfaces
net:
  port: 27017
  # bindIp: 127.0.0.1  # this is default setting
  bindIp: 0.0.0.0  # this is what changed

# other settings

But since now your database is public and no longer secure.

In my experience, the first thing we should do in this situation is change the default port.

Change default port

Let's map it to another free port:

docker run --name mongodb -p 27117:27017 -v /path/to/custom/mongod.conf:/etc/mongod.conf mongo

So that the connection string will become mongodb://IP_ADDRESS:27117, not default port.

As you know, there are many bots those automatically scan for public databases on the internet. They try to check the popular default ports, such as 3306 (mysql), 5432 (posgres), 6379 (redis), 27017 (mongo), etc. Changing to a strange port may help you in almost cases.

However, that's not enough.

Enable authentication

By default MongoDB will start with authentication disabled. We should enable it with a little bit change to entrypoint. In order to make things easier management, we create a docker-compose.yaml file as below:

version: "3"

services:
  mongodb:
    image: mongo
    container_name: mongodb
    ports:
      - 27117:27017
    volumes:
      - ./mongod.conf:/etc/mongod.conf
    entrypoint: ["mongod", "--auth", "--config", "/etc/mongod.conf"]

In the above .yaml file, we added our custom entrypoint to run mongodb with --auth flag. Now when we run it with docker-compose, we should get MongoDB within a container named mongodb similar to the docker run command earlier.

And the following is the most important parts of this article...

Create users with specific permissions

Until now, our MongoDB instance does not have any user yet. We will get into its container and create a few ones.

Firstly, let's get the interactive mongo shell by running:

docker exec -it mongodb mongo

With opening mongo shell, we can now run the commands to switch to admin database and create a user named boss with admin permission on any database:

use admin
db.createUser({
  user: 'boss',
  pwd: 'YourVeryComplexPassword',
  roles:['userAdminAnyDatabase']
})

Done, press Ctrl + C to exit the shell and leave that session.

Note that, at this time, we had already a MongoDB instance run with authentication mode enabled, and an admin user. The connection string for boss user now looks like:

mongodb://boss:YourVeryComplexPassword@IP_ADDR:27117

But that's power user, don't use it for regular applications. Instead, we should create more users for different purposes.

To create more user or database, we have to come back to mongo shell as boss user:

docker exec -it mongodb mongo -u boss

Enter password when asked and you should see something like:

Assume that we have a plan to use a database blogs to store our great blog posts. We should add a user named writer which can write to this database, and a user named reader which can read only.

Do that as below:

# go to database `admin`
use admin

# add writer
db.createUser({
  user: 'writer',
  pwd: 'YourComplexPassword',
  roles:[
    { 
      role: 'readWrite',
      db: 'blogs'
    }
  ]
})

# add reader
db.createUser({
  user: 'reader',
  pwd: 'YourSimplePassword',
  roles:[
    { 
      role: 'read',
      db: 'blogs'
    }
  ]
})

Done, press Ctrl + C to exit the shell and leave that session again.

Now we have 2 new users, one for read/write and other for read only action. Their connection strings are:

mongodb://writer:YourComplexPassword@IP_ADDR:27117 mongodb://reader:YourSimplePassword@IP_ADDR:27117

With the apps or API endpoints those only load and show blog posts, we just need to use the connection string for reader. Otherwise, when we need to add more blog posts, we will connect to database as writer.

That's it. Enjoy playing with docker and MongoDB :)