Security: authentication, RBAC, TLS and encryption
Harden a self-managed deployment: enable auth, least-privilege roles, TLS everywhere, internal authentication, encryption at rest, field-level encryption and auditing.
- Enable authentication and create least-privilege users
- Use built-in and custom roles
- Configure TLS and internal member authentication
- Choose between encryption at rest, CSFLE and Queryable Encryption
conceptThe concept
- Authentication: SCRAM-SHA-256 by default; x.509 certificates; LDAP, Kerberos and OIDC in Enterprise editions (Percona Server for MongoDB also supports LDAP and Kerberos).
- Authorization (RBAC): users get roles; roles grant privileges (actions on resources). Built-ins:
read,readWrite,dbAdmin,userAdmin,clusterMonitor,clusterAdmin,backup,restore,root. Custom roles narrow privileges further. - Network:
bindIpto private interfaces, firewall/security groups, TLS withnet.tls.mode: requireTLS. - Internal auth: replica set and cluster members authenticate each other with a keyFile or x.509 (
security.clusterAuthMode). - Encryption at rest: storage-engine encryption (Enterprise, Percona Server for MongoDB with Vault/KMIP), or volume encryption (LUKS, EBS).
- Client-side field level encryption (CSFLE) and Queryable Encryption (7.0 GA): the driver encrypts sensitive fields before they leave the application; the server never sees plaintext.
- Auditing: MongoDB Enterprise and Percona Server for MongoDB (free) write an audit log of authentication attempts, user and role changes, DDL and, if you opt in, reads and writes. Community Edition has no audit log. Configure it under
auditLogwith afilterso you record what compliance needs without logging every query; CRUD auditing also needsauditAuthorizationSuccessand costs throughput. Ship the file off the host (syslog or a log agent) so an attacker with server access cannot quietly edit it.
whyWhy it matters
Unauthenticated MongoDB instances exposed to the internet were famously ransomed by the thousands. Today the risks are subtler: over-privileged application users, shared admin credentials, plaintext connections inside the VPC, and backups without encryption.
Use caseReal-world scenario
The shop application connects as shop_app with readWrite on shop only; reporting uses shop_ro with read pinned to analytics nodes; the backup agent uses the backup role; humans log in with LDAP accounts mapped to roles; card numbers are protected with Queryable Encryption so DBAs never see them.
Try itExamples
Run and edit these in the interactive lesson.
use admin
// clean up from a previous run so the example can be repeated
for (const u of ["shop_app", "analyst"]) { try { db.dropUser(u) } catch (e) {} }
try { db.dropRole("ordersReadOnlyNoPII") } catch (e) {}
db.createRole({ role: "ordersReadOnlyNoPII", privileges: [{ resource: { db: "shop", collection: "orders" }, actions: ["find"] }], roles: [] })
db.createUser({ user: "shop_app", pwd: "use-a-secret-manager", roles: [{ role: "readWrite", db: "shop" }] })
db.createUser({ user: "analyst", pwd: "use-a-secret-manager", roles: ["ordersReadOnlyNoPII", { role: "clusterMonitor", db: "admin" }] })
db.getUsers().users.map(u => ({ user: u.user, roles: u.roles }))
use shopnet:
port: 27017
bindIp: 10.0.1.15,localhost
tls:
mode: requireTLS
certificateKeyFile: /etc/mongo/tls/server.pem
CAFile: /etc/mongo/tls/ca.pem
security:
authorization: enabled
clusterAuthMode: x509 # or keyFile: /etc/mongo/keyfile (chmod 400)
setParameter:
authenticationMechanisms: SCRAM-SHA-256,MONGODB-X509db.adminCommand({ connectionStatus: 1 })
db.getSiblingDB("admin").getRoles({ showPrivileges: false, showBuiltinRoles: false })# mongod.conf on Percona Server for MongoDB (same keys on MongoDB Enterprise)
auditLog:
destination: file # or syslog, so the trail leaves the host
format: JSON
path: /var/log/mongodb/audit.json
filter: '{ atype: { $in: [
"authenticate", "logout",
"createUser", "dropUser", "updateUser", "grantRolesToUser", "revokeRolesFromUser",
"createRole", "dropRole", "updateRole",
"createCollection", "dropCollection", "dropDatabase", "renameCollection",
"createIndex", "dropIndex", "shutdown"
] } }'
# Failed logins only, for a brute-force alert:
# filter: '{ atype: "authenticate", result: { $ne: 0 } }' # 18 = AuthenticationFailedinternalsUnder the hood
Users are stored in admin.system.users with salted SCRAM credentials; the authentication database is where the user is defined (authSource). The localhost exception lets you create the first user on a fresh instance with auth enabled, then closes. With CSFLE the driver fetches data keys from a key vault collection, encrypted by a customer master key in a KMS (AWS KMS, Azure, GCP, KMIP); deterministic encryption allows equality queries, random encryption does not, and Queryable Encryption supports equality and range on randomly encrypted data.
Avoid / preferDo this, not that
db.createUser({ user: "app", pwd: "app123", roles: ["root"] })db.createUser({ user: "app", pwd: passwordPrompt(),
roles: [{ role: "readWrite", db: "shop" }] })An application needs read and write on its own database, nothing more. root turns any SQL-injection-style bug into a full cluster compromise.
net: bindIp: 0.0.0.0
net:
bindIp: 10.0.1.5
tls:
mode: requireTLS
certificateKeyFile: /etc/ssl/mongod.pem
security:
authorization: enabledBind to the private interface only, require TLS, and turn on authorization. Exposed, unauthenticated instances are found and wiped within hours.
PitfallsCommon mistakes
- Binding to 0.0.0.0 with auth disabled, even "temporarily".
- One
rootuser shared by the application, scripts and humans. - TLS only at the load balancer while member-to-member traffic is plaintext.
- Passwords in connection strings committed to git; use a secret manager and rotate.
- Keyfiles with permissive file permissions, or the same keyfile across environments.
LimitsWhen not to use it
Do not build your own authentication proxy or encryption layer in front of MongoDB when built-in SCRAM, x.509, TLS and roles cover the need. Do not give applications dbOwner or dbAdmin "to be safe"; those roles can drop collections and change validation.
SecuritySecurity notes
SCRAM-SHA-256 is the default mechanism. For audit trails, MongoDB Enterprise and Percona Server for MongoDB provide an audit log; enable it for authentication events and privileged commands, and ship it off the host.
ChallengeTry it yourself
Create a custom role reviewWriter that can only read orders and insert into reviews, create a user with only that role, and show the user's roles.
Show one solution
db.createRole({ role: "reviewWriter", privileges: [
{ resource: { db: "shop", collection: "orders" }, actions: ["find"] },
{ resource: { db: "shop", collection: "reviews" }, actions: ["insert"] }
], roles: [] })
db.createUser({ user: "review_svc", pwd: "use-passwordPrompt-in-real-shells", roles: ["reviewWriter"] })
db.getUser("review_svc").rolespasswordPrompt(). Running this twice fails because the role already exists.InterviewQuestions you should be able to answer
How would you secure a new self-managed MongoDB deployment?
Private network only (bindIp, security groups), TLS required for clients and members, internal auth via x.509 or keyFile, authorization: enabled, admin user created via the localhost exception, per-application least-privilege users and custom roles, human access via LDAP/OIDC with MFA where available, encryption at rest (engine or volume), CSFLE/Queryable Encryption for PII, auditing enabled, secrets in a vault with rotation, encrypted backups in a separate account, and regular patching.
CSFLE vs encryption at rest?
Encryption at rest protects data files and backups from someone with disk access; the server and DBAs still see plaintext. CSFLE and Queryable Encryption encrypt specific fields in the driver, so the server, logs, backups and DBAs only see ciphertext; only applications with access to the keys can decrypt. They complement each other.
An auditor asks who changed a user's roles last month. How do you answer on self-managed MongoDB?
From the audit log, if it was enabled: Percona Server for MongoDB or Enterprise record grantRolesToUser, updateUser and similar events with the user, client address and time. Community Edition has no audit log, so the honest answer there is "we cannot prove it"; that is a reason to run Percona Server or Enterprise where compliance matters. Going forward: a filter covering authentication, user and role changes and DDL, the log shipped off the host to append-only storage, retention matching the policy, and alerts on failed logins and privilege changes.
QuizCheck your understanding
5 questions with instant feedback in the interactive lesson; a wrong answer goes into your review deck.