Setting up MongoDB Locally

A SIMPLE MONGO DATABASE

I've used MongoDB quite a bit and wanted to share how I usually approach building a quick instance locally. If you know me, you know that I enjoy when things are broken down Barney-style.

gif of Barney the purple dinosaur singing "I love you, you love me..."

So this should be easy to follow whether you're new to all this or a seasoned vet. With that said, we'll walk through:

I relied on MongoDB's installation instructions and their core documentation for the installation. Additional sources are noted inline as appropriate. If at any time you need to start over from scratch, you can use the Helpful Scraps section at the bottom. There are commands to clean up the database as well as how to perform a clean uninstall. You'll also find some specifics of my local setup.



INSTALL

gif of mongodb being installed in a terminal

Installing and starting MongoDB is simple. Open your terminal and use the below commands:

brew tap mongodb/brew
brew install mongodb-community@4.0 
mongod --config /usr/local/etc/mongod.conf &

Homebrew's tap command creates a shallow clone of MongoDB's formulae. This means that it adds a reference/link to that GitHub repository in your Homebrew for future use.

We run install knowing that it is using the MongoDB formulae we specified before. In addition to the MongoDB binaries, Homebrew creates your configuration file (mongod.conf) and necessary paths.

Despite using Homebrew for installation, we will not be using their services command. This is because brew services uses launchctl's plist files. Consequently, it does not pull in a fresh copy of your configuration file on each startup.

Instead, we're directly invoking mongod, MongoDB's parent process, with a reference to our 'mongod.conf' using the --config flag. In Linux, the & is a control operator. It allows your shell to manage the command(s) issued in the background; this outputs the process identifier (PID). If you want to verify, you can use jobs -l to print the process running background and its PID. You can also use ps (with a | grep mongo* in this case).



LOAD

gif of mongodb being loaded with a json dataset in a terminal

Now that we've installed and started our MongoDB, we need to populate it with data. We'll be using the dataset from the mongo-pokemon repository provided by ALT-WDI. To keep this simple, I've forked this repo and trimmed it down to the single dataset file we'll need.

Make sure you have started your MongoDB instance then open your terminal, navigate to a clean directory, and run the following:

git clone https://github.com/jackkeck/pokemonDataSet
cd pokemonDataSet
mongoimport -d pokemon -c pokemons --jsonArray < pokemon.json
mongo
show dbs
use pokemon
show collections
db.pokemons.find().pretty()

Git's clone command will copy my forked repository to your local machine. Once it's downloaded, you use cd to drop down into the repository.

MongoDB's mongoimport command allows us to bulk load data. In this case, we're using flags to accomplish a few things. We're creating a database called "pokemon" using the -d flag. We're creating a collection called "pokemons" using the -c flag. We're telling the import command to treat the data file as a JSON Array. Finally, we're specifying pokemon.json as our import file.

MongoDB provides a Javascript-based shell through it's mongo command. Once we pop a shell, we're using the show dbs command to list all available databases to verify we've created the pokemon database. We use the pokemon database then list its collections using show collections. We can see the "pokemons" collection was created so we use db.pokemons.find().pretty() to check that the data was imported. I like to add the .pretty() method to make the output easier to read. For those of you who are familiar with relational databases and sql, this is the mongo equivalent of select * from pokemon.pokemons.



SECURITY

