Setting up MongoDB Locally
Tags: mongodb, beginners, devops, pokemon
Published: 2019-08-02 18:51:48

## **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..."](https://media.giphy.com/media/GMalHy5bWkfN6/giphy.gif)

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:

  * [Basic Installation](#install)
  * [Loading a Dataset](#load)
  * [Basic Security](#security)

I relied on [MongoDB's installation instructions](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-os-x/) and their [core documentation](https://docs.mongodb.com/) 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](#help) section at the bottom. There are commands to clean up the database as well as how to perform a clean uninstall. Additionally, you can find the specifics of my [local setup](#localhost) below.
***
***

## **MY LOCALHOST** <a name="localhost"></a>
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 |  

***
***

## **INSTALL** <a name="install"></a>
![gif of mongodb being installed in a terminal](https://media.giphy.com/media/ciwIbp8x2krQnhPgYu/giphy.gif)
Installing and starting MongoDB is simple. Open your terminal and use the below commands: 
```bash
brew tap mongodb/brew
brew install mongodb-community@4.0 
mongod --config /usr/local/etc/mongod.conf &
```
Homebrew's [`tap`](https://docs.brew.sh/Taps) command creates a shallow clone of [MongoDB's formulae](https://github.com/mongodb/homebrew-brew). This means that it adds a reference/link to that GitHub repository in your Homebrew for future use. 

We run [`install`](https://docs.brew.sh/Manpage#install-formula) 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`](https://docs.brew.sh/Manpage#services-subcommand) 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.](https://github.com/Homebrew/homebrew-services/issues/186)

Instead, we're directly invoking [`mongod`](https://docs.mongodb.com/manual/reference/program/mongod/), MongoDB's parent process, with a reference to our 'mongod.conf' using the `--config` flag. In Linux, the `&` is a [control operator](https://www.w3resource.com/linux-system-administration/control-operators.php). It allows your shell to manage the command(s) issued in the background; this outputs the [process identifier (PID)](http://www.linfo.org/pid.html). 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** <a name="load"></a>   
![gif of mongodb being loaded with a json dataset in a terminal](https://media.giphy.com/media/iFhFPIbQEQ6GPIs4KY/giphy.gif)
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`](https://github.com/ATL-WDI-Exercises/mongo-pokemon) repository provided by [ALT-WDI](https://github.com/ATL-WDI-Exercises). 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:
```bash
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`](https://git-scm.com/docs/git-clone) command will copy my forked repository to your local machine. Once it's downloaded, you use [`cd`](http://linuxcommand.org/lc3_man_pages/cdh.html) to drop down into the repository.

MongoDB's [`mongoimport`](https://docs.mongodb.com/manual/reference/program/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](https://www.json.org/). Finally, we're specifying pokemon.json as our import file.

MongoDB provides a Javascript-based shell through it's [`mongo`](https://docs.mongodb.com/manual/mongo/) command. Once we pop a shell, we're using the [`show dbs`](https://docs.mongodb.com/manual/reference/mongo-shell/) 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`](https://docs.mongodb.com/manual/reference/mongo-shell/). We can see the "pokemons" collection was created so we use [`db.pokemons.find().pretty()`](https://docs.mongodb.com/manual/reference/method/db.collection.find/index.html) to check that the data was imported. I like to add the [`.pretty()`](https://docs.mongodb.com/manual/reference/method/cursor.pretty/index.html) 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** <a name="security"></a>
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** <a name="rbac"></a>
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"_:](https://csrc.nist.gov/publications/detail/conference-paper/1992/10/13/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 (RW) access, but only to the pokemon database. 

#### *CREATE ADMIN USER* <a name="admin"></a>
![gif of creating an admin user for mongodb in terminal](https://media.giphy.com/media/McUpUbMLNHwjFm0o3K/giphy.gif)
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:
```javascript
use admin

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

#### *ENABLE AUTHORIZATION* <a name="enableauth"></a>
![gif of editing mongodb configuration to enable security authorization](https://media.giphy.com/media/MBNtB3E78GeCA0Rdyl/giphy.gif)
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.
```yaml
/:~$ 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* <a name="basicuser"></a>
![gif of creating a basic user for mongodb in terminal](https://media.giphy.com/media/J4Ij43gtosj1PWAVaE/giphy.gif)
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:
```javascript
db.auth("admin", "doNotEatThat")

use pokemon

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

#### *TRUST, BUT VERIFY* <a name="trustbutverify"></a>
![gif showing inability to perform actions mongodb without first authenticating ](https://media.giphy.com/media/XBu8j8LPQB3WNqQn1m/giphy.gif)
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** <a name="defaultport"></a>
![gif of editing the default port in mongod.conf using terminal](https://media.giphy.com/media/LPHQpKvJKUsIZSOD2A/giphy.gif)
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.
```yaml
/:~$ 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** <a name="help"></a> 

**DATABASE CLEANUP**
To quickly wipe all databases _except admin_, please use this nifty one-liner from the user [kev](https://stackoverflow.com/users/348785/kev) as part of the [MongoDB drop every database](https://stackoverflow.com/a/16908246) thread on Stack Overflow:
```bash
mongo --quiet --eval 'db.getMongo().getDBNames().forEach(function(i){db.getSiblingDB(i).dropDatabase()})'
```
**CLEAN UNISTALLL**
To completely uninstall MongoDB and delete all of its tertiary data, please run the following commands:
```bash
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*
``` 
***
***