MongoDB replica set authentication guide
1. Create replica set
Launch EC2 instance & install mongoDB
You have to launch at least 3 EC2 instances. Make sure those 3 instances can connect to each other (set up ssh key, ssh config and check security groups). Use ansibe playbook (role XX.mongodb) to install mongoDB
Create mongoDB replica set
- In the first instance:
mongo
When mongo shell appear, run:
rs.status()
rs.initiate()
MongoDB will make the current EC2 instance become PRIMARY
- Add members to the replica set:
rs.add( { host: "ip-172-31-37-61.ap-northeast-1.compute.internal:27017" } )
rs.add( { host: "ip-172-31-44-98.ap-northeast-1.compute.internal:27017" } )
- Try to connect to the replica set:
mongo --host mongo-002
mongo --host mongo-003
Note that mongo-002 and mongo-003 is the DNS of the 2 private-IP instances: 172.31.37.61 and 172.31.44.98 (you can set alias in /etc/hosts)
If success ⇒ OK!
You can log in to mongo shell and see the status of the replica set. Example of result of rs.status() command:
rsCMS:PRIMARY> rs.status()
{
"set" : "rsCMS",
"date" : ISODate("2021-07-19T10:35:35.903Z"),
"myState" : 1,
"term" : NumberLong(3),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"heartbeatIntervalMillis" : NumberLong(2000),
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1626690933, 1),
"t" : NumberLong(3)
},
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1626690933, 1),
"t" : NumberLong(3)
},
"appliedOpTime" : {
"ts" : Timestamp(1626690933, 1),
"t" : NumberLong(3)
},
"durableOpTime" : {
"ts" : Timestamp(1626690933, 1),
"t" : NumberLong(3)
}
},
"lastStableCheckpointTimestamp" : Timestamp(1626690893, 1),
"electionCandidateMetrics" : {
"lastElectionReason" : "electionTimeout",
"lastElectionDate" : ISODate("2021-07-19T09:52:53.446Z"),
"electionTerm" : NumberLong(3),
"lastCommittedOpTimeAtElection" : {
"ts" : Timestamp(0, 0),
"t" : NumberLong(-1)
},
"lastSeenOpTimeAtElection" : {
"ts" : Timestamp(1626685293, 1),
"t" : NumberLong(2)
},
"numVotesNeeded" : 2,
"priorityAtElection" : 1,
"electionTimeoutMillis" : NumberLong(10000),
"numCatchUpOps" : NumberLong(0),
"newTermStartDate" : ISODate("2021-07-19T09:52:53.452Z"),
"wMajorityWriteAvailabilityDate" : ISODate("2021-07-19T09:52:54.774Z")
},
"members" : [
{
"_id" : 0,
"name" : "ip-172-31-43-145.ap-northeast-1.compute.internal:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 2795,
"optime" : {
"ts" : Timestamp(1626690933, 1),
"t" : NumberLong(3)
},
"optimeDate" : ISODate("2021-07-19T10:35:33Z"),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"electionTime" : Timestamp(1626688373, 1),
"electionDate" : ISODate("2021-07-19T09:52:53Z"),
"configVersion" : 5,
"self" : true,
"lastHeartbeatMessage" : ""
},
{
"_id" : 1,
"name" : "ip-172-31-44-98.ap-northeast-1.compute.internal:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 2416,
"optime" : {
"ts" : Timestamp(1626690933, 1),
"t" : NumberLong(3)
},
"optimeDurable" : {
"ts" : Timestamp(1626690933, 1),
"t" : NumberLong(3)
},
"optimeDate" : ISODate("2021-07-19T10:35:33Z"),
"optimeDurableDate" : ISODate("2021-07-19T10:35:33Z"),
"lastHeartbeat" : ISODate("2021-07-19T10:35:34.142Z"),
"lastHeartbeatRecv" : ISODate("2021-07-19T10:35:34.742Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "ip-172-31-37-61.ap-northeast-1.compute.internal:27017",
"syncSourceHost" : "ip-172-31-37-61.ap-northeast-1.compute.internal:27017",
"syncSourceId" : 2,
"infoMessage" : "",
"configVersion" : 5
},
{
"_id" : 2,
"name" : "ip-172-31-37-61.ap-northeast-1.compute.internal:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 2570,
"optime" : {
"ts" : Timestamp(1626690933, 1),
"t" : NumberLong(3)
},
"optimeDurable" : {
"ts" : Timestamp(1626690933, 1),
"t" : NumberLong(3)
},
"optimeDate" : ISODate("2021-07-19T10:35:33Z"),
"optimeDurableDate" : ISODate("2021-07-19T10:35:33Z"),
"lastHeartbeat" : ISODate("2021-07-19T10:35:34.142Z"),
"lastHeartbeatRecv" : ISODate("2021-07-19T10:35:35.483Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "ip-172-31-43-145.ap-northeast-1.compute.internal:27017",
"syncSourceHost" : "ip-172-31-43-145.ap-northeast-1.compute.internal:27017",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 5
}
],
"ok" : 1,
"operationTime" : Timestamp(1626690933, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1626690933, 1),
"signature" : {
"hash" : BinData(0,"hzd0+nqjoPVXbOcLPn1wM0sZiJM="),
"keyId" : NumberLong("6986536417509769219")
}
}
}
rsCMS:PRIMARY>
- Insert some sample data:
db.products.insert( { item: "card", qty: 15 } )
2. Create replica set authentication
1. Create keyfile:
cd /srv/
sudo mkdir mongodb
openssl rand -base64 756 > /srv/mongodb/keyfile
chmod 400 /srv/mongodb/keyfile
sudo chown mongod /srv/mongodb/keyfile
Note: You have to copy the keyfile that you have generated to each replica set member (also have to chmod 400 and chown mongod on member servers)
- Run ansible copy toi /nfs/
2. Create superuser:
Note: You have to stand on the Primary server to do this!
You have to create user root before you enable access control for the replica set. To create other users, you have to log into user root!
Log in to the mongo shell
mongo
and run:
admin = db.getSiblingDB("admin")
admin.createUser(
{
user: "root",
pwd: "root",
roles: [ { role: 'root', db: 'admin' } ]
}
)
3. Shutdown all the members of the replica set:
- SSH to the MongoDB servers
- Run: sudo systemctl stop mongod
4. Modify the /etc/mongod.conf file:
You have to add security.keyFile option to the replica set:
# 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
# Where and how to store data.
storage:
dbPath: /var/lib/mongo
journal:
enabled: true
# engine:
# mmapv1:
# wiredTiger:
# how the process runs
processManagement:
fork: true # fork and run in background
pidFilePath: /var/run/mongodb/mongod.pid # location of pidfile
timeZoneInfo: /usr/share/zoneinfo
# network interfaces
net:
port: 27017
# bindIp: 127.0.0.1 # Enter 0.0.0.0,:: to bind to all IPv4 and IPv6 addresses or, alternatively, use the net.bindIpAll setting.
bindIp: 0.0.0.0
#security:
security:
authorization: enabled
keyFile: /srv/mongodb/keyfile
#operationProfiling:
replication:
replSetName: "rsCMS"
#sharding:
## Enterprise-Only Options
#auditLog:
#snmp:
5. Start mongod again:
SSH to your mongodb servers and run:
sudo systemctl start mongod
6. Log into the mongo shell with root user:
You now can log into mongoDB using root user:
mongo -u "root" -p --authenticationDatabase "admin"
Note: With users that authenticationDatabase is admin, –authenticationDatabase “admin” is optional
3. Create users:
1. Create user same as root but can not access to config and local:
admin = db.getSiblingDB("admin")
admin.createUser(
{
user: "adminrw",
pwd: "adminrw",
roles: [ { role: "readWriteAnyDatabase", db: "admin" } ]
}
)
2. Create DB read only user (but can not read config and local db):
admin = db.getSiblingDB("admin")
admin.createUser(
{
user: "read",
pwd: "read",
roles: [ { role: "readAnyDatabase", db: "admin" } ]
}
)
3. Create Database – level access control user:
use testDB1
db.createUser(
{
user: "admin1",
pwd: "admin1",
roles: [ { role: "readWrite", db: "testDB1" } ]
}
)
When log in:
mongo -u "admin1" -p --authenticationDatabase "testDB1"
Note: –authenticationDatabase “testDB1” is neccessary. If you do not specify ⇒ can not authenticate.
4. Create collection – level access control user:
- Create user – defined role:
use admin
db.createRole(
{
role: "manageOpRole",
privileges: [
{ resource: { db: "test", collection: "products" }, actions: [ "find", "update", "insert" ] },
{ resource: { db: "testDB1", collection: "user" }, actions: [ "find", "update" ] },
{ resource: { db: "testDB2", collection: "" }, actions: [ "listCollections" ] }
],
roles: []
}
)
- Create user and attach role:
admin = db.getSiblingDB("admin")
admin.createUser(
{
user: "cadmin2",
pwd: "cadmin2",
roles: [ { role: "manageOpRole", db: "admin" } ]
}
)
4. Testing
Note: In our case, mongo-003 is primary and 001, 002 are secondaries.
Case 1: Log in to mongo shell and do not provide any authentication data ⇒ Can not find db information
Even in the case that you know that there are dbs that exist:
You can not interact with dbs
Case 2: Log into mongo shell, provide wrong authentication data:
- Wrong password:
- Wrong username:
Case 3: Log into mongo shell with correct authentication data:
Log into mongo shell with correct authentication data on secondaries:
Case 4: Read Write any database but not config and local:
- When log into by root user:
- Log in by root user in secondaries:
- Log into by adminrw user:
Case 5: Read only but can not read local and config DB: (log in to by read user)
Case 6: Access control for only one DB only: (Log into by admin1 user)
- If connect to wrong DB:
- Connect to right DB:
Case 7: Create collection – level access control: