Javascript required
Skip to content Skip to sidebar Skip to footer

Mongodb Create Read Only User for All Databases

The author selected the Open Internet/Free Speech Fund to receive a donation as part of the Write for DOnations program.

Introduction

Modern database systems are capable of storing and processing huge amounts of data. Because of this, it's relatively uncommon for any one user person to be solely responsible for handling all the activities related to managing a database. Often, different database users require different levels of access to certain parts of a database: some users might need only to read the data in specific databases, while others must be able to insert new documents or modify existing ones. Likewise, an application might require unique permissions that only allow it to access the parts of a database it needs to function.

MongoDB employs a robust mechanism to control access and privileges to a databases system known as Role-Based Access Control (RBAC). In this tutorial, you'll learn how RBAC works, the meaning and purpose of the principle of least privilege, as well as how to use MongoDB's access privileges features in practice.

Prerequisites

To follow this tutorial, you will need:

  • A server with a regular, non-root user with sudo privileges and a firewall configured with UFW. You can prepare your server by following this initial server setup tutorial.
  • MongoDB installed on your server. To set this up, follow our tutorial on How to Install MongoDB on Ubuntu 20.04.
  • Your server's MongoDB instance secured by enabling authentication and creating an administrative user. To secure MongoDB like this, follow our tutorial on How To Secure MongoDB on Ubuntu 20.04.

Note: The example tutorials on how to configure your server, install and then secure MongoDB installation refer to Ubuntu 20.04. This tutorial concentrates on MongoDB itself, not the underlying operating system. It will generally work with any MongoDB installation regardless of the operating system as long as authentication has been enabled.

How MongoDB Controls Access with Role-Based Access Control

Access control — also known as authorization — is a security technique that involves determining who can gain access to which resources.

To better understand access control in MongoDB, it can be helpful to first distinguish it from a different but closely related concept: authentication. Authentication is the process of confirming whether a user or client is actually who they claim to be. Authorization, on the other hand, involves setting rules for a given user or group of users to define what actions they can perform and which resources they can access.

The following subsections expand on how MongoDB handles authentication and authorization.

Authentication in MongoDB

In many database management systems, users are identified with just a username and password pair. When connecting to a database with valid credentials, the user is authenticated and granted the level of access associated with that user. In such an approach, the user directory is flat, which means that for the entire database server each username must be unique.

In contrast, MongoDB employs a more complex user directory structure. In MongoDB users are not only identified by their usernames, but also by the database in which they were created. For each user, the database in which they were created is known as that user's authentication database.

This means that in MongoDB it's possible to have multiple users with the same username (sammy, for example) as long as they are created in different authentication databases. To authenticate as a user, you must provide not only a username and password, but also the name of the authentication database associated with that user.

One might assume that users created in a given authentication database would have access privileges available only to that particular database, but this is not the case. Each user, no matter which authentication database it was created in, can have privileges assigned across different databases.

In MongoDB, you control who has access to what resources on a database and to which degree through a mechanism called Role-Based Access Control, often shortened as RBAC.

In Role-Based Access Control, users are not given permissions to perform actions on resources directly, such as inserting a new document into the database or querying a particular collection. This would make the security policies difficult to manage and keep consistent with many users in the system. Instead, the rules allowing actions on particular resources are assigned to roles.

It can be helpful to think of a role as one of a given user's jobs or responsibilities. For example, it might make sense for a manager to have read and write access to every document in a company's MongoDB instance, whereas a sales analyst might have a read-only access only to sales records.

Roles are defined with a set of one or more privileges. Each privilege consists of an action (such as creating new documents, retrieving data from a document, or creating and deleting users) and the resource on which that action can be performed (such as a database named reports or a collection called orders). Just like in real life, where a company may have many sales analysts and employees having more than one responsibility, in MongoDB many users can be assigned to the same role and a single user can have many roles granted.

Roles are identified with the combination of the role name and the database, as each role — except those created in the admin database — can only include privileges applying to its own database. By granting a user roles defined in a database other than their authentication database, a user can be given permissions to act on more than one database. Roles can be granted when you create a user or any time after then. Revoking role membership can also be done at will, making it straightforward to decouple user management from access rights management.

