Introduction to the

MongoDB, also known as _Mongo_, is an open source document database used in many modern web applications. It is classified as a NoSQL database because it does not rely on the relational database model. Instead, it uses JSON-like documents and has dynamic modes. This means that unlike relational databases, MongoDB does not require a predefined schema before adding data to the database.

When you use multiple distributed MongoDB instances, such as in the case of replica sets or shard database architectures, it is important to ensure secure communication between them. One way to do this is through key file authentication. This involves creating a special file that is essentially the shared password for each member of the cluster.

This tutorial Outlines how to update an existing replica set to use key file authentication. The programs covered by this guide will also ensure that the replica set does not experience any downtime, so that the data within the replica set remains available to any customers or applications that need to access it.

The premise condition

To complete this tutorial, you will need.

  • Three servers, one running Ubuntu 20.04. All three servers should have a non-root user for administration and a firewall configured with the UFW. To set this up, follow our Initial server setup guide for Ubuntu 20.04.
  • Install MongoDB on each of your Ubuntu servers. Follow our tutorial on how to install MongoDB on Ubuntu 20.04 and make sure you complete each step on each of your servers.
  • Your three MongoDB installations are configured as a replica set. Follow this tutorial: How to configure a MongoDB replica set on Ubuntu 20.04.
  • Generate SSH keys for each server. In addition, you should make sure that each server has the public keys of the other two servers added to itauthorized_keysFile. This is to ensure that each machine can communicate with each other over SSH, which makes it easier to distribute key files to each machine in Step 2. To set these up, follow our guidelines:How do I set SSH Keys on Ubuntu 20.04.

Note that for clarity, this guide will follow the convention in the prerequisite replica set tutorial and call the three servers Mongo0, Mongo1, and Mongo2. It also assumes that you have completed Step 1 of the guide and configured the hosts file for each server to resolve the following hostname to the IP address of a particular server.

The host name Resolve to
mongo0.replset.member mongo0
mongo1.replset.member mongo1
mongo2.replset.member mongo2

In this guide, there are cases where you have to run a command or update a file on one of these servers. In this case, this guide will use Mongo0 by default in the examples, and will be represented by displaying commands or file changes on a blue background, like this.

Any command or file modification that must be run on multiple servers will have a standard gray background, like this.

About key file authentication

In MongoDB, key file authentication relies on salting Challenge Response Authentication mechanism (SCRAM), which is the default authentication mechanism of database system. SCRAM involves MongoDB reading and validating a combination of user-submitted credentials with their username, password, and authentication database, all of which are known to a given MongoDB instance. This is the same authentication mechanism for a user who provides a password when connecting to the database.

In key file authentication, the key file serves as the shared password for each member of the cluster. A key file must contain between 6 and 1024 characters. The key file can contain only base64 characters, and note that MongoDB strips whitespace characters when reading the key. Starting with version 4.2 of Mongo, KeyFiles uses the YAML format, which allows you to share multiple keys in a keyfile.

Warning. The community version of MongoDB comes with two authentication methods to help keep your database secure, namely _ key file authentication _ and _X.509 authentication _. For production deployments with replication, the MongoDB documentation recommends x.509 authentication, which describes key files as a “minimal form of security” that is “best suited for test or development environments.”

The process of obtaining and configuring X.509 certificates has a number of considerations that must be decided on a case-by-case basis, which means that the process is beyond the scope of the DigitalOcean tutorial. If you plan to use replica sets in a production environment, we strongly recommend that you consult the official MongoDB documentation on X.509 authentication.

If you plan to use your replica set for testing or development, you can follow along with this tutorial to add a layer of security to your cluster.

Step 1 – Create a user administrator

When you enable authentication in MongoDB, it will also enable _ role-based access control _ for replica sets. According to MongoDB documentation.

MongoDB uses role-based access Control (RBAC) to manage access to MongoDB systems. A user is granted one or more roles that determine the user’s access to database resources and operations.

When access control is enabled on MongoDB instances, it means that you will not be able to access any resources on the system unless you are authenticated as a valid MongoDB user. Even so, you must authenticate as a user with appropriate permissions to access specific resources.

If you do not create a user for your MongoDB system before enabling key file authentication (and hence access control), you will not be locked out of the replica set. You can create a MongoDB user that you can use to validate the group, via Mongo’s localhost exception if necessary. This is a special exception that MongoDB makes for configurations where access control is enabled but users are lacking. This exception only allows you to connect to a database on the localhost and then create a user in the admin database.

