Recently I had to setup a MongoDB instance on one of our servers to use in an internal application we're building during Hackfridays. I went with MongoDB because my experience with it is very superficial and Hackfriday projects are perfect to delve into unknown tech (and it also fitted the purpose of the application).

The server where the database would reside is used for multiple internal tools, so my idea was to run MongoDB on a Docker container to keep it isolated and easier to manage.
After figuring that out, my first thought was that I needed to use the firewall to block MongoDB's port (27017) since I only needed internal usage at the moment.

Installing and configuring Ubuntu's ufw (Uncomplicated Firewall) is as simple as this:

$ apt install ufw
$ ufw enable
$ ufw allow "OpenSSH"

In the example, we're adding the OpenSSH profile to the allowed rules. This allows connections on port 22 with the TCP protocol (IPv4 and IPv6). Check enabled rules and general status info with ufw status.

$ ufw status

Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp (OpenSSH)           ALLOW IN    Anywhere
22/tcp (OpenSSH (v6))      ALLOW IN    Anywhere (v6)

After making sure that ufw was not accepting connections to port 27017, I went ahead with the basic setup to run the container with a mounted data volume.

$ mkdir -p /var/docker/mongo/data

$ docker run --rm -d -p 27017:27017 --name mongodb \
-v /var/docker/mongo/data:/data/db \
mongo

Looking good!

However, in the middle of all this, someone mentioned that I probably needed to add further security measures as Docker would bypass the firewall. Wait, what?! Yes, Docker manipulates iptables (which is the actual firewall beneath ufw). You can read more about it here (https://docs.docker.com/network/iptables/).

A quick check allowed me to verify this. I was able to connect to MongoDB from my computer using a management tool (Robot 3T, in my case).

Solving it

It is possible to prevent this behavior by setting the iptables option to false in the DOCKER_OPTS environment variable. This didn't seem to be the best option to me because:

  • it's not the default and advised behavior;
  • we already had other containers running on this machine;
  • Friday is definitely not a good day to break stuff.

The solution is quite simple: enable authentication in MongoDB. This should actually have been one of the first things to setup but hey, I'm a MongoDB noob!

By default, if you start a MongoDB instance, it won't have any authentication setup. This can be activated by making some changes to the config file. Since there is no configuration file, it needs to be created. You can find one by entering the container and taking /etc/mongod.conf.orig or you can get it on MongoDB's GitHub repo: https://github.com/mongodb/mongo/blob/master/rpm/mongod.conf. By default, MongoDB will look for a configuration file in /etc/mongod.conf.

You then add this block and restart the service:

security:
  authorization: "enabled"

The file should look like this:

# mongod.conf
# ogirinal default file can be found here: https://github.com/mongodb/mongo/blob/master/rpm/mongod.conf

# for documentation of all options, see:
#   http://docs.mongodb.org/manual/reference/configuration-options/

# where to write logging data.
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log

# network interfaces
# net:
#   port: 27017
#   bindIp: 127.0.0.1


# how the process runs
processManagement:
  timeZoneInfo: /usr/share/zoneinfo

security:
  authorization: "enabled"

Notice also the network interfaces. By keeping it commented, MongoDB will accept connections from any IP address. You can uncomment as is to accept only local connections, or you can add additional addresses.

Now, the problem with our configuration file is that it is not persisted and you would need to restart the MongoDB service inside the container. This is a bit nonsense.

The final solution is to keep the configuration file in your computer, mount another volume and tell MongoDB that it should read a configuration file.

I've created a cnf directory next to my data directory in /var/docker/mongo/. Side note: I now run all my local databases in Docker, and mount whatever volumes I need in /var/docker, such as /var/docker/mysql/.

Create the directory where I would mount the config volume:

mkdir -p /var/docker/mongo/cnf

Run mongo on Docker:

docker run --rm -d -p 27017:27017 --name mongodb \
-v /var/docker/mongo/data:/data/db \
-v /var/docker/mongo/cnf:/etc/mongo \
-e MONGO_INITDB_ROOT_USERNAME=mongoadmin \
-e MONGO_INITDB_ROOT_PASSWORD=someStrongPassword \
mongo --config /etc/mongo/mongod.conf

Here's an article about how I manage local databases with Docker:
https://blog.cloudoki.com/docker-development-databases/