Andrew Mercer
on this page

MongoDB Security

MongoDB ships with no authentication enabled by default — anyone who can reach the port has full access until you turn authorization on. See Overview for basic connection syntax referenced below.

Change the Bind IP

By default older versions bind to all interfaces; restrict this before exposing an instance beyond localhost:

net:
  port: 27017
  bindIp: 10.0.0.10       # a specific internal IP ...
  # bindIp: 127.0.0.1     # ... or localhost-only
mongo --host <bind_ip>
mongo --host <bind_ip> --port <custom_port>   # if you also changed the listen port

Create the First Admin User

Without authorization enabled yet, connect locally and create a root-role user (full superuser) or a narrower userAdminAnyDatabase user (can manage users/roles but not read/write arbitrary data — often the better first account to create):

use admin

db.createUser({
  user: "mongoAdmin",
  pwd: "a_strong_password",
  roles: [ { role: "root", db: "admin" } ]
})
// Narrower alternative — can administer users but not read application data directly
db.createUser({
  user: "userAdmin",
  pwd: "a_strong_password",
  roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
})

Enable Authorization

# mongod.conf
security:
  authorization: enabled
sudo systemctl restart mongodb.service

Test It

use admin
db.auth("mongoAdmin", "a_strong_password")   // returns 1 on success
db.system.users.find()                        // inspect stored user/role records

Connecting With Auth

# Connect directly to the admin database
mongo admin --host <bind_ip> -u mongoAdmin -p

# Connect to a different database, but authenticate against admin
mongo app_db --host <bind_ip> -u mongoAdmin -p --authenticationDatabase=admin

# Same, connecting to the default database
mongo --host <bind_ip> -u mongoAdmin -p --authenticationDatabase=admin

Create a Dedicated User Per Application/Database

Avoid handing out the admin account for routine application access — scope a user to just what it needs:

db.createUser({
  user: "app_user",
  pwd: "a_strong_password",
  roles: [
    { role: "readWrite", db: "app_db" },
    { role: "read", db: "reporting" }
  ]
})

A user scoped this way can only see and use the databases it was granted — show dbs will correctly report not authorized for listDatabases unless it also has a role that includes that privilege. That's expected behavior, not a bug — it's the isolation the scoped grant is meant to provide.

Reset a User's Password

# Temporarily disable authorization to get in
# security:
#   authorization: enabled
systemctl restart mongodb.service
use admin
db.updateUser("mongoAdmin", { pwd: "a_new_password" })
# Re-enable authorization
security:
  authorization: enabled
systemctl restart mongodb.service

Delete a User

use target_db
db.dropUser("app_user")

Built-in Roles Reference

Category Roles
Database user read, readWrite
Database admin dbAdmin, userAdmin, dbOwner
All-database (cluster-wide) readAnyDatabase, readWriteAnyDatabase, userAdminAnyDatabase, dbAdminAnyDatabase
Cluster admin clusterManager, clusterMonitor, hostManager, clusterAdmin
Backup/restore backup, restore
Superuser root (implies most of the above)
Internal __system (used by MongoDB itself, not for application accounts)

Prefer the narrowest role that gets the job done — root for every service account defeats the purpose of having roles at all.

Enable TLS

Verify TLS support is compiled in:

mongod --version

Generate Certificates

Create a CA and a server certificate (or use a properly issued one from an internal/external CA — self-signed is shown here for reference):

mkdir /home/mongodb
# ... generate ca.crt and a combined server cert+key (mongodb-cert.pem) here ...
mv mongodb-cert.pem ca.crt /home/mongodb
chown -R mongodb:mongodb /home/mongodb
# mongod.conf, under net:
net:
  ssl:
    mode: requireSSL
    PEMKeyFile: /home/mongodb/mongodb-cert.pem
    CAFile: /home/mongodb/ca.crt
sudo systemctl restart mongodb.service

Important: once TLS is enabled, connect using the certificate's hostname, not a bare IP — connecting by IP against a cert issued for a hostname produces:

Error: socket exception [CONNECT_ERROR] for The server certificate does not match the host name <ip>
mongo --host mongo1.example.local --ssl --sslPEMKeyFile /home/mongodb/client.pem --sslCAFile /home/mongodb/ca.crt

x509 Client-Certificate Authentication

Rather than a username/password, a client can authenticate using the identity baked into its certificate. Get the certificate's subject line:

openssl x509 -in client.pem -inform PEM -subject -nameopt RFC2253 -noout
subject= CN=client1.example.local,O=Example Org,L=City,ST=State,C=US

Create a MongoDB user matching that exact subject string, in the special $external database:

db.getSiblingDB("$external").runCommand({
  createUser: "subject= CN=client1.example.local,O=Example Org,L=City,ST=State,C=US",
  roles: [ { role: "root", db: "admin" } ]
})
db.getSiblingDB("$external").auth({
  user: "subject= CN=client1.example.local,O=Example Org,L=City,ST=State,C=US",
  mechanism: "MONGODB-X509"
})

Disable the Localhost Exception

By default, MongoDB allows an unauthenticated connection from localhost to create the very first user (a bootstrapping convenience). Once real users exist, disable this explicitly rather than relying on it closing itself automatically:

mongod --setParameter enableLocalhostAuthBypass=false