create secure mongodb replica-set in amazon aws ec2 instances

by jagbir on September 10, 2012

Installation and configuration of MongoDB is relatively very easy. There are different ways you can run MongoDB, be it running single server, master-slave, replica-set or running in sharded/cluster way. By default, MongoDB installation doesn’t provide secure environment where clients should authenticate themselves before accesssing database.

My setup consists 2 small instances running as normal MongoDB servers and 1 micro instance running as arbiter. An arbiter is special member in replica-set which takes part in voting/selection of primary but doesn’t store any data in it. Its perfectly fine to host your arbiter server within an existing server running for some other service like load balancer (haproxy) etc because of its lighweight resource utilization. To make setup secure, clients must use username/password to connect to MongoDB and also members of replica-set must have shared key file.

I have tested instructions written below in instances running Ubuntu 12.04 but I strongly reccommend to have adequate testing/checking before putting such setup in production.

Step 1: Create instances and install MongoDB

Create 3 instances of your choice (micro/small/medium etc). I have used 64-bit publicly available Ubuntu AMIs from here for my instances. Let’s assume we got 3 instances with following internal IPs:

Instance1: 10.150.150.150
Instance2: 10.150.150.151
Instance3: 10.150.150.152

Time to configure them now, execute following commands in all 3 instances:

$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv 7F0CEB10
$ echo "deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen" | sudo tee /etc/apt/sources.list.d/10gen.list
$ sudo apt-get update
$ sudo apt-get install mongodb-10gen

Here first we added 10gen key in our keys database and then added mongodb repository path. After that apt-get install mongodb-10gen will install latest stable release of mongodb in our server.

You can also store your mongodb database in separate EBS volume or with RAIDed EBS volumes, If interested, please refer this detailed article about it: create and manage RAID0/RAID10 using EBS volumes in AWS EC2 Ubuntu instance

Step 2: Configure replica-set name and initialize it
Open the mongodb config file /etc/mongodb.conf and jump to last line. Uncomment the line which starts with replSet and put a name for your replica-set, your file should look like this in all 3 instances:

$ tail /etc/mongodb.conf
#slave = true
#source = master.example.com
# Slave only: specify a single database to replicate
#only = master.example.com
# or
#master = true
#source = slave.example.com
 
# in replica set configuration, specify the name of the replica set
replSet = myreplica

Make sure to restart the mongodb service after any change in config file:

$ sudo service mongodb restart
mongodb stop/waiting
mongodb start/running, process 28129

Now in instance1 or instance2, start mongo shell by typing ‘mongo’. Initialize the replica-set and other 2 instances. Please remember that our 3rd instance is arbiter one so there’s different command to add that instance in replica-set:

ubuntu@instance1:~$ mongo
> rs.initiate();
> rs.add("10.150.150.151"); 
> rs.addArb("10.150.150.152");  ## its rs.addArb() to add arbiter

The output of these commands are not shown here because I already have working replica-set and just replaying the command for the purpose of this article. Anyhow, mongo should reply with “ok” upon a correct command and throws error otherwise.

Once you add servers in replica-set, the members of replica-set have voting among themselves where arbiter will vote and the server which have highest uptime will become primary (you can override that with priorty setting though).

After a while of initializing replica-set, the instance1 should become primary:

$ mongo
myreplica:PRIMARY>  rs.conf();
{
        "_id" : "myreplica",
        "version" : 6,
        "members" : [
                {
                        "_id" : 0,
                        "host" : "10.150.150.150",
                        "priority" : 2
                },
                {
                        "_id" : 1,
                        "host" : "10.150.150.151",
                        "priority" : 2
                },
                {
                        "_id" : 2,
                        "host" : "10.150.150.152",
                        "arbiterOnly" : true
                }
        ]
}
myreplica:PRIMARY>

You can use “rs.status()” for more detailed information about replica-set:

myreplica:PRIMARY> rs.status();
{
        "set" : "myreplica",
        "date" : ISODate("2012-09-10T09:48:25Z"),
        "myState" : 1,
        "members" : [
                {
                        "_id" : 0,
                        "name" : "10.150.150.150",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "uptime" : 269445,
                        "optime" : Timestamp(1347270449000, 1),
                        "optimeDate" : ISODate("2012-09-10T09:47:29Z"),
                        "self" : true
                },
                {
                        "_id" : 1,
                        "name" : "10.150.150.151",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 269303,
                        "optime" : Timestamp(1347270449000, 1),
                        "optimeDate" : ISODate("2012-09-10T09:47:29Z"),
                        "lastHeartbeat" : ISODate("2012-09-10T09:48:23Z"),
                        "pingMs" : 0
                },
                {
                        "_id" : 2,
                        "name" : "10.150.150.152",
                        "health" : 1,
                        "state" : 7,
                        "stateStr" : "ARBITER",
                        "uptime" : 269261,
                        "lastHeartbeat" : ISODate("2012-09-10T09:48:24Z"),
                        "pingMs" : 0
                }
        ],
        "ok" : 1
}

You can jump to other hosts instance2/instance3 and check status from there as well. Now the replica-set is operational, let’s make it secure.

Step 3: Enable authentication and secure replica-set
Authentication is not enabled by default in MongoDB but its very easy to use. Open config file /etc/mongodb.conf and uncomment the auth=true line and comment out noauth line. These two lines should look like below:

$ grep auth /etc/mongodb.conf
#noauth = true
auth = true

Make this change in all instances and restart mongodb service.

$ sudo service mongodb restart
mongodb stop/waiting
mongodb start/running, process 28329

Go to mongo shell again and configure the administrative user:

$ mongo
> use admin; 
switched to db admin
> db.addUser("mongoadmin","mongopass");

After this, you need to authenticate yourself to access details. Let’s try to get in mongo shell and show databases:

$ mongo
> show dbs; 
Mon Sep 10 10:02:09 uncaught exception: listDatabases failed:{ "errmsg" : "need to login", "ok" : 0 }

So you need to supply your credentials:

> use admin;
> db.auth("mongoadmin","mongopass"); 
1
myreplica:PRIMARY>

If you have some database, you can create username/password for that and instruct teams to use that user/pass to get connect. Let’s say you have a database name proddb, create a user/pass for it:

myreplica:PRIMARY> use proddb; 
myreplica:PRIMARY> db.addUser("produ","mypass");
myreplica:PRIMARY> db.addUser("produread","otherpass", true);

Here, produ user will have read/write access to database proddb while produread will have only read-only access.

Step 4: Secure replica-set members
To make your replica-set more secure, you can enable keyFile option where each server needs to have an identical key file which works like a password for their internal communication. Essentially its just a text file.

Repeat following commands in all instances.

Create a key file:

$ echo "myrandomtextforkeyfiletosecurereplicaset" | sudo tee /etc/mongodb.key
$ sudo vim /etc/mongodb.conf
keyFile=/etc/mongodb.key  ## uncomment and set path of key file
$ sudo service mongodb restart

Now each member should have this identical file to remain a member of replica-set. Get more information about securing MongoDB from here.

Have you tried securing your MongoDB installation? Is there anything you would like to recommend in this context or if there’s any comment/suggestion, please put it in comments below.

Helpful related articles:
* create and manage RAID0/RAID10 using EBS volumes in AWS EC2 Ubuntu instance
* Simple and efficient MongoDB Backup using script

Previous post:

Next post: