Enable MongoDB authorization
/etc/mongod.confsecurity: authorization: enabledsudo systemctl restart mongodDo not do this yet. Read the safety section first — the order matters, and doing this on a running system breaks every client at once.
Why it matters
Section titled “Why it matters”Every other database on this site ships with some authentication. MongoDB ships
with none. A default mongod is not protected by a weak password; there is no
password. Connect, and you are effectively an administrator: read every
collection, drop every database, create users.
The only thing standing between a default MongoDB and the internet is
bindIp, which has defaulted to loopback since 3.6. That
default is the reason the situation improved after 2017 — but it is one line in
one file, and a container image, a cloud template or a colleague debugging a
connection can undo it in a second. When it goes, there is nothing behind it.
That combination — no authentication, and one setting away from public — is what produced the ransomware wave.
The localhost exception
Section titled “The localhost exception”MongoDB has a bootstrap mechanism that looks like a hole and isn’t:
When authorization is enabled and no users exist yet, MongoDB permits
unauthenticated connections from localhost — but only to create the first user in
the admin database. As soon as one user exists, the exception closes
permanently and automatically.
This is what lets you enable auth and then create your admin account, rather than needing a credential that doesn’t exist yet. It’s a well-designed bootstrap, and it is not something to disable.
Two things to know about it:
- It is localhost only. It never applies over the network, so it is not a remote hole.
- It closes on the first user in
admin, not the first user anywhere. Create your admin user first, then everything else.
Creating the first user
Section titled “Creating the first user”use admindb.createUser({ user: "admin", pwd: passwordPrompt(), roles: [ { role: "userAdminAnyDatabase", db: "admin" }, { role: "readWriteAnyDatabase", db: "admin" } ]})Use passwordPrompt(), not a string literal. A password typed into mongosh
as a literal lands in ~/.mongosh/mongosh_repl_history and in your shell history
if you passed --eval. passwordPrompt() reads it interactively and keeps it out
of both.
Note this first account is deliberately an admin account, not your application account — see least privilege roles for the accounts your apps should get.
Related
Section titled “Related”- Bind to a private interface — the only thing protecting you before this.
- Grant least privilege roles — the accounts to create next.