Maintaining a secure MongoDB setup requires administrators to implement controls that ensure users and applications may only access the data they need. Fortunately, MongoDB provides features to implement these controls and restrictions for any MongoDB setup.
If you’re coming from a development environment, there’s something that needs to be really clear in your mind:
developer friendly != production ready
There is a whole raft of measures that can be used to make your production environment more secure such as:
- Enabling access control and enforce authentication,
- Configuring role-based access control,
- Encrypting communication,
- Limiting network exposure,
- Auditing system activity,
- Encrypting and protect data,
- Running MongoDB with a dedicated user, and
- Running MongoDB with secure configuration options.
We’re going to look at the first two in this article. We could consider these as being ‘minimum best practice’.
Enable Access Control and Enforce Authentication
First a couple of concepts need to be explained.
Authentication is the process of verifying the identity of a database client. It answers the question “Who are you?” When access control is enabled, MongoDB requires all clients to identify themselves in order to determine whether they have access at all.
Although authentication and authorization are closely connected, authentication is distinct from authorization. Authorization determines the verified user’s access to resources and operations. It answers the question “OK, I know you, but what are you permitted to do here?”
Enable access control and specify the authentication method. You can use the default MongoDB authentication method or an existing external framework. In clustered setups, you have to enable authentication for each and every MongoDB server.
Configure Role-Based Access Control
Create a user administrator first, then create additional roles and users. Create a unique MongoDB user for each person and application that accesses the system.
Create roles that define the exact access a set of users’ needs. Follow a principle of least privilege. In other words, only give as much away has you have to and no more. Then create users and assign them only the roles they need to perform their operations. A user can be a real person or a client application.
A Worked Example
Create an Administrator User
Enabling access control requires authentication of every user. Once authenticated, users only have the privileges as defined in the roles granted to the users.
So before enabling access control, we need to create an ‘admin’ user having been granted the userAdminAnyDatabase role.
To disable access control in the first instance, we configure /etc/mongod.conf to have authorization disabled:
security: authorization: disabled
Alternatively, if you find the security setting commented out, authorization will be disable by default:
#security:
Start MongoDB without access control:
sudo service mongod start
Connect a mongo shell to the instance:
mongo
Create the admin user, or whatever name you prefer, on the admin database:
use admin db.createUser( { user: "admin", pwd: " changeMe", roles: [ "userAdminAnyDatabase" "root" "dbAdminAnyDatabase" "readWriteAnyDatabase" ] } )
Check that you can connect as the newly created user. Sometimes this fails, as shown below. If that’s your case, just change the user password [to the same password] and you should be able connect:
> db.auth("admin", "changeMe") Error: Authentication failed. 0 > db.changeUserPassword("admin", "changeMe") > db.auth("admin", "changeMe") 1
Stop the service:
sudo service mongod stop
Enable Client Access Control
To enable access control, we edit /etc/mongod.conf to have authorization enabled:
security: authorization: enabled
Restart the service:
sudo service mongod start
Connect a mongo shell to the instance and check that you can login using the newly created administrator:
mongo use admin db.auth(“admin”, “changeMe”)
Create Roles
Create a reader role, and a writer role in the test database. This example is using the restaurants collection which is part of the Getting Started Guide. In this demo, we are going to grant read and write permissions to this single collection:
use test db.createRole( { role: “test.reader”, privileges: [ { resource: { db: “test”, collection: “restaurants” }, actions: [ “find”, “listCollections” ] } ], roles: [] } ) db.createRole( { role: “test.writer”, privileges: [ { resource: { db: “test”, collection: “restaurants” }, actions: [“update”, “insert”, “remove” ] } ], roles: [“test.reader”] } )
Note how test.writer inherits the test.reader role.
Create Users
Using the above created roles, create the required users. Here we have two data scientists who have read-only access to the collection(s). Furthermore, using the custom data, we can identify these users directly back to their employee IDs:
db.createUser( { user: “test.dataScientist01”, pwd: “changeMe”, customData: { employeeId: 12345 }, roles: [“test.reader”] } ) db.createUser( { user: “test.dataScientist02”, pwd: “changeMe”, customData: { employeeId: 67890 }, roles: [“test.reader”] } )
Finally we create the user for our application:
db.createUser( { user: “test.appUser”, pwd: “changeMe”, customData: { appName : “restaurantreview.com” }, roles: [“test.writer”] } )
This is a pretty simple example, but it shows what can be achieved wrapping privileges into roles. As new collections are added to the database, only the roles need to be amended. All the users, in turn, inherit their privileges via the roles, keeping administration down to minimum.