MongoDB provides a set of built-in roles describing privileges commonly used in database systems, such as read to grant read-only access, readWrite to grant both read and write permissions, or dbOwner to grant full administrative privileges over a given database. For more specific scenarios, user-defined roles can be also created with custom sets of privileges.

Note: You can find detailed information on all built-in roles provided by MongoDB on the Built-in roles page in the official MongoDB documentation.

Role-Based Access Control makes it possible to assign users only the minimum, precise level of access permissions they need to work on their respective tasks. This is an important security practice known as the principle of least privilege.

By following along with this guide, you will build an example database environment, create a few sample databases and users, each having different levels of access granted, showcasing the Role-Based Access Control in action.

Step 1 — Outlining the Example Scenario and Preparing the Sample Databases

To explain how Role-Based Access Control — RBAC, for short — works in practice, this guide follows an example scenario with an imaginary sales company called Sammy Sales that uses two databases.

The first database (called sales) will store data about customer orders in the company shop with two separate collections: customers for their customers' personal data and orders for order details.

The second database (this one called reports) will store aggregated reports on monthly sales. This database will contain a single collection named reports.

The company has only two employees, both of whom have a level of database access that follows least privilege approach:

  • Sammy, a sales representative, needs full access to both collections in the sales database, but has no need for working with the reports database.
  • Joe, a sales analyst, needs write access to the reports database to construct reports as well as read-only access to the sales database to retrieve the data.

The requirements are illustrated on the following diagram:

The levels of access required for Sammy and Joe

To create these sample databases, open the MongoDB shell on the server where you installed MongoDB with a command like the following, making sure you authenticate as your administrative user in the process. This example follows the conventions established in the prerequisite How To Secure MongoDB on Ubuntu 20.04 tutorial, in which the administrative MongoDB user is named AdminSammy. Be sure to replace AdminSammy with your own administrative user's username if different:

                      
  • mongo -u AdminSammy -p --authenticationDatabase admin

When prompted, enter the password that you set during installation to get access to the shell.

You can verify that you have access to the entire MongoDB instance by issuing the show dbs command:

                      
  • show dbs

This will return a list of all the databases currently available:

                      

Output

admin 0.000GB config 0.000GB local 0.000GB

After confirming that you can access these databases, switch to the sales database:

                      
  • use sales

The shell will reply with a short confirmation

                      

Output

switched to db sales

In MongoDB, there is there is no explicit action to create a database. A database is only created when it stores at least one document. With this in mind, you will need to insert some sample documents to prepare the databases and collections used in examples throughout this guide.

You are now in the sales database, but it won't actually exist until you insert something into it.

Create a collection named customers within sales and simultaneously insert a new document into it with the following operation:

                      
  • db.customers.insert({name: 'Sammy'})

This example document only contains a name field with the value 'Sammy'. Note that the data itself is not relevant for showcasing how the access rights work in practice, so this step outlines how to create database documents that only contain example mock data.

MongoDB will confirm the insertion with:

                      

Output

WriteResult({ "nInserted" : 1 })

Repeat this process, but this time create a collection named orders. To do this, run the following command. This time, the document's only field is total and it has a value of 100:

                      
  • db.orders.insert({total: 100})

MongoDB will once again confirm that the document was properly inserted.

Since you will be working with two databases, you will need to prepare the reports database as well. To do so, first switch to the reports database:

                      
  • use reports

And insert another document, this time into reports collection:

                      
  • db.reports.insert({orders: 1})

To confirm that both databases have been properly prepared, issue the show dbs command once again.

                      
  • show dbs

After running this command a second time, the result will show two new entries for newly-created databases. These databases only persisted after you created the first documents within each of them:

                      

Output

admin 0.000GB config 0.000GB local 0.000GB reports 0.000GB sales 0.000GB

The sample databases are now ready. Now, you can create a pair of users who will have the least amount of access privileges to the newly-created databases needed for this example scenario.

Step 2 — Creating the First User

In this step, you'll create the first of two MongoDB users. This first user will be for Sammy, the company's sales representative. This account will need full access to the sales database, but no access whatsoever to the reports database.

