Quantcast
Channel: Ramblings on life & code » James Tan
Viewing all articles
Browse latest Browse all 10

MongoDB Auth – Part 2 (Custom roles)

$
0
0

In Part 1 of this series, we stepped through the process to create MongoDB system users: admin, MMS monitoring agent, and MMS backup agent users. We now look at ways to create MongoDB application users – the accounts that our applications uses to authenticate with MongoDB.

Application isolation

For example, suppose we have two applications, app1 and app2, that share the same MongoDB deployment (which might be a standalone, replica set, or sharded cluster). We want to ensure that each application can only read and write to its own set of data for integrity, privacy, and security reasons. So let’s use db.createUser() to assign the dbOwner role to the respective databases:

db = db.getSiblingDB('admin')
// Remember to change the passwords from "change_me" to something more secure!
db.createUser({ user: 'admin', pwd: 'change_me', roles: [ 'root' ] })
db.createUser({ user: 'app1',  pwd: 'change_me',
                roles: [ { role: 'dbOwner', db: 'app1' ] })
db.createUser({ user: 'app2',  pwd: 'change_me',
                roles: [ { role: 'dbOwner', db: 'app2' ] })

Let’s test that this works by spinning up a clean MongoDB instance in a terminal window:

$ mkdir ~/data
$ mongod --dbpath ~/data

Now save the user creation script above to create-app-users-1.js and execute in another terminal window:

$ mongo create-app-users-1.js

Restart MongoDB with authentication enabled by first terminating the current instance by pressing Ctrl-c and running:

$ mongod --dbpath ~/data --auth

In another terminal window, connect via mongo shell without authentication and try to read/write the app1 database:

$ mongo
MongoDB shell version: 2.6.4
connecting to: test
> use app1
switched to db app1
> db.test.insert({a:1})
WriteResult({
	"writeError" : {
		"code" : 13,
		"errmsg" : "not authorized on app1 to execute command { insert: \"test\", documents: [ { _id: ObjectId('5435dfa6e8ebb07a1eaa14a4'), a: 1.0 } ], ordered: true }"
	}
})
>
> db.test.find()
error: { "$err" : "not authorized for query on app1.test", "code" : 13 }
>
> show collections
2014-10-09T02:07:12.055+0100 error: {
	"$err" : "not authorized for query on app1.system.namespaces",
	"code" : 13
} at src/mongo/shell/query.js:131
>

It fails as expected. Now repeat while authenticated as the app1 user:

$ mongo admin -u app1 -p change_me
MongoDB shell version: 2.6.4
connecting to: admin
> use app1
switched to db app1
> db.test.insert({a:1})
WriteResult({ "nInserted" : 1 })
>
> db.test.find()
{ "_id" : ObjectId("5435defc147496da04af01ba"), "a" : 1 }
>
> show collections
system.indexes
test
>

Works as expected. Now try the same thing with the app2 database while still authenticated as app1:

> use app2
switched to db app2
> db.test.insert({a:1})
WriteResult({
	"writeError" : {
		"code" : 13,
		"errmsg" : "not authorized on app2 to execute command { insert: \"test\", documents: [ { _id: ObjectId('5435e0e2e8ebb07a1eaa14a5'), a: 1.0 } ], ordered: true }"
	}
})
>
> db.test.find()
error: { "$err" : "not authorized for query on app2.test", "code" : 13 }
>
> show collections
2014-10-09T02:12:11.698+0100 error: {
	"$err" : "not authorized for query on app2.system.namespaces",
	"code" : 13
} at src/mongo/shell/query.js:131
>

It fails as expected, so now we know our application users are set up properly.

Application user lockdown

Granting dbOwner to the application user might be a bit too much for some. For example, not granting index creation permissions will help prevent accidentally foreground index creation, which locks the database until completion. It may even be desirable to whitelist collections that can be read and written to, in order protect against code typos or mistakenly dropping the wrong collections. Or perhaps allow only inserts to a log or audit collection to prevent tampering. To do so, we create a custom role and assign that to our new app3 user:

// See http://docs.mongodb.org/manual/reference/privilege-actions/ for the list
// of actions and what they allow.
 
// Allow db.stats(), db.<collection>.stats(), etc. on database
db_actions = [ "dbStats", "collStats", "planCacheRead", "planCacheWrite" ]
 
// Allow CRUD, profiler, etc. on collection
collection_actions = [
  "find", "insert", "update", "remove", "enableProfiler", "killCursors"
]
 
db.createRole({
  role: "app3",
  privileges: [
    // Allow common cluster read-only actions like db.serverStatus(), show dbs, and db.currentOp()
    { resource: { cluster: true }, actions: [ "serverStatus", "listDatabases", "inprog" ] },

    { resource: { db: "app3", collection: "" }, actions: db_actions },
    { resource: { db: "app3", collection: "coll1" }, actions: collection_actions },
    { resource: { db: "app3", collection: "coll2" }, actions: collection_actions },
 
    // Only allow application to insert to the audit collection, to prevent any
    // possibility of tampering
    { resource: { db: "app3", collection: "audit" }, actions: [ "insert" ] },
 
    // Allow db.<collection>.getIndexes()
    { resource: { db: "app3", collection: "system.indexes" }, actions: [ "find" ] },
 
    // Allow 'show collections'
    { resource: { db: "app3", collection: "system.namespaces" }, actions: [ "find" ] }, 
  ],
  roles: [ ]
})
 
db.createUser({ user: "app3", pwd: "change_me", roles: [ "app3" ] })

Adapt the script according to your needs and refer the MongoDB documentation for details. In particular, see Built-in Roles, Privilege Actions, db.createUser(), and db.createRole().

Viewing all articles
Browse latest Browse all 10

Trending Articles