Security is one of the cornerstones of any enterprise-level system. Not always would you find a system in a completely safe and secure environment to allow unauthenticated user access to it. Apart from test environments, almost every production environment requires proper access rights and perhaps audit of the system access too. Mongo security has multiple aspects:
In this and the next recipe, we would be looking at how to address the first and the second point given here. The third point of encrypting the data being transmitted on the wire is not supported by default by the community edition of mongo and would need a rebuild of mongo database with the ssl
option enabled.
In this recipe, we will set up users for a standalone mongo instance. We need to start a standalone server listening to any port for client connections; in this case, we will stick to the default 27017
. If you are not aware how to start a standalone server, refer to Installing single node MongoDB in Chapter 1, Installing and Starting the Server. We also need to start a shell that would be used for this admin operation. For a replica set, we will only be connected to a primary and perform these operations.
We will add an admin user, a read-only user for a test database, and a read-write user for test database in this recipe.
It is assumed that at this point:
Perform the following steps:
> use admin > db.createUser({ user:'admin', pwd:'admin', customData:{desc:'The admin user for admin db'}, roles:['readWrite', 'dbAdmin', 'clusterAdmin'] })
read_user
and write_user
to test database. To add the users, execute the following from the mongo shell:> use test > db.createUser({ user:'read_user', pwd:'read_user', customData:{desc:'The read only user for test database'}, roles:['read'] } ) > db.createUser({ user:'write_user', pwd:'write_user', customData:{desc:'The read write user for test database'}, roles:['readWrite'] } )
--auth
option on the command line:$ mongod .. <other options as provided earlier> --auth
If your mongod instance is using /etc/mongod.conf
, then add the line auth = true
in the configuration file and restart the mongod service.
> db.testAuth.find()
testAuth
need not exist, but you should see an error that we are not authorized to query the collection.read_user
as follows:> db.auth('read_user', 'read_user')
find
operation as follows. It should not give an error and it might not return any results depending on whether the collection exists or not:> db.testAuth.find()
> db.testAuth.insert({i:1})
auth
function, where as in previous case we passed two parameters for the username and password:> db.logout() > db.auth({user:'write_user', pwd:'write_user'}) Now to execute the insert again as follows, this time around it should work > db.testAuth.insert({i:1})
> db.serverStatus()
admin
database. We are currently connected to the server using a write_user
that has read-write permissions on the test
database. From the mongo shell, try to do the following:> use admin > show collections
$ mongo -u admin -p admin admin
> show collections
> db.serverStatus()
We executed a lot of steps and now we will take a closer look at them.
Initially, the server is started without --auth
option and hence no security is enforced by default. We create an admin user with the db.createUser
method. The signature of the method to create the user is createUser(user, writeConcern)
. The first parameter is the user, which actually is a JSON document and second is the write concern to use for user creation. The JSON document for the user has the following format:
{ 'user' : <user name>, 'pwd' : <password>, 'customData': {<JSON document providing any user specific data>} 'roles':[<roles of the user>] }
The roles provided here can be provided as follows, assuming that the current database when the user is created is test on the shell:
[{'role' : 'read', 'db':'reports'}, 'readWrite']
This gives the user being created read access to the reports db
and readWrite
access to the test
database. Let's see the complete user creation call of the test
user:
> use test > db.createUser({ user:'test', pwd:'test', customData:{desc:'read access on reports and readWrite access on test'}, roles:[ {role:'read', db : 'reports'}, 'readWrite' ] } )
The write concern, which is an optional parameter, can be provided as the JSON document. Some examples values are {w:1}
, {w:'majority'}
.
Coming back to the admin user creation, we created the user in step 2 using the createUser
method and gave three inbuilt roles to this user in the admin
database.
In step 3, we created the read
and read-write
users in test
database using the same createUser
method.
We shut down the MongoDB server after the admin
, read
, and read-write
user creation and restarted it with the --auth
option.
On starting the server again, we will connect to it from the shell in step 8, but unauthenticated. Here, we try to execute a find
query on a collection in test database, which fails as we are unauthenticated. This indicates that the server now requires appropriate credentials to execute operations on it. In step 8 and 9, we log in using the read_user
and first execute a find
operation (which succeeds), and then an insert that doesn't as the user has read privileges only. The way to authenticate a user by invoking from the shell db.auth(<user name>, <password>)
and db.logout()
, which will logout the current logged in user.
In steps 10 to 12, we demonstrate that we can perform insert
operations using write_user
but admin operations like db.serverStatus()
cannot be executed. This is because these operations execute an admin command
on the server, which a non-admin user and not permitted to invoke these. Similarly, when we change the database to admin, the write_user
, which is from test
database, is not permitted to perform any operations like getting a list of collections or any operation to query a collection in admin
database.
In Step 14, we log in to the shell using the admin
user to the admin
database. Previously, we logged in to database using the auth
method; in this case, we used the -u
and -p
options for providing the username and the password. We also provided the name of the database to connect to, which is admin in this case. Here, we are able to view the collections on the admin database and also execute admin operations like getting the server status. Executing the db.serverStatus
call is possible as the user is given the clusterAdmin
role.
One final thing to note, apart from writing to a collection, a user with write privileges can also create indexes on the collection in which he has write access.
In this recipe, we saw how we can create different users and what permissions they have restricting some set of operations. In the following recipe, we will see how we can have authentication done at process level. That is, how can one mongo instance authenticate itself for being added to a replica set.