However, relying on the localhost exception to create MongoDB users after authentication is enabled means that your replication set will experience some downtime, as replication will not be able to verify connections until after you have created the user. This step Outlines how to create a user _ before _ enable authentication _ to ensure that your replica set remains available. This user will have permission to create other users on the database, giving you the freedom to create other users in the future and have any permissions they need. In MongoDB, users with this permission are called _ user administrator _.

First, connect to the primary members of your replica set. If you are not sure which member is primary, you can run the rs.status() method to identify it.

Run the following mongo command at bash prompt on any Ubuntu server hosting the MongoDB instance in the copy group. The –eval option of this command instructs the Mongo operation not to open the shell interface environment that you see when you run Mongo yourself, but to run the single-quoted command or method that follows the –eval argument.

mongo --eval 'rs.status()'
Copy the code

Rs.status () returns a lot of information, but the relevant part of the output is “members” : arrays. In the context of MongoDB, an array is a collection of files between square brackets ([and]).

In the “members”: array, you’ll find files, each containing information about a member of your replica set. In each of these members’ files, find the “stateStr” field. The member whose stateStr” value is “PRIMARY” is the PRIMARY member of your replicated set. The following example shows the case where Mongo0 is the main member.

Output. . .
    "members" : [
        {
            "_id" : 0,
            "name" : "mongo0.replset.member:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
. . .
        },
. . .
Copy the code

Once you know which replica set member is primary, SSH goes into the server hosting that instance. For demonstration purposes, this guide will continue to use Mongo0 as the master instance example.

ssh sammy@mongo0_ip_address
Copy the code

Once logged in to the server, connect to MongoDB by opening the Mongo shell environment.

mongo
Copy the code

When creating users in MongoDB, you must create them in a specific database that will be used as their _ authentication database _. A combination of the user’s name and their authentication database serves as the user’s unique identifier.

Some administrative actions only apply to users whose authentication database is an Admin database — this is a special privileged database included with every MongoDB installation, including the ability to create new users. Because the goal of this step is to create a user administrator that can create other users in the replicate set, connect to the Admin database so that you can grant the appropriate permissions to this user.

use admin
Copy the code
Outputswitched to db admin
Copy the code

MongoDB installs shell methods based on JavaScript that you can use to manage your database. One of them, the db.createUser method, is used to create a new user in the database where the method is running.

Start the db.createUser method.

db.createUser(
Copy the code

Note: Mongo does not register the db.createUser method as done until you enter a closed parenthesis. Before you do this, the hint will change from a greater-than sign (>) to an ellipsis (…). .

This method requires you to specify a username and password for the user, as well as any role you want the user to have. To review, MongoDB stores data in jSON-like documents; When you create a new user, all you do is create a document to hold the appropriate user data as separate fields.

Like objects in JSON, documents in MongoDB begin and end with curly braces ({and}). Enter a opening brace to start the user document.

{
Copy the code

Next, enter a user: field with the user name you want as the value in double quotes, followed by a comma. The following example specifies the user name UserAdminSammy, but you can enter any user name you like.

user: "UserAdminSammy".Copy the code

Next, enter a PWD field with the passwordPrompt() method as its value. When you execute the db.createUser method, the passwordPrompt() method will provide a prompt for you to enter your password. This is more secure than the other methods because the password is entered in clear text, just as you would enter a user name.

Note: the passwordPrompt() method is only compatible with MongoDB4.2 and later versions. If you are using an older version of Mongo, you will have to enter your password in plain text, just as you would enter your user name.

pwd: "password".Copy the code

Make sure there is a comma after this field as well.

pwd: passwordPrompt(),
Copy the code

Then enter a roles field, followed by an array that specifies the roles you want your admin user to have. In MongoDB, _ role _ defines what actions users can perform on resources they have access to. You can define your own custom roles, but Mongo also has some built-in roles that grant common permissions.

Since you are creating a user administrator, you should at least grant them the built-in userAdminAnyDatabase role for the Admin database. This will allow user administrators to create and modify new users and roles. Because the administrative user has this role in the admin database, this also grants it superuser rights over the entire cluster.

roles: [ { role: "userAdminAnyDatabase", db: "admin"}]Copy the code

After this, enter a closing bracket to indicate the end of the file.

}
Copy the code

Then enter a closing brace to close and execute the db.createUser method.

)
Copy the code

In general, your db.createUser method should look like this.

> db.createUser( ... {... user: "UserAdminSammy", ... pwd: passwordPrompt(), ... roles: [ { role: "userAdminAnyDatabase", db: "admin" } ] ... }...).Copy the code

If each line is syntactically correct, the method will execute normally and prompt you for a password.

OutputEnter password:
Copy the code

Enter a strong password of your choice. You will then receive a confirmation that the user has been added.

OutputSuccessfully added user: {
    "user" : "UserAdminSammy",
    "roles" : [
        {
            "role" : "userAdminAnyDatabase",
            "db" : "admin"
        },
        "readWriteAnyDatabase"
    ]
}
Copy the code

That’s it. You’ve added a MongoDB user profile that you can use to manage other users and roles on your system. You can test this by creating another user, as described in the rest of this step.

First, authenticate as the user administrator you just created.

db.auth( "UserAdminSammy", passwordPrompt() )
Copy the code

Db.auth (), which returns 1 if authentication succeeds.

Output1
Copy the code

Note: In the future, if you want to authenticate as an administrator when connecting to a cluster, you can do so directly from your server prompt with commands like the following.

mongo -u "UserAdminSammy" -p --authenticationDatabase "admin"
Copy the code

In this command, the -u option tells the shell that the following arguments are the user names you want to authenticate. The -p flag tells it to prompt you for a password, with the –authenticationDatabase option before the name of the user’s authenticationDatabase. If you enter an incorrect password, or if the username does not match the authentication database, you will not be able to authenticate and you will have to try to connect again.

Also, note that in order for you, as user administrator, to create new users in the replicate set, you must connect to the primary member of the replicate set.

The procedure for adding another user is the same as the procedure for the user administrator. The following example creates a new user with a clusterAdmin role, which means they will be able to perform some operations related to replication and sharding. Within MongoDB, users with these permissions are called _ cluster administrator _.

Having a dedicated user to perform such a specific function is a good security practice because it limits the number of privileged users you can have on the system. After key file authentication is enabled later in this tutorial, any client that wants to perform any of the operations allowed by the clusterAdmin role — for example, any RS. Methods, such as rs.status() or rs.conf() — must first be authenticated as the cluster administrator.

That is, you can provide the user with any role you want, as well as a different name and authentication database. However, if you want this new user to function as a cluster administrator, you must grant them the clusterAdmin role in the Admin database.

In addition to creating a user to act as the cluster administrator, the following method names the user ClusterAdminSammy and uses the passwordPrompt() method to prompt you for a password.

db.createUser(
{
user: "ClusterAdminSammy".pwd: passwordPrompt(),
roles: [ { role: "clusterAdmin", db: "admin"}]})Copy the code

Also, if you are using MongoDB prior to version 4.2, you must write passwords in clear text instead of using the passwordPrompt() method.

If each line is syntactically correct, the method will execute normally and prompt you for a password.

OutputEnter password:
Copy the code

Enter a strong password of your choice. You will then receive a confirmation that the user has been added.

OutputSuccessfully added user: {
    "user" : "ClusterAdminSammy",
    "roles" : [
        {
            "role" : "clusterAdmin",
            "db" : "admin"
        }
    ]
}
Copy the code

This output confirms that your user administrator can create new users and grant them roles. Now you can close MongoDB shell.

exit
Copy the code

Alternatively, you can close the shell by pressing CTRL + C.

At this point, if you have any customers or applications connected to your MongoDB cluster, this would be a good time to create one or more dedicated users with appropriate roles that they can use to validate the database. Otherwise, read on to learn how to generate a key file, distribute it to the members of the replica set, and then configure each replica set to require the members of the replica set to authenticate with the key file.

Step 2 – Create and distribute the authentication key file

It is helpful to create a directory on each server to store the key files before creating them to keep things in order. Run the following command to create a directory called mongo-Security in the Ubuntu admin user’s home directory and run it on all three servers.

mkdir ~/mongo-security
Copy the code

A key file is then generated on one of the servers. You can do this on any of your servers, but for illustrative purposes, this guide will generate key files on Mongo0.

Navigate to the mongo-Security directory you just created.

cd ~/mongo-security/
Copy the code

In that directory, create a key file with the following openssl command.

openssl rand -base64 768 > keyfile.txt
Copy the code

Notice the arguments to this command.

  • rand: instructs OpenSSL to generate pseudo-random bytes of data
  • -base64: specifies that the command should use base64 encoding to represent pseudo-random data as printable text. This is important because, as mentioned earlier, the MongoDB key file can only contain characters from base64 sets.
  • 768: The number of bytes that the command should generate. In Base64 encoding, three binary bytes of data are represented as four characters. Since MongoDB key files can be up to 1024 characters long, 768 is the maximum number of bytes you can generate for a valid key file

The 768 argument in this command is followed by a greater than sign (>). This will redirect the output of the command to a new file called keyfile.txt, which will serve as your keyfile. Feel free to name the keyfile instead of keyfile.txt if you wish, but be sure to change the file name in future commands.

Next, change the permissions of the key file so that only the owner can read it.

chmod 400 keyfile.txt
Copy the code

After that, distribute the key files to the other two servers that host the MongoDB instances in your replica set. Assuming you follow the prerequisites guide on how to set up an SSH key, you can do this with the SCP command.

scp keyfile.txt [email protected]:/home/sammy/mongo-security
scp keyfile.txt [email protected]:/home/sammy/mongo-security
Copy the code

Note that each of these commands copies the key file directly into the ~/mongo-security/ directory you created earlier on mongo1 and mongo2. Be sure to change Sammy to the name of the Ubuntu admin user profile you create on each server.

Next, change the owner of the file to the mongodb user profile. This is a special user created when you install MongoDB to run the Mongod service. This user must have access to the key file for MongoDB to use for authentication.

Run the following command on each of your servers to change the owner of the key file to the mongodb user account.

sudo chown mongodb:mongodb ~/mongo-security/keyfile.txt
Copy the code

After changing the owner of the key file on each server, you can reconfigure each of your MongoDB instances to perform key file authentication.

Step 3 – Enable key file authentication

Now that you have generated a key file and distributed it to each server in the replica set, you can update the MongoDB configuration file on each server to perform the key file validation.

This step requires reconfiguring the secondary members of the replica set to avoid any downtime when the members of the replica set need to be authenticated. You will then instruct your key member to step down and become a minor member. This will cause the secondary members to hold an election to select a new master member and keep your cluster available to any customers or applications that need access to it. You will then reconfigure the former master node to enable authentication.

Host secondary members of the replica set on each of your servers and open the MongoDB configuration file with your favorite text editor.

sudo nano /etc/mongod.conf
Copy the code

In this file, find the Security section. By default, it looks like this.

/etc/mongod.conf

.#security:.Copy the code

Remove the pound number (#) from this line and uncomment it. Then, on the next line, add a keyFile: directive, followed by the full path to the keyFile you created in the previous step.

/etc/mongod.conf

. . .
security:
  keyFile: /home/sammy/mongo-security/keyfile.txt
. . .
Copy the code

Notice that there are two Spaces at the beginning of this line. These are required to read the configuration file correctly. When you enter this line in your own configuration file, make sure that the path you provide reflects the actual path to the key file on each server.

Below the keyFile directive, add a transitionToAuth directive with a value of true. When set to true, this configuration option allows MongoDB instances to accept both authenticated and unauthenticated connections. This is very useful when reconfiguring the replica set to enforce authentication, as it will ensure that your data is still available when you restart each member of the replica set.

/etc/mongod.conf

. . .
security:
  keyFile: /home/sammy/mongo-security/keyfile.txt
  transitionToAuth: true.Copy the code

Also, make sure you include two whitespace before the transitionToAuth directive.

After making these changes, save and close the file. If you edit it with Nano, you can do this by pressing CTRL + X, Y, and then ENTER.

Then restart the Mongod service ** on the servers of the two secondary instances, ** to make these changes effective immediately.

sudo systemctl restart mongod
Copy the code

In this way, you have configured key file authentication for the secondary members of the replicated set. At this point, both authenticated and unauthenticated users have unrestricted access to these members.

Next, you will repeat the procedure on the main members. Before you can do that, however, you must lower the level of the member so that it is no longer the master member. To do this, open MongoDB’s shell on the server that hosts the primary members. For the sake of illustration, this guide will again assume that this is Mongo0.

mongo
Copy the code

At the prompt, run the rs.stepDown() method. This would instruct major members to become minor members and would cause the current minor members to hold elections to determine who would be the new major members.

rs.stepDown()
Copy the code

If the method returns “OK” : 1 in the output, it means that the junior member has successfully stepped down to become a secondary member.

Output{
    "ok" : 1,
    "$clusterTime" : {
        "clusterTime" : Timestamp(1614795467, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    },
    "operationTime" : Timestamp(1614795467, 1)
}
Copy the code

After removing the master member, you can close the Mongo shell.

exit
Copy the code

Next, open the MongoDB configuration file on this server.

sudo nano /etc/mongod.conf
Copy the code

Find the security section, remove the pound symbol, and uncomment the Security header. Then add the same keyFile and transitionToAuth directives that you added to other MongoDB instances. After making these changes, the Security section will look like this.

/etc/mongod.conf

. . .
security:
  keyFile: /home/sammy/mongo-security/keyfile.txt
  transitionToAuth: true.Copy the code

Again, make sure that the file path after the keyFile instruction reflects the actual location of the keyFile on the server.

When finished, save and close the file. Then restart the Mongod process.

sudo systemctl restart mongod
Copy the code

After that, all of your MongoDB instances will be able to accept both authenticated and unauthenticated connections. In the final step of this tutorial, you will configure your instance to require users to authenticate before performing privileged operations.

Step 4 — NotransitionToAuthRestart each member to enforce authentication

At this point, each of your MongoDB instances is configured to transitionToAuth, set to True. This means that even if you have made each server use the key file you created to authenticate internal connections, they will still be able to accept unauthenticated connections.

To change this and require each member to enforce authentication, reopen the mongod. Conf file on each server.

sudo nano /etc/mongod.conf
Copy the code

Locate the Security section and disable the transitionToAuth directive. You can do this by prefacing the line with a pound symbol.

/etc/mongod.conf

. . .
security:
  keyFile: /home/sammy/mongo-security/keyfile.txt
  #transitionToAuth: true.Copy the code

After disabling the transitionToAuth directive in the configuration file for each instance, save and close each file.

Then, restart the Mongod service on each server.

sudo systemctl restart mongod
Copy the code

After that, each MongoDB instance in your replica set will require you to authenticate to perform privileged operations.

To test this, try running a MongoDB method that works when called by an authenticated user with the appropriate permissions. Try running the following commands from any of the prompts on your Ubuntu server.

mongo --eval 'rs.status()'
Copy the code

Although you successfully ran this method in Step 1, since you have enabled key file authentication, the rs.status() method can now only be run by users granted the clusterAdmin or clusterManager role. Whether you run this command on the server hosting the primary member or on the server hosting the secondary member, it will not work because you are not authenticated.

Output... MongoDB server version: 4.4.4 {"operationTime" : Timestamp(1616184183, 1), "OK" : 0, "errmsg" : "command replSetGetStatus requires authentication", "code" : 13, "codeName" : "Unauthorized", "$clusterTime" : { "clusterTime" : Timestamp(1616184183, 1), "signature" : { "hash" : BinData(0,"huJUmB/lrrxpx9YfnONM4mayJwo="), "keyId" : NumberLong("6941116945081040899") } } }Copy the code

To recall, with access control enabled, all cluster management methods (including Rs., such as Rs.status ()) only work when invoked by an authenticated user granted the appropriate cluster management role. If you have created a cluster administrator — as described in Step 1 — and authenticated as that user, this method will work as expected.

mongo -u "ClusterAdminSammy" -p --authenticationDatabase "admin" --eval 'rs.status()'
Copy the code

After you enter the user password when prompted, you’ll see the output of the rs.status() method.

Output... MongoDB server version: 4.4.4 {"set" : "shard2", "date" : ISODate(" 2021-03-19T20:21:45.528z "), "myState" : 2, "term" : NumberLong(4), "syncSourceHost" : "mongo1.replset.member:27017", "syncSourceId" : 2, "heartbeatIntervalMillis" : NumberLong(2000), "majorityVoteCount" : 2, . . .Copy the code

This confirms that the replica set is being authenticated and that you can successfully authenticate it.

conclusion

By completing this tutorial, you create a key file with OpenSSL, then configure a replica of MongoDB and require its members to use it for internal authentication. You also created a user administrator, which will allow you to manage users and roles in the future. Your replica set will not experience any downtime during the entire process, and your data will remain available to your customers and applications.

If you want to learn more about MongoDB, we encourage you to check out our entire MongoDB content library.