MongoDB and the security community have had an interesting relationship. Up until version 3.6, MongoDB used to allow any connection on port 27017 by default (meaning if you weren't behind a firewall, anyone could pop into your database). Even now, it still doesn't ship with any kind of access control or authentication enabled by default unless you have an enterprise-licensed version 💰. This section will walk through some basic steps we can take to protect our instance.



ROLE-BASED ACCESS CONTROL

As a rule, we should try to adhere to the principle of least privilege (PoLP). By applying this principle now, we're ensuring that we build our application with a level of access that we'll have in a production environment.

MongoDB uses role-based access control (RBAC) to allow for the governance (er.... control) of access. There is a ton of material available on what RBAC is. If you're interested, here is the definition from David Ferraiolo's and Richard Kuhn's conference paper "Role-Based Access Controls":

A role-based access control (RBAC) policy bases access control decisions on the functions a user is allowed to perform within an organization. The users cannot pass access permissions on to other users at their discretion.

MongoDB does not ship with this enabled by default. In order to enable this, we need to:

  1. Create a superuser.
  2. Enable authorization in our mongod.conf.
  3. Create a user with Read and Write (RO) access, but only to the pokemon database.

CREATE ADMIN USER

gif of creating an admin user for mongodb in terminal

A superuser has access to do anything and everything. You've been operating with this access this whole time by default. So let's create a dedicated admin user with a password. In your terminal, open a mongo shell and run the following:

use admin

db.createUser(
  { 
    user: "admin",  
    pwd: "doNotEatThat", 
    roles: 
      [
        {role: "userAdminAnyDatabase", db: "admin"},
        {role: "dbAdminAnyDatabase",   db: "admin"},
        {role: "readWriteAnyDatabase", db: "admin"}
      ]
  }
)


ENABLE AUTHORIZATION

gif of editing mongodb configuration to enable security authorization

Once the admin user is created, we need to enable the security.authorization setting in MongoDB.

  1. Stop MongoDB Run pkill -f mongod to stop the database. You can confirm it has stopped using jobs -l.
  2. Update mongod.conf I used sudo vi. For reference, use your arrow keys to get to the end of the file, press "i" on the keyboard to type, add the security block below, press "esc" on the keyboard, then type ":wq" then hit enter to save and exit.
/:~$ sudo vi /usr/local/etc/mongod.conf

systemLog:
  destination: file
  path: /usr/local/var/log/mongodb/mongo.log
  logAppend: true
storage:
  dbPath: /usr/local/var/mongodb
net:
  bindIp: 127.0.0.1
security:
   authorization: enabled

CREATE BASIC USER

gif of creating a basic user for mongodb in terminal The basic user will have read and write access for only the pokemon database. In your terminal, open a mongo shell and run the following:

db.auth("admin", "doNotEatThat")

use pokemon

db.createUser(
  { 
    user: "misty",  
    pwd: "godDamitPsyduck", 
    roles: 
      [
        {role: "readWrite", db: "pokemon"}
      ]
  }
)


TRUST, BUT VERIFY

gif showing inability to perform actions mongodb without first authenticating

To verify your database is now governed using RBAC, try to connect and view something. You'll notice that when you initiate a connection using the mongo shell, there is no longer a warning message stating "Access Control is not enabled for this database". Additionally, we're unable to view or edit anything until we authenticate using db.auth("user","pwd").



CHANGE DEFAULT PORT

gif of editing the default port in mongod.conf using terminal

Starting in MongoDB 3.6, mongod is now configured to allow connections only from localhost. This means that you can only access your instance from your laptop. That's excellent however IP's can be spoofed, etc. One extra step we'll take is to change the default MongoDB's default port. Similar to how we enabled security.authorization, we'll update our mongod.conf.

  1. Stop MongoDB Run pkill -f mongod to stop the database. You can confirm it has stopped using jobs -l.
  2. Update mongod.conf I used sudo vi. For reference, use your arrow keys to get to the end of the file, press "i" on the keyboard to type, add the port setting below, press "esc" on the keyboard, then type ":wq" then hit enter to save and exit.
/:~$ sudo vi /usr/local/etc/mongod.conf

systemLog:
  destination: file
  path: /usr/local/var/log/mongodb/mongo.log
  logAppend: true
storage:
  dbPath: /usr/local/var/mongodb
net:
  port: 23456
  bindIp: 127.0.0.1
security:
   authorization: enabled

Now start your database back up. Please keep in mind that you will need to run 'mongo --port 23456' when you launch you connect now.



HELPFUL SCRAPS

DATABASE CLEANUP To quickly wipe all databases except admin, please use this nifty one-liner from the user kev as part of the MongoDB drop every database thread on Stack Overflow:

mongo --quiet --eval 'db.getMongo().getDBNames().forEach(function(i))'

CLEAN UNISTALLL To completely uninstall MongoDB and delete all of its tertiary data, please run the following commands:

pkill -f mongod
launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.mongodb.plist
rm -f ~/Library/LaunchAgents/homebrew.mxcl.mongodb.plist
launchctl remove homebrew.mxcl.mongodb 
brew uninstall --force mongodb-community 
brew untap mongodb/brew 
rm -rf /usr/local/etc/mongo*
rm -rf /usr/local/bin/mong*
rm -rf /usr/local/var/mongodb 
rm -rf /Users/$USER/Library/Caches/Homebrew/downloads/*mongodb*


MY LOCALHOST

In the interest of transparency and sanity when debugging, these are the relevant details of my local environment.

Local Setup
OS: macOS Mojave 10.14.4
HOMEBREW: Homebrew 2.1.9
GIT: git version 2.22.0
MONGODB: MongoDB Community 4.0