Welcome to my another post about RBAC (Role Based Access Control) on Amazon DocumentDB. Access restrictions are always important to keep our databases and collections safe from unauthorized users or restricting accounts which doesn't require certain access based on the role a user plays.
Every DocumentDB cluster gets created with 2 default users.
serviceadmin user
master user
Serviceadmin user is created by Amazon for implicit uses to get the cluster created and we will not be able to control or administer the serviceadmin user. Master user is additionally created as part of cluster creation steps which gives the ebility to administer database (as root) for tasks like creating additional users, etc...
You should be able to see this by just typing "show users" as the cluster is created.
rs0:PRIMARY> show users
{
"_id" : "serviceadmin",
"user" : "serviceadmin",
"db" : "admin",
"roles" : [
{
"db" : "admin",
"role" : "root"
}
]
}
{
"_id" : "adminuser",
"user" : "adminuser",
"db" : "admin",
"roles" : [
{
"db" : "admin",
"role" : "root"
}
]
}
rs0:PRIMARY>
For creating a Role based access control, we will have to create additional roles that can be assigned to users.
Lets take a use case, we have a database called "database1" and a collection named "collection1". So, to harden our database to an enterprise standard, its good that we segregate the roles into 3 different forms.
Read
ReadWrite
Owner
Creating ReadOnly Role:
mongo --ssl --host docdb-2022-12-05-04-20-20.cx0t7bu8bkss.ap-southeast-2.docdb.amazonaws.com:27017 --sslCAFile rds-combined-ca-bundle.pem --username adminuser --password
use admin
db.createRole(
{
role: "ReadOnlyDatabase1",
privileges: [
{
resource: {db: "database1", collection: "collection1"}, actions: ["find"]
}],
roles: []
}
)
Creating ReadWrite Role:
db.createRole(
{
role: "ReadWriteDatabase1",
privileges: [
{
resource: {db: "database1", collection: "collection1"}, actions: ["find", "update", "insert", "remove"]
}],
roles: []
}
)
Creating Owner Role:
db.createRole(
{
role: "OwnerDatabase1",
privileges: [
{
resource: {db: "database1", collection: ""}, actions: ["collMod", "collStats", "createCollection", "createIndex", "dropIndex", "dbStats", "find", "killCursors", "listIndexes", "listCollections", "modifyChangeStreams", "update", "insert", "remove"]
}],
roles: []
}
)
Now lets switch to database "database1" and create a couple of documents in collection1.
use database1
db.collection1.insert({hello:"World"})
db.collection1.insert({hello1:"World1"})
Lets create 3 different additional users and assign them the different roles to demonstrate. You can take User1 as a Developer/Programmer who needs just a ReadOnly privilege into a PROD database. User2 could be a service account that is used by application to insert/update/find/delete documents. And User3 could be a managerial user who needs explicit privileges like createIndex, DropIndex, CreateCollections, etc...
db.createUser({user: "User1", pwd: "abc123", roles: ["ReadOnlyDatabase1"]})
db.createUser({user: "User2", pwd: "abc123", roles: ["ReadWriteDatabase1"]})
db.createUser({user: "User3", pwd: "abc123", roles: ["OwnerDatabase1"]})
Connecting as User1:
mongo --ssl --host docdb-2022-12-05-04-20-20.cx0t7bu8bkss.ap-southeast-2.docdb.amazonaws.com:27017 --sslCAFile rds-combined-ca-bundle.pem --username User1 --password
rs0:PRIMARY> use database1
switched to db database1
rs0:PRIMARY> db.collection1.find()
{ "_id" : ObjectId("638ddb5199587488213b2817"), "hello" : "World" }
{ "_id" : ObjectId("638ddd6f99587488213b2819"), "hello1" : "World1" }
Trying to insert -
rs0:PRIMARY> db.collection1.insert({hello2:"World2"})
rs0:PRIMARY> db.collection1.insert({hello2:"World2"})
WriteCommandError({
"ok" : 0,
"operationTime" : Timestamp(1670242546, 1),
"code" : 13,
"errmsg" : "Authorization failure"
})
Connecting as User2:
mongo --ssl --host docdb-2022-12-05-04-20-20.cx0t7bu8bkss.ap-southeast-2.docdb.amazonaws.com:27017 --sslCAFile rds-combined-ca-bundle.pem --username User2 --password
Now the user is able to insert a document in "database1".
rs0:PRIMARY> use database1
switched to db database1
rs0:PRIMARY> db.collection1.find()
{ "_id" : ObjectId("638ddb5199587488213b2817"), "hello" : "World" }
{ "_id" : ObjectId("638ddd6f99587488213b2819"), "hello1" : "World1" }
rs0:PRIMARY> db.collection1.insert({hello2:"World2"})
WriteResult({ "nInserted" : 1 })
rs0:PRIMARY> db.collection1.find()
{ "_id" : ObjectId("638ddb5199587488213b2817"), "hello" : "World" }
{ "_id" : ObjectId("638ddd6f99587488213b2819"), "hello1" : "World1" }
{ "_id" : ObjectId("638de16f6358e4856b9ac2bf"), "hello2" : "World2" }
rs0:PRIMARY>
However if the user tries to create a document in "database2", it should not be able to..
rs0:PRIMARY> use database2
switched to db database2
rs0:PRIMARY> db.collection1.insert({hello:"World"})
WriteCommandError({
"ok" : 0,
"operationTime" : Timestamp(1670242736, 1),
"code" : 13,
"errmsg" : "Authorization failure"
})
rs0:PRIMARY>
Connecting as User3:
mongo --ssl --host docdb-2022-12-05-04-20-20.cx0t7bu8bkss.ap-southeast-2.docdb.amazonaws.com:27017 --sslCAFile rds-combined-ca-bundle.pem --username User3 --password
rs0:PRIMARY> show dbs
database1 0.000GB
rs0:PRIMARY> use database1
switched to db database1
rs0:PRIMARY> db.collection2.insert({hello:"World"})
WriteResult({ "nInserted" : 1 })
rs0:PRIMARY>
rs0:PRIMARY> show collections
col
collection1
collection2
rs0:PRIMARY>
Here, I have created a new collection called "collection2" where it shows the ability to create collections which is not available as part of ReadOnly or ReadWrite roles.
Likewise, we can keep customizing the roles and make the access controls as granular as we want to harden the security standards of our database.
Hope this post was useful.
Cheers,
Balaaji Dhananjayan