For this, we'll use the built-in readWrite role to grant both read and write access to the resources in the sales database. Since Sammy is a sales representative, we'll also use the sales database as the authentication database for the newly-created user.

First, switch to the sales database:

                      
  • use sales

The shell will return a confirmation that you're using the chosen database:

                      

Output

switched to db sales

Because Sammy works in the sales department, their MongoDB user account will be created with sales as the authentication database.

Run the following method to create the sammy user:

                      
  • db.createUser(
  • {
  • user: "sammy",
  • pwd: passwordPrompt(),
  • roles: [
  • { role: "readWrite", db: "sales" }
  • ]
  • }
  • )

This createUser method includes the following objects:

  • user represents the username, which is sammy in this example.
  • pwd represents the password. By using passwordPrompt() you will ensure that MongoDB shell will ask for the password when executing the command to be entered.
  • roles is the list of roles granted. This example assigns sammy the readWrite role, granting them read and write access to the sales database. No other roles are assigned to sammy now, which means this user will not have any additional access rights immediately after creation.

Note: As mentioned in the prerequisite tutorial on How To Secure MongoDB, the passwordPrompt() method is only compatible with MongoDB versions 4.2 and newer. If you're using an older version of Mongo, then you will have to write out this user's password in cleartext, similarly to how you wrote out the username:

                              
  • . . .
  • pwd: "password",
  • . . .

If this method is successful, it will return a confirmation message from the MongoDB shell similar to the following:

                      

Output

Successfully added user: { "user" : "sammy", "roles" : [ { "role" : "readWrite", "db" : "sales" } ] }

You can now verify that the new user can log in to the database and whether their access rights you specified are properly enforced.

You will keep the current MongoDB shell with your administrative user logged in open for later, so open a separate server session.

From the new server session, open the MongoDB shell. This time, specify specify sammy as the user and sales as the authentication database:

                      
  • mongo -u sammy -p --authenticationDatabase sales

Enter the password you set when creating the sammy user. After accessing the shell prompt, execute the show dbs command to list the available databases:

                      
  • show dbs

In contrast with your administrative account, only one database will be listed for sammy, as you've only granted them access to the sales database.

                      

Output

sales 0.000GB

Now check whether sammy can retrieve objects from both collections in the sales database. Switch to the sales database:

                      
  • use sales

Then try to retrieve all customers:

                      
  • db.customers.find()

This find command will return the document you created in this collection in Step 1:

                      

Output

{ "_id" : ObjectId("60d888946ae8ac2c9120ec40"), "name" : "Sammy" }

You can also confirm that the second collection, orders, is available as intended:

                      
  • db.orders.find()
                      

Output

{ "_id" : ObjectId("60d890730d31cc50dedea6ff"), "total" : 100 }

To make sure the access rights for sales database have been properly configured, you can check whether sammy can insert new documents as well. Try inserting a new customer:

                      
  • db.customers.insert({name: 'Ellie'})

Because you granted sammy the readWrite role, they are authorized to write new documents to this database. MongoDB will confirm the insertion was completed successfully:

                      

Output

WriteResult({ "nInserted" : 1 })

Lastly, verify whether sammy can access the reports database. They will not be able to read or write any data in this database, as you did not grant them access through the assigned roles.

Switch to the reports database:

                      
  • use reports

This use command will not result in any error on its own. Try accessing the document you inserted in Step 1 by running the following:

                      
  • db.reports.find()

Now, MongoDB will throw an error message instead of returning any objects:

                      

Output

Error: error: { "ok" : 0, "errmsg" : "not authorized on reports to execute command { find: \"reports\", filter: {}, lsid: { id: UUID(\"cca9e905-89f8-4903-ae12-46f23b43b967\") }, $db: \"reports\" }", "code" : 13, "codeName" : "Unauthorized" }

The Unauthorized error message tells you that sammy does not have enough access rights to interact with the data in the reports database.

So far, you've created the first database user with limited privileges and verified the access rights are properly enforced. Next, you will create a second user with different privileges.

Step 3 — Creating the Second User

