Setting up users in MongoDB

Security is one of the cornerstones of any enterprise-level system. Not always will 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, an audit of the system access too. Mongo security has multiple aspects:

  • Access rights for the end users accessing the system. There will be multiple roles, such as admin, read-only users, and read/write nonadministrative users.
  • Authentication of the nodes that are added to the replica set. In a replica set, one should only be allowed to add authenticated systems. The integrity of the system will be compromised if any unauthenticated node is added to the replica set.
  • Encryption of the data that is transmitted across the wire between the nodes of the replica sets, or even the client and the server (or the mongos process in the case of a sharded setup).

In this recipe and the next one, we will be looking at how to address the first two points mentioned in the preceding bullet list. The last point, about encrypting the data being transmitted on the wire, is not supported by default by the community edition of Mongo, and it will need a rebuild of the Mongo database with the ssl option enabled.

Note

All the steps are executed on the MongoDB server Version 2.4.6, and all the explanations hold true for this version. There are quite a few changes related to the content we discuss here that are present in Version 2.6 of MongoDB. Any 2.6-specific details will be mentioned as and when needed.

Getting ready

In this recipe, we will be setting 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 of how to start a standalone server, refer to the Single node installation of MongoDB recipe in Chapter 1, Installing and Starting the MongoDB Server. We also need to start a shell that will be used for this admin operation. For a replica set, we will only be connected to a primary instance and will perform these operations.

How to do it…

We will add an admin user, a read-only user for the test database, and a read-write user for the test database in this recipe.

The following are assumed at this point:

  • The server is started, up and running and we are connected to it from the shell.
  • The server is started without any special command-line argument other than those mentioned in Chapter 1, Installing and Starting the MongoDB Server, for starting a single node. Thus, we have full access to the server for any user.

Let's get started:

  1. The first step is to create an admin user. Note that, till Version 2.4 of MongoDB, the method name is addUser. However, in Version 2.6 of MongoDB, the method is createUser. We will look at both methods of creating the users. Execute steps 3 and 4 if you are working on a MongoDB server, Version 2.4 and steps 5 and 6 if you are working on a MongoDB server, Version 2.6.
  2. Execute the following command in the Mongo shell to switch to the admin database:
    > use admin
    
  3. In the admin database, we will add a user called admin and the password as admin:
    > db.addUser('admin', 'admin')
    {
      "user" : "admin",
      "readOnly" : false,
      "pwd" : "7c67ef13bbd4cae106d959320af3f704",
      "_id" : ObjectId("52ea98ef2d00f6e6fb1fcdba")
    }
    
  4. We will now switch to the test database as follows:
    > use test
    
  5. In the test database, we will create two users, a read-only user called read_user and a read/write user called write_user. The password for both these users is the same as their usernames.
  6. Execute the following commands to create these users:
    > db.addUser({user:'read_user', pwd:'read_user', roles:['read']})
    {
      "user" : "read_user",
      "pwd" : "60477dd7460977860674077dc0039102",
      "roles" : [
        "read"
      ],
      "_id" : ObjectId("52ee29012d00f6e6fb1fcdbc")
    }
    > db.addUser({user:'write_user', pwd:'write_user', roles:['readWrite']})
    {
      "user" : "write_user",
      "pwd" : "7944cf3480b0eabbf0cff4498ed9652b",
      "roles" : [
        "readWrite"
      ],
      "_id" : ObjectId("52ee292c2d00f6e6fb1fcdbd")
    }
    
  7. We will look at how to create users in the admin and test databases in Version 2.6 of MongoDB. These steps are identical to Version 2.4 of MongoDB, except for the name of the methods. There are some additional features for this method that we will see in detail in the next section. First, we start by creating the admin user in the admin database, as follows:
    > use admin
    > db.createUser({
      user:'admin', pwd:'admin',
      customData:{desc:'The admin user for admin db'},
      roles:['readWrite', 'dbAdmin', clusterAdmin']
    }
    )
    
  8. We will add read_user and write_user to the test database. To add the users, execute the following commands 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']
    }
    )
    
  9. Now shut down the MongoDB server and the close the shell too. Restart the MongoDB server but with the --auth option on the command line, as follows:
    $ mongod .. <other options as provided earlier> --auth
    
  10. Now connect to the server from the newly opened Mongo shell and execute the following command:
    > db.testAuth.find()
    

    The testAuth collection need not exist, but you should see an error stating that we are not authorized to query the collection

  11. We will now log in from the shell using read_user as follows:
    > db.auth('read_user', 'read_user')
    
  12. We will now execute the same find operation as follows (note that the find operation should not give an error and may not return any results, depending on whether the collection exists or not):
    > db.testAuth.find()
    
  13. Now we will try to insert a document as follows (note that we should get an error stating that you are not authorized to insert data in this collection):
    > db.testAuth.insert({i:1})
    
  14. We will now log out and log in again, but with a write user as follows. Note the difference in the way we log in this time around, as against the previous instance. We are providing a document as the parameter to the auth function, whereas, in the previous case, we passed two parameters for the username and password.
    > db.logout()
    > db.auth({user:'write_user', pwd:'write_user'})
    
  15. Now execute the insert operation again as follows (this time, it should work):
    > db.testAuth.insert({i:1})
    
  16. Now execute the following command on the shell. You should get the unauthorized error:
    > db.serverStatus()
    
  17. We will now switch to the admin database. We are currently connected to the server using write_user, which has read/write permissions on the test database. From the Mongo shell, try to execute the following commands:
    > use admin
    > show collections
    
  18. Close the Mongo shell or open a new shell, as follows, from the operating system's console. This should take us directly to the admin database:
    $ mongo -u admin -p admin admin
    
  19. Now execute the following on the shell. It should show us the collections in the admin database:
    > show collections
    
  20. Try and execute the following operation:
    > db.serverStatus()
    

    Execute this step if you are on Version 2.4 of MongoDB and create the admin user using the db.addUser('<user name>', '<password>').

  21. Switch to the test database and execute the insert and find operations as follows:
    > use test
    > db.testAuth.insert({i:1})
    > db.testAuth.find()
    

