Setting up users in Mongo

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:

  • Access rights for the end users accessing the system. There would be multiple roles such as admin, read-only users, and read and write non-administrative 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 would 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 case of sharded setup).

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.

Getting ready

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.

How to do it…

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:

  • The server is 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 Server for Starting a single node instance using command-line options recipe. We thus have full access to the server for any user.

Perform the following steps:

  1. The first step we will do is to create an admin user. All the commands assume that you are using MongoDB 3.0 and above.
  2. First, we start by creating the admin user in admin database as follows:
    > use admin
    > db.createUser({
          user:'admin', pwd:'admin',
          customData:{desc:'The admin user for admin db'},
          roles:['readWrite', 'dbAdmin', 'clusterAdmin']
        })
    
  3. We will add the 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']
      }
    )
    
  4. Now shut down the mongo server and the close the shell too. Restart the mongo server but with the --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.

  5. Now connect to the server from the newly opened mongo shell and execute the following:
    > db.testAuth.find()
    
  6. The collection testAuth need not exist, but you should see an error that we are not authorized to query the collection.
  7. We will now log in from the shell using a read_user as follows:
    > db.auth('read_user', 'read_user')
    
  8. We will now execute the same 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()
    
  9. Now, we will try to insert a document as follows. We should get an error that you are not authorized to insert data in this collection.
    > db.testAuth.insert({i:1})
    
  10. We will now log out and log in again, but with a write user as follows. Note the difference in the way we login this time around as against the previous instance. We are providing a document as the parameter to the 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})
    
  11. Now, execute the following on the shell. You should get the unauthorized error:
    > db.serverStatus()
    
  12. We will now switch to 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
    
  13. Close the mongo shell or open a new shell as follows from the operating system's console. This should take us directly to admin database:
    $ mongo -u admin -p admin admin
    
  14. Now execute the following on the shell. It should show us the collections in the admin database:
    > show collections
    
  15. Try and execute the following operation:
    > db.serverStatus()
    

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 --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.

There's more…

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.

See also

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

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