Having created the sammy MongoDB user for Sammy, the sales representative, you still need an account for Joe, the company's sales analyst. Recall from the example scenario that Joe's job function requires a different set of privileges for the databases.

The process of creating this new user account is similar to the process you followed to create the sammy user.

Return to the server session where your administrative user is logged in to the MongoDB shell. From there, switch to reports database:

                      
  • use reports

Because Joe works in the reporting department, their MongoDB user account will be created with reports as the authentication database.

Create the new joe user with the following command:

                      
  • db.createUser(
  • {
  • user: "joe",
  • pwd: passwordPrompt(),
  • roles: [
  • { role: "readWrite", db: "reports" },
  • { role: "read", db: "sales" }
  • ]
  • }
  • )

Notice the differences between the method used to create joe and the one used to create sammy in the previous step. This time, you assign two separate roles:

  • readWrite applied to the reports database means joe will be able to read and write sales report data to this database
  • read applied to the sales database makes sure that joe can access the sales data, but will not be able to write any documents into that database

Both of these roles are built-in MongoDB roles.

This command will return a confirmation message similar to the following:

                      

Output

Successfully added user: { "user" : "joe", "roles" : [ { "role" : "readWrite", "db" : "reports" }, { "role" : "read", "db" : "sales" } ] }

Next, verify that the new user's permissions are being properly enforced.

Once again, open another server session, as you'll make use of both the administrative MongoDB user and the sammy user in a later step.

Open the MongoDB shell, this time specifying joe as the user and reports as the authentication database:

                      
  • mongo -u joe -p --authenticationDatabase reports

When prompted, enter the password you set when creating the joe user. Once you have access to the shell prompt, execute the show dbs command to list the databases available to joe:

                      
  • show dbs

Since joe can use both the sales and reports databases, those two databases will be listed in the output:

                      

Output

reports 0.000GB sales 0.000GB

Now you can check whether joe can retrieve objects from the sales database.

Switch to sales:

                      
  • use sales

Run the following find command to try to retrieve all orders:

                      
  • db.orders.find()

Assuming you set up permissions correctly, this command will return the lone document you created in this collection in Step 1:

                      

Output

{ "_id" : ObjectId("60d890730d31cc50dedea6ff"), "total" : 100 }

Next, try inserting a new document into the orders collection:

                      
  • db.orders.insert({total: 50})

Because you assigned joe only a read role for this database, this insert command will fail with an error message:

                      

Output

WriteCommandError({ "ok" : 0, "errmsg" : "not authorized on sales to execute command { insert: \"orders\", ordered: true, lsid: { id: UUID(\"ebbe853b-e269-463f-a1d4-2c5a5accb966\") }, $db: \"sales\" }", "code" : 13, "codeName" : "Unauthorized" })

The Unauthorized message tells you the reason behind the failure — the access rights joe has are not enough to insert a new document.

Next, confirm whether joe can read and write data in the reports database.

Switch to reports:

                      
  • use reports

Then, try accessing the data within it using the find command:

                      
  • db.reports.find()

Since joe is allowed to read from the database, MongoDB will respond with the list of currently available documents in this collection:

                      

Output

{ "_id" : ObjectId("60d8897d6ae8ac2c9120ec41"), "orders" : 1 }

Then try inserting a new report by running the following command:

                      
  • db.reports.insert({orders: 2})

This command will also succeed with the output message similar to this:

                      

Output

WriteResult({ "nInserted" : 1 })

With that, you've created the second database user with limited privileges, but this time you granted them roles to two separate databases. You also verified that their access rights are properly enforced by the database server. Next, you will grant and then revoke additional permissions to one of the existing users.

Step 4 — Granting and Revoking Roles for Existing Users

In steps 2 and 3, you created new users and assigned them to roles during creation process. In practice, database administrators often need to revoke or grant new privileges to users that have already been created in the system. In this step, you will grant the sammy user a new role to allow them to access the reports database and revoke that permission shortly afterwards.

From the administrative shell, switch to the sales database where the user sammy was created:

                      
  • use sales

To verify the user sammy is there, execute the show users command:

                      
  • show users

This command will return a list of all the users in this database as well as their respective roles:

                      

Output

{ "_id" : "sales.sammy", "userId" : UUID("cbc8ac18-37d8-4531-a52b-e7574044abcd"), "user" : "sammy", "db" : "sales", "roles" : [ { "role" : "readWrite", "db" : "sales" } ], "mechanisms" : [ "SCRAM-SHA-1", "SCRAM-SHA-256" ] }

To assign a new role to this user, you can use the grantRolesToUser method. This method accepts the user's name and the list of roles to be granted in the same syntax as used when creating a new user.

The goal in this step is to grant sammy read-only permissions for reports database, so assign them the read role for that database:

                      
  • db.grantRolesToUser("sammy", [{role: "read", db: "reports"}])

The command yields no output unless there was an error, so the lack of any message is an expected behavior. You can verify the command has taken effect by executing the show users command again:

                      
  • show users

This command will return output similar to the following:

                      

Output

{ "_id" : "sales.sammy", "userId" : UUID("cbc8ac18-37d8-4531-a52b-e7574044abcd"), "user" : "sammy", "db" : "sales", "roles" : [ { "role" : "read", "db" : "reports" }, { "role" : "readWrite", "db" : "sales" } ], "mechanisms" : [ "SCRAM-SHA-1", "SCRAM-SHA-256" ] }

Notice the newly-added role in the roles section.

Now you can check whether sammy is indeed able to access the reports database after the change. Switch to the terminal window with sammy logged in and try accessing the reports once again.

Switch to the reports database:

                      
  • use reports

And then run the find command on the reports collection:

                      
  • db.reports.find()

Last time, the command failed with an error message. This time, though, it will return the list of documents in the reports database:

                      

Output

{ "_id" : ObjectId("60d8897d6ae8ac2c9120ec41"), "orders" : 1 } { "_id" : ObjectId("60d899cafe3d26bf80e947fd"), "orders" : 2 }

After some time you might want to revoke the sammy user's ability to access the reports. To illustrate this, go back to the administrative console and execute the following command, which will revoke the sammy user's read privileges on the reports database:

                      
  • db.revokeRolesFromUser("sammy", [{role: "read", db: "reports"}])

The revokeRolesFromUser method takes the same set of arguments as grantRolesToUser, but instead removes the specified roles.

Once again, you can verify that the role is no longer available with show users:

                      

Output

{ "_id" : "sales.sammy", "userId" : UUID("cbc8ac18-37d8-4531-a52b-e7574044abcd"), "user" : "sammy", "db" : "sales", "roles" : [ { "role" : "readWrite", "db" : "sales" } ], "mechanisms" : [ "SCRAM-SHA-1", "SCRAM-SHA-256" ] }

To double check that sammy can no longer read from the reports database, try re-running the previous find command in the shell with sammy logged in:

                      
  • db.reports.find()

This time the command will once again fail with an Unauthorized error:

                      

Output

Error: error: { "ok" : 0, "errmsg" : "not authorized on reports to execute command { find: \"reports\", filter: {}, lsid: { id: UUID(\"2c86fba2-7615-40ae-9c3b-2dfdac2ed288\") }, $db: \"reports\" }", "code" : 13, "codeName" : "Unauthorized" }

This shows that your revocation of the sammy user's read role was successful.

Conclusion

In this article, you learned how to create users with limited access to the database and use the Role-Based Access Control to enforce the least privilege principle on your database, granting only the minimal necessary set of privileges to users. You also learned how to grant and revoke roles from existing users, learning how to manage access rights on a living database server when the necessary permissions change over time and verify that the changes take effect immediately.

With MongoDB's Role-Based Access Control you can define precise access levels using features such as User-Defined Roles, create custom roles when built-in ones are not satisfactory, and Collection-Level Access Control that allows administrators to grant users privileges to specific collections rather than whole databases. We encourage you to learn more about these and other Role-Based Access Control features described in detail in the official MongoDB documentation.

Mongodb Create Read Only User for All Databases

Source: https://www.digitalocean.com/community/tutorials/how-to-use-mongodb-access-control