Intro
MongoDB is a popular document database often deployed without changing its default settings. Out of the box, a fresh instance listens on all network interfaces, has no authentication enabled, and grants unrestricted access to any client that can connect. For development teams and DevOps engineers, this is a serious risk: exposed MongoDB instances have been repeatedly targeted by attackers who steal data, delete databases, and demand ransom.
Security hardening is the practice of reducing the attack surface of a system by changing defaults, enabling access controls, and verifying that the resulting configuration behaves as expected. This guide walks through a practical hardening process for MongoDB. You will learn how to inventory your environment, apply scoped configuration changes safely, verify the results with observable checks, and prepare a rollback plan if something breaks. The goal is not to turn every MongoDB deployment into a fortress overnight, but to establish a repeatable baseline that protects your data without paralyzing development.
The process follows three principles drawn from successful operational practice: separate research from implementation, pilot changes narrowly before scaling, and make every step measurable. By the end, you will have a concrete checklist that you can adapt to your own environment.
Version and Environment Inventory
Before changing any security setting, you need a clear picture of what you are running. An inventory prevents surprises such as applying a setting that is not supported in your MongoDB version or accidentally locking yourself out of a replica set member.
Identify MongoDB Version and Edition
Run the following command in the MongoDB shell (mongosh) to get the server version and build information:
// Connect to the admin database
db = db.getSiblingDB('admin');
// Print version and build info
printjson(db.runCommand({ buildInfo: 1 }));
Expected output (truncated for readability):
{
"version": "7.0.4",
"gitVersion": "...",
"modules": ["enterprise"],
"sysInfo": "...",
"ok": 1
}
Note whether the modules field includes "enterprise" or is absent (Community Edition). Enterprise Edition includes advanced security features such as LDAP authentication, encryption at rest using KMIP, and auditing. Community Edition still supports authentication, TLS, and role-based access control, but you cannot use those enterprise-only features. Knowing the edition prevents you from attempting to configure something unsupported.
Determine Deployment Topology
MongoDB can run as a standalone server, a replica set, or a sharded cluster. Each topology requires different hardening steps. To check the current topology, use:
// In mongosh
printjson(db.adminCommand({ hello: 1 }));
If the result includes "isWritablePrimary": true and no "setName", you are on a standalone. If "setName" is present, it is a replica set. For sharded clusters, you will also see "msg": "isdbgrid" when connecting to a mongos router.
Inventory Users, Roles, and Authentication Status
Check whether authentication is already enabled and what users exist. First, see if you are connected without credentials. In mongosh, run:
// Show current user (empty if no authentication)
printjson(db.runCommand({ connectionStatus: 1 }).authInfo.authenticatedUsers);
If the result is an empty array, authentication is not enabled or you are connected as a user with no roles. To list all users in the admin database:
use admin;
db.getUsers();
This returns an array of user documents, including their roles and authentication restrictions. Document these users; you may need to migrate or modify them after enabling access control.
Check Network Exposure and Configuration File
Determine which interfaces and ports MongoDB is listening on. On Linux, use ss or netstat:
sudo ss -tulpn | grep mongod
Example output:
tcp LISTEN 0 128 0.0.0.0:27017 0.0.0.0:* users:(("mongod",pid=1234, fd=10))
The 0.0.0.0:27017 means MongoDB is listening on all IPv4 interfaces, which is a security risk. Find the configuration file, typically /etc/mongod.conf on Linux, and check the net section:
net:
port: 27017
bindIp: 0.0.0.0 # This is the problem
Prerequisites for Hardening
Before proceeding, ensure you have:
- Administrative shell access to the MongoDB host.
- A backup or snapshot of the database, especially if you are changing authentication settings.
- The MongoDB documentation for your specific version downloaded or bookmarked.
- A test environment or a maintenance window for a replica set (some changes require restarting
mongod). - A plan to update application connection strings with new credentials or TLS settings.
A clear inventory reduces rework later. You will know exactly what to change and what to avoid.
Safe Configuration Path
Hardening is best done incrementally, with each step verified before moving to the next. This section outlines a scoped approach: first restrict network exposure, then enable authentication, then implement role-based access control, and finally encrypt connections. Each step includes concrete commands and expected outcomes.
Step 1: Bind MongoDB to Localhost or Private Interface
Binding MongoDB to a specific network interface is the fastest way to reduce exposure. If your application runs on the same host, bind to 127.0.0.1. If it runs on other servers in a private network, bind to the private IP address. Avoid 0.0.0.0 unless you have a compelling reason.
Edit the configuration file (/etc/mongod.conf) and set:
net:
port: 27017
bindIp: 127.0.0.1 # Change from 0.0.0.0
Then restart MongoDB with the appropriate system command:
sudo systemctl restart mongod
Verify the listening socket:
sudo ss -tulpn | grep mongod
Expected output:
tcp LISTEN 0 128 127.0.0.1:27017 0.0.0.0:* users:(("mongod",pid=1234, fd=10))
If you need remote access from a specific IP range, consider using a firewall (e.g., iptables or cloud security groups) instead of binding broadly. Binding to localhost is the simplest safe default for a pilot.
Step 2: Enable Authentication and Create an Administrator
MongoDB does not enable authentication by default. To turn it on, you must create a user administrator and then restart the server with the --auth option or security.authorization: enabled in the config file.
First, connect without authentication (while the server is still running without auth) and create an administrator:
use admin;
db.createUser({
user: "siteUserAdmin",
pwd: "changeMeToAStrongPassword", // replace with a secure password
roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
});
This creates a user with the ability to create and manage users on any database, but not to read or write application data. That separation of duties is important for security.
Next, enable authorization in the configuration file:
security:
authorization: enabled
Restart MongoDB:
sudo systemctl restart mongod
Now connect as the administrator:
mongosh --authenticationDatabase admin -u siteUserAdmin -p 'changeMeToAStrongPassword' --eval 'db.runCommand({ connectionStatus: 1 })'
Expected output shows the authenticated user and roles.
If you see an authentication error, check that the password was typed correctly and that the user exists in the admin database.
Step 3: Implement Least-Privilege Roles for Applications
Never use the administrator account for application connections. Instead, create dedicated users with only the permissions needed. For example, if your application only reads and writes documents in a database named appdb, create a user with the readWrite role on that database:
use appdb;
db.createUser({
user: "appUser",
pwd: "anotherStrongPassword",
roles: [ { role: "readWrite", db: "appdb" } ]
});
If the application only needs to read from a reporting database, use the read role. Avoid granting the root role or dbAdmin unless absolutely necessary. You can also define custom roles for finer-grained control, but start with built-in roles.
To list available built-in roles, run:
show roles;
Step 4: Enable TLS/SSL Encryption for Client Connections
Encrypting traffic between clients and MongoDB prevents eavesdropping and credential theft. This step requires a TLS certificate. For a pilot, you can generate a self-signed certificate, but in production use a certificate signed by a trusted certificate authority.
Generate a self-signed certificate (for testing only):
openssl req -newkey rsa:2048 -new -x509 -days 365 -nodes -out mongodb-cert.crt -keyout mongodb-cert.key
cat mongodb-cert.key mongodb-cert.crt > mongodb.pem
chmod 600 mongodb.pem
Move the mongodb.pem file to a secure location, e.g., /etc/ssl/mongodb.pem, and ensure the mongod user can read it. Then configure MongoDB to use TLS:
net:
tls:
mode: requireTLS
certificateKeyFile: /etc/ssl/mongodb.pem
Restart MongoDB:
sudo systemctl restart mongod
Connect using TLS:
mongosh --tls --tlsCAFile /path/to/ca.crt --host <hostname> --authenticationDatabase admin -u siteUserAdmin -p 'changeMeToAStrongPassword'
For self-signed certificates, you may need --tlsAllowInvalidCertificates for testing. Never use that flag in production.
Step 5: Secure Configuration File and Key Permissions
The MongoDB configuration file contains sensitive settings and sometimes credentials (e.g., for LDAP or KMIP). Restrict its permissions:
sudo chmod 600 /etc/mongod.conf
sudo chown mongod:mongod /etc/mongod.conf
Also restrict access to the TLS private key:
sudo chmod 600 /etc/ssl/mongodb.pem
sudo chown mongod:mongod /etc/ssl/mongodb.pem
Scoped Implementation Choices
When rolling out these changes to multiple servers, do not change everything at once. Pick one application and one database as a pilot. Apply the network binding and authentication first, then test the application thoroughly. Only then add TLS and least-privilege roles. This narrow, measurable pilot reduces the chance of breaking production traffic and makes it easier to identify which change caused a problem if something goes wrong.
Verification and Diagnostics
After each hardening step, verify that the change took effect and that legitimate operations still work. This section provides observable checks and expected results.
Verify Authentication is Enforced
Try connecting without credentials:
mongosh --eval 'db.runCommand({ ping: 1 })'
Expected result: an error such as MongoServerError: command ping requires authentication. If you get a { ok: 1 } response, authentication is not enabled.
Verify Network Binding
Use ss as shown earlier to confirm the listening address is correct. Additionally, test connectivity from a remote host (where applicable):
nc -zv <mongodb_host_ip> 27017
If bound to 127.0.0.1 and you are not on that host, the connection should be refused. If bound to a private IP, only hosts on that private network should be able to connect.
Verify User Roles and Permissions
Connect as the application user and attempt an operation outside its permissions. For example, if appUser has readWrite on appdb but not on otherdb:
use otherdb;
db.test.insertOne({ x: 1 });
Expected result: MongoServerError: not authorized on otherdb to execute command. This confirms that least privilege is working.
Verify TLS Encryption
Connect with the --tls option and check the connection metadata:
// In mongosh
db.runCommand({ connectionStatus: 1 }).security;
If TLS is active, the output includes "SSLServerHasCertificateAuthority": true (or similar depending on version). Alternatively, use openssl s_client to inspect the certificate:
echo | openssl s_client -connect <hostname>:27017 -servername <hostname> | openssl x509 -noout -subject -issuer
You should see the certificate details.
Run a Comprehensive Check with mongosh
The following script combines several checks. Save it as check_security.js and run it with mongosh --file check_security.js (as an administrator):
// check_security.js
print('--- Security Diagnostics ---');
// Check authentication status
const connStatus = db.runCommand({ connectionStatus: 1 });
print('Authenticated users:', JSON.stringify(connStatus.authInfo.authenticatedUsers));
// Check if TLS is active (requires admin privileges)
const serverStatus = db.adminCommand({ serverStatus: 1 });
print('TLS enabled:', serverStatus.security && serverStatus.security.tls ? 'yes' : 'no');
// List all users in admin database
const adminDb = db.getSiblingDB('admin');
print('Admin users:', JSON.stringify(adminDb.getUsers().map(u => ({ user: u.user, roles: u.roles }))));
// Check if authorization is enabled (from server parameters)
const params = db.adminCommand({ getParameter: '*' });
print('Authorization enabled:', params.authorization);
Expected output includes your authenticated user, a list of users, and authorization: enabled. If any of these are missing, re-run the appropriate hardening step.
Use MongoDB Logs for Diagnostics
MongoDB logs authentication failures and other security events. Check the log file (often /var/log/mongodb/mongod.log) for errors:
sudo grep -i 'authentication' /var/log/mongodb/mongod.log | tail -n 20
Example output:
2025-01-01T12:00:00.000+0000 I ACCESS [conn123] Successfully authenticated as principal siteUserAdmin on admin
If you see repeated Authentication failed messages, troubleshoot the user credentials or connection string.
Common Pitfalls and How to Avoid Them
Even experienced administrators can run into problems during MongoDB hardening. Here are the most frequent mistakes and how to prevent or recover from them.
Forgetting to Create an Admin User Before Enabling Authentication
Why it happens: In the rush to secure the server, administrators enable authorization in the config file and restart MongoDB without first creating any user. The server then requires authentication for all connections, but no credentials exist, resulting in a complete lockout.
How to avoid it: Always create at least one user administrator while the server is still running without authentication, as shown in Step 2. Verify the user exists with db.getUsers() before enabling authorization.
How to recover: Temporarily disable authorization in the config file, restart MongoDB, create the administrator user, re-enable authorization, and restart again.
Binding to the Wrong Interface and Breaking Application Connectivity
Why it happens: Binding to 127.0.0.1 on a server where the application runs on a different host, or forgetting to update the connection string after changing the bind address.
How to avoid it: Inventory your application architecture before changing the bind IP. Know where your application servers are located and which IPs they use to reach MongoDB. If the application is remote, bind to the appropriate private IP or use a firewall to restrict access.
How to recover: Update the bind IP to the correct value or set up a temporary SSH tunnel to restore connectivity, then fix the configuration permanently.
Using Overly Broad Roles for Application Users
Why it happens: Convenience leads to granting root or dbAdmin to application accounts, or reusing the administrator credentials in the application code. This defeats the purpose of least privilege and increases the blast radius if the application is compromised.
How to avoid it: Define explicit roles based on actual application needs. Start with built-in roles like readWrite on the specific database, and only add more permissions when a concrete need arises. Never use the admin user for application connections.
How to recover: Audit existing users with db.getUsers(), identify accounts with excessive privileges, and replace them with new, narrowly scoped users. Update the application connection strings accordingly.
Ignoring TLS Certificate Expiry
Why it happens: Self-signed certificates or CA-signed certificates have an expiration date, and without monitoring, they expire silently, causing application connections to fail.
How to avoid it: Use certificates with long validity for internal deployments (e.g., 1 year) and set up a calendar reminder or monitoring alert to renew them before expiration. For production, automate certificate renewal with a tool like certbot or your internal PKI.
How to recover: Generate a new certificate, update the configuration file, and restart MongoDB. Ensure the new certificate is distributed to clients if using certificate pinning.
Failing to Test After Each Change
Why it happens: Teams may apply multiple hardening steps at once without verifying each one, making it difficult to isolate which change caused a failure.
How to avoid it: Follow the incremental approach in this guide: change one setting, restart, run the verification checks, and test the application. Only proceed to the next step after the current one is confirmed working.
How to recover: If a combined change breaks something, roll back to the previous working configuration and reapply changes one by one to identify the culprit.
Operations Checklist
Use the following checklist as a repeatable review before and after hardening, and for ongoing operations. The table format allows you to track each item. Assign a single accountable owner for each check and review on a defined cadence.
| Check | Command / Action | Expected Result | Owner | Review Frequency |
|---|---|---|---|---|
| MongoDB version | db.runCommand({ buildInfo: 1 }).version | Version matches your inventory | Database Administrator (e.g., Alex Chen) | Quarterly |
| Topology | db.adminCommand({ hello: 1 }) | Correct standalone/replica set/sharded | Database Administrator | Quarterly |
| Authentication enabled | db.adminCommand({ getParameter: '*' }).authorization | enabled | Security Engineer (e.g., Priya Shah) | Monthly |
| Network binding | sudo ss -tulpn | grep mongod | Listening on intended IP only | System Administrator | Monthly |
| Admin user exists | use admin; db.getUsers() | At least one user with userAdminAnyDatabase | Database Administrator | Monthly |
| Application users | use appdb; db.getUsers() | Users have least-privilege roles | Application Owner (e.g., Jordan Lee) | Monthly |
| TLS active | db.adminCommand({ serverStatus: 1 }).security.tls | true | Security Engineer | Monthly |
| Config file permissions | ls -l /etc/mongod.conf | -rw------- mongod mongod | System Administrator | Quarterly |
| Log review | sudo grep -i 'authentication' /var/log/mongodb/mongod.log | No unexpected failures | Security Engineer | Weekly |
Perform this checklist:
- Before any major change.
- After each hardening step.
- Monthly as part of routine security review.
- When onboarding new team members to ensure they understand the security posture.
Document any deviations and update the checklist if your environment changes (e.g., new databases, new users).
Failure Modes and Recovery
Hardening changes can break application connectivity or lock you out. This section describes common failure modes and how to recover.
Locked Out of MongoDB After Enabling Authentication
If you enabled authentication without first creating an administrator user, you will not be able to authenticate. To recover, you must temporarily disable authorization and restart MongoDB to create the user.
Steps:
- Edit
/etc/mongod.confand comment out or removesecurity.authorization. - Restart MongoDB:
sudo systemctl restart mongod. - Connect without authentication and create the administrator user as shown in Step 2 of the Safe Configuration Path.
- Re-enable authorization and restart again.
To avoid this, always create at least one administrator before enabling auth.
Application Cannot Connect After Binding to Localhost
If you bind MongoDB to 127.0.0.1 but your application runs on a different host, the application will fail to connect. Check application logs for connection errors. To recover, either bind to the appropriate private IP or set up a secure tunnel (e.g., SSH port forward) as a temporary measure. Then update the application connection string accordingly.
TLS Configuration Errors Cause Server Startup Failure
If the TLS certificate file path is wrong or permissions are too open, mongod may fail to start. Check the system logs:
sudo journalctl -u mongod -n 50 --no-pager
Typical errors include Permission denied or cannot read certificate file. Fix the file permissions (chmod 600) and ownership (chown mongod:mongod), or correct the path in the configuration file, then restart.
Rollback Plan
Before making changes, document the original configuration. You can copy /etc/mongod.conf to a backup file:
sudo cp /etc/mongod.conf /etc/mongod.conf.bak
If a change causes problems, restore the original configuration and restart:
sudo cp /etc/mongod.conf.bak /etc/mongod.conf
sudo systemctl restart mongod
However, note that disabling authentication after users have been created does not remove the users; they remain but are ignored. If you re-enable auth later, the same users will work.
Verification After Recovery
After rolling back or fixing an issue, re-run the verification checks in the previous section to confirm that the system is in the expected state. Pay special attention to authentication and network binding.
Conclusion
Hardening MongoDB is not a one-time task but a continuous practice. By following the steps in this guide, you have:
- Inventoried your MongoDB version, topology, users, and network exposure.
- Bound the server to a safe interface, enabled authentication, created least-privilege users, and enabled TLS.
- Verified each change with concrete commands and expected outputs.
- Prepared recovery steps for common failures and established a repeatable operations checklist.
Your next steps should be to:
- Apply these hardening steps to a single pilot database or application.
- Run the full verification script and checklist.
- Test the application thoroughly, including failure scenarios.
- Document the resulting configuration and share the checklist with your team.
- Schedule regular reviews and update the hardening as your environment evolves.
This practical, measured approach reduces risk without unnecessary disruption. Start with the narrow pilot, inspect the results, and then expand to other parts of your infrastructure. Security is a process, and each verified step moves you closer to a stronger posture.