How it works…

We executed a lot of steps and now we will take a closer look at them.

Initially, the server is started without the --auth option; hence, no security is enforced by default.

Version 2.4 of MongoDB is where we create a user in the admin database using the addUser(<userName>, <password>) form of the method. This creates a user in the admin database; this special user has read/write access to all the databases and can run admin commands, such as db.serverStatus(), and other replication and sharding-related commands. All users created in databases other than admin, whether read or write, will only be able to access the collections in their respective databases.

In version 2.6, however, we create the admin user using the db.createUser method. Let us take a closer look at this method first. 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 the second parameter 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 that is being created read access to the db reports and readWrite access to the test database. Let us see the complete user creation call for 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 sample values are {w:1} and {w:'majority'}.

Coming back to the admin user creation, we created the user in step 4 using the createUser method and gave three roles to this user in the admin database.

In steps 4 and 6, we created the read and read/write users in the test database using the addUser method for version 2.4 and the createUser method for version 2.6. The JSON document for the creation of a user in version 2.4 is identical to the user JSON document in version 2.6, except for a couple of differences. First, there is no customData field and second, the roles array contains string values only for the user roles.

The JSON document for the user in version 2.4 has the following format:

{
  'user' : <user name>,
  'pwd' : <password>,
  'roles':[<string values for roles of the user>]
}

We shut down the MongoDB server after the admin read and read-write user creation, and restart it with the --auth option.

On starting the server again, we connect to it from the shell, which is in step 9, but unauthenticated. Here, we try to execute a find query on a collection in the test database; this fails, as we are unauthenticated. This shows that the server now requires appropriate credentials to execute operations on it. In steps 10 to 11, we log in using read_user and try to execute a find operation first, which succeeds, and then an insert operation, which doesn't, as the user has read privileges only. The way to authenticate a user is by invoking db.auth(<user name>, <password>) from the shell, and db.logout() will log out the current logged in user.

In steps 13 to 15, we demonstrate that we can perform insert operations using write_user, but admin operations such as db.serverStatus() cannot be executed as these operations execute adminCommand on the server. This means that a non admin user is not permitted to invoke these operations. Similarly, when we change the database to admin, the write_user, which is from the test database, is not permitted to perform any operations such as getting a list of collections or any operation to query a collection in the admin database.

In steps 16 to 19, we log in to the shell using the admin user to the admin database. Previously, we logged in to the database using the auth method; in this case, we used the -u and -p options to provide 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 such as getting the server status. In version 2.6, executing the db.serverStatus call is possible, as the user is given the clusterAdmin role.

In step 18, we are able to switch to any other database and execute read/write operations. This is a special privilege for the users of the admin database, which no other user has. This is possible because we created the user in version 2.4 using the version 2.2 style of user creation, db.addUser(<username>, <password>). The admin user created in version 2.6 is not able to query the test database as it would need appropriate read and read/write privileges on the respective databases to perform these operations.

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.

There's more…

In this recipe, we saw how we can create different users and what permissions they have, restricting some sets of operations. In the next recipe, we will see how we can have authentication done at the process level. That is, how one Mongo instance can authenticate itself for being added to a replica set.

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset