Introduction

This article is about the access control necessary to secure a MongoDB database.

Specifically, it is about those features that we can use to ensure that only authorised users are allowed to access the database.

Each MongoDB user should only be able to access only the data that is required for their role in the organisation, as determined by whoever has the task of managing data security within the organisation.

This is required for good stewardship of the data and compliance with international requirements.

Access Control

Access control ensures that the people accessing the database are positively identified and can access, update, or delete the data that they are entitled to.

This is the topic we will cover in this article. From within the database, we can take care of the authentication of clients and the authorization of the operations they wish to perform.

Authentication

When a client, or a user, reaches the database, the first task is to check whether this user is known or not, and can provide credentials to ensure that they are convincingly identified. This is known as authentication.

With MongoDB we can use one of the following tools to deal with the authentication:

SCRAM. The default authentication mechanism for MongoDB. It verifies the user and password supplied against the user’s name, password and authentication database.

The default authentication mechanism for MongoDB. It verifies the user and password supplied against the user’s name, password and authentication database. x.509 Certificates. Instead of usernames and passwords, this mechanism uses x.509 certificates to authenticate clients against servers or members inside a Replica Set or a Sharded Cluster. Wikipedia says: “An x.509 certificate contains a public key and an identity and is either signed by a certificate authority or self-signed. Someone holding the certificate can rely on the public key it contains to establish secure communications”.

LDAP. This is a protocol whose most common use is to provide a central place to store usernames and passwords allowing different applications to connect to the LDAP server to validate users.

This is a protocol whose most common use is to provide a central place to store usernames and passwords allowing different applications to connect to the LDAP server to validate users. Kerberos. This is an industry standard authentication protocol based on tickets.

External tools are only offered in the MongoDB Enterprise version.

As a best practice, we will create login credentials for each entity that will need to access the database, but only for those. By doing this, we will be able to audit all the activities done by all the users and accomplish GDPR requirements.

Apart from user authentication, you will also need to authenticate servers and network processes.

Yes, in a Replica Set or a Sharded Cluster all the nodes check each other continuously in order to be sure all of them are known (in other words, to confirm their membership), as well as other tasks such as checking the health of each member in order to determine whether the Replica must accomplish a new election.

So what is an election?

In MongoDB, only one node is able to perform writes. When this node is down, or a network partition comes into play, the remaining nodes begin an election in order to choose the new primary node and get the service running without stopping.

Authorization

The administrator of the database is the right person for the task of granting, or denying, permissions to users for doing operations with database resources.

By using roles, we can specify what action can be done with a resource. Therefore, a role is a privilege granted to a user to do specific tasks with specific resources.

Resources ← Actions ← Roles (Privileges) → Users

MongoDB offers built-in roles and also enables you to define new ones depending on the specific requirements for the database. These roles are defined in terms of actions on resources.

Actions are all those kinds of operations we can run against the database such as find, delete, insert, update, or createIndex. A resource can be a collection, a document, an index, a database, and so on.

Through the use of read-only views, administrators get field-level security by restricting access to sensitive data exposing only a subset of it. Permissions granted against views are specified separately from those granted to the underlying collections.

Each role should grant only the necessary permissions for that role, and users should only be assigned roles that are appropriate for their requirements.

Database for Authentication

MongoDB users must identify themselves with the database in which they were originally created. This is usually the admin database but can be any other.

Whichever database the users have been created on, they will be able to take actions on other databases if a suitable role has been granted to the user.

MongoDB Users

Before you enable access control, you should create a user that can then create users and assign roles to them once access control is enabled.

This user-admin will then be used to create and maintain other users and roles, so needs to be assigned a suitable role to enable it to do so.

In case you do not create this user-admin, you will not be able to log in, or create new users and roles when access control is enabled.

Localhost Exception

If you were to enable access control without previously having created at least a user-admin user, then you would not be able to log in.

The localhost exception is here to avoid this problem by allowing you to create the first user after enabling access control.

To do this, you need to:

Enable access control Connect to the localhost interface Create the first user in the admin database that must have enough permissions to manage other users and roles.



This localhost exception applies only when there are still no users created. You must choose between two options: either create your first user before enabling the access control or afterwards via the use of the localhost exception.

How to Enable Access Control

When starting your mongod service you use parameters to specify the characteristics of your database or, better, use a config file.

Either way, you have to use the security options:

security authorization:enabled

This setting enables or disables the Role-Based Access Control.

How to Create a User

Before creating a MongoDB user, it is worth thinking about the tasks the user is going to perform.

Probably, there will be several users with the same level of permissions, so the smartest option is to create a role and assign it to each of these users.

By changing only a role, you will update the permissions of all users who use it. Otherwise, a change to an access requirement for a group or class of user would need to be done for every user.

The first step is to change context to the database in which you are going to create the role:

> use admin

The second step is to execute this command:

> db.createUser({ user : '<userName>', pwd : '<password>', roles : [ { role : '<roleName>', db : '<dbName>' } | '<roleName>', …] })

If you want to create a user without assigning that user any role you only have to specify an empty roles field.

If you’re still getting the hang of the MongoDB query language, there is a way to add or create users in just a few clicks.

How to Drop a User

Assuming that you are logged in with a suitable role that allows you to drop users, you will need to change context to the database where it was created…

> use admin

…and you run the command:

> db.dropUser('<userName>')

Alternatively, you can delete a user in just three clicks.

Where are Users Stored?

To check a user, you must change your context to the database in which the user was created, such as the Admin database.

> use '<dbName>'

You can then use either

> db.system.users.find()

or

> db.getUsers()

But, if you only want to ask for a specific user, use this command:

> db.getUser('<userName>')

How to Login

There are three possible cases, obviously all of them with the same philosophy. Let’s see:

Inside the database

$ mongo > use '<dbName>' > db.auth('<userName>','<password>')

I do not recommend that you use this method because the password is visible while you are typing it.

From the shell

$ mongo --authenticationDatabase <dbName> -u <userName> -p MongoDB shell version v3.6.4-rc0 Enter password: connecting to: mongodb://127.0.0.1:27017 MongoDB server version: 3.6.4-rc0 MongoDB Enterprise >

This is my preferred option.

In this case, if we do not specify the --authenticationDatabase parameter, the database will always try to authenticate the user against the database we are about to be connected to.

If we do not specify a database name to connect to, as I did in the example above, the server will do it to the ‘test’ database.

From a MongoDB client

From a MongoDB client, we must use a connection string like this:

mongo://<userName>:<password>@<hostName>:27017/<dbName>?options

How to Logout

The logout ends the current authentication session. You must do it in the same database that you authenticated to.

> use admin > db.logout()

MongoDB Roles

As you already know, a role is a privilege granted to a user for making actions over resources.

The role defines the tasks that the role member is allowed to do and the resources where those tasks can be done.

MongoDB offers built-in roles for the most common purposes. But, also, allows us to create our own roles depending on our specific needs.

Each role is scoped to the database in which it has been created.

A role can only include privileges that apply to its database and can only inherit from other roles in its database.

A role created in the admin database can include privileges that apply to the admin database, other databases or to the cluster resource, and can inherit from roles in other databases as well as the admin database.

So, if you need to inherit from a role created in another database you will have to create your new role in the admin database.

Where are Roles Stored?

I have explained before that you can create roles in the admin database or in any other.

Therefore, if you want to check them, you must do it in the database where they were defined:

> use '<dbName>'

to get all the roles of a database, use

> db.system.roles.find()

or

> db.getRoles()

If you only want to ask for a specific role, you will use this command:

> use '<dbName>' > db.getRole('<roleName>')

Built-in Roles

MongoDB classifies the built-in roles as

Let’s see each of them in detail.

Database User Roles

The roles available at the database level are:

read – Read data on all non-system collections

readWrite – Include all ‘read’ role privileges and the ability to write data on all non-system collections

Database Administration Roles

The database administration roles we can use are the following:

dbAdmin – Grant privileges to perform administrative tasks

userAdmin – Allows you to create and modify users and roles on the current database

dbOwner – This role combines the following: readWrite dbAdmin userAdmin



Cluster Administration Roles

Roles at the admin database for administering the whole system.

clusterMonitor – Provides read-only access to monitoring tools

clusterManager – For management and monitoring actions on the cluster

hostManager – To monitor and manage servers

clusterAdmin – Combines the other three roles plus dropDatabase action

Backup and Restoration Roles

This role belongs to the admin database.

backup – Provides the privileges needed for backing up data

restore – Provides the privileges needed to restore data from backups

All-Database Roles

These roles lie on the admin database and provide privileges which apply to all databases.

readAnyDatabase – The same as ‘read’ role but applies to all databases

readWriteAnyDatabase – The same as ‘readWrite’ role but applies to all databases

userAdminAnyDatabase – The same as ‘userAdmin’ role but applies to all databases

dbAdminAnyDatabase – The same as ‘dbAdmin’ role but applies to all databases

Superuser Roles

The following roles are not superuser roles directly but are able to assign any user any privilege on any database, also themselves.

userAdmin

dbOwner

userAdminAnyDatabase

The root role provides full privileges on all resources:

root

How to Check the Privileges of a Role

If you need to know the privileges (inherited from other roles or not) of a role, you can activate the ‘showPrivileges’ field:

> use '<dbName>' > db.getRole('<roleName>', { showPrivileges : true })

Summary of Roles

Whoever is administering the MongoDB estate must find the most suitable roles for his own use-cases.

In my opinion, the following roles are typically the most useful:

userAdminAnyDatabase

clusterManager

clusterMonitor

backup

restore

dbAdmin

readWrite

read

How to Grant a Role to a User

You can grant a role as you create the user, or retrospectively.

The next command is valid for assigning a role at the same time you create the user:

> use '<dbName>' > db.createUser( { user: "<userName>", pwd: "<password>", roles: [ { role: "<roleName>", db: "<dbName>" } ] })

And you can use this command to do it later:

> use '<dbName>' > db.grantRolesToUser( '<userName>', [ { role : '<roleName>', db : '<dbName>' }, '<roleName>', … ] )

How to Revoke a Role from a User

> use '<dbName>' > db.revokeRolesFromUser( '<userName>', [ { role : '<roleName>', db : '<dbname>' } | '<roleName>' ] )

User-defined Roles

How to Create a Role

> use '<dbName>' > db.createRole({ role: "<roleName>", privileges: [ { resource: { db : “<dbName>”, collection : “<collectionName>” }, actions: [ '<actionName>' ] } ], roles: [ { role : '<fatherRoleName>', db : '<dbName>'} | '<roleName>' ] })

Want to skip the mongo shell? Here’s how you can create a role using a GUI instead.

How to Drop a Role

> use '<dbName>' > db.dropRole('<roleName>')

How to Grant or Revoke Privileges to/from a Role

These commands grant or revoke privileges to a user-defined role.

> use '<dbName>' > db.grantPrivilegesToRole( '<roleName>', [ { resource : { db : '<dbName>', collection : '<collectionName'> }, actions : [ '<actionName>',... ] }, ... ] )

> db.revokePrivilegesFromRole( '<roleName>', [ { resource : { db : '<dbName>', collection : '<collectionName'> }, actions : [ '<actionName>',... ] }, … ] )

How to Grant or Revoke Roles to/from a Role

> use '<dbName>' > db.grantRolesToRole( '<roleName>', [ { role : '<roleName>', db : '<dbName>' } | <roles> ] )

> db.revokeRolesFromRole( '<roleName>', [ { role : '<roleName>', db : '<dbName>' } | <roles> ] )

Be careful! As the documentation says: “An update to the privileges or roles array completely replaces the previous array’s values”.

> use '<dbName>' > db.updateRole( '<roleName>', { privileges : [ { resource : { db : '<dbName>', collection : '<collectionName>' }, actions : [ '<actionName>' ] },... ], roles : [ { role : '<roleName>', db : '<dbName>' } | '<roleName>' ] } )