Context
In my current web application project, I set up a MongoDB database, including server administrator and project users, by means of a number of JavaScript files that are executed with the MongoDB shell.
I can't seem to find a way to handle root
or user passwords in a safe fashion:
Problem 1: create users
This is an examplary JavaScript file I use to create a superuser and a project user:
use admin
db.createUser(
{
user: "root",
pwd: "abc123",
roles: [
{
role: "root",
db: "admin"
}]
})
use project_db
db.createUser(
{
user: "project_admin",
pwd: "def456",
roles: [
{
role: "dbOwner",
db: "project_db"
}]
})
Obviously, this file is under version control. How do I go about not storing cleartext passwords in there ?!? The db.createUser(...)
docs explicitely state the cleartext password must be passed (except when using an external user database).
Seriously ?!?
Problem 2: use credentials
I found three ways to pass credentials when accessing the database (to e.g. run the database setup script); none of them works satisfactorily:
authenticating on the command line
The mongo
executable takes corresponding arguments:
mongo --username project_admin \
--password def456 \
--authenticationDatabase project_db \
< "${path_to_db_build_script}"
Problem: The password is visible in e.g. ps
output. Not acceptable.
Passing --username project_admin
only fails with Error: Missing expected field "pwd"
.
Passing --username project_admin --password
makes mongo
query the password interactively which obviously prevents automatic script execution - and automatic is why this is a script in the first place...
authenticating using ~/.mongorc.js
Taken from this blog post:
db = connect("localhost:27017/project_db");
db.getSiblingDB("project_db").auth("project_admin", "def456");
This does work, but does not seem to provide a way to work with more than one user. There might be an approach to work with one .js
file per user and/or .js
file templates, but once there is any complexity involved there, those files should be under version control - and that brings us back to the same problem as for creating users.
authenticating using code
In theory, it should also be possible to use db.auth(...)
to authenticate within the script.
In practice, this just seems an epic fail to me:
This works, but stores credentials in code:
db.auth("project_admin", "def456")
This works using a JSON doc; also stores credentials in code:
db.auth({ user: "project_admin", pwd: "def456" })
db.auth(...)
does have a digestPassword
parameter which is largely undocumented, but the name suggests it is meant to indicate the password is passed in some encrypted / hashed / salted / whatever fashion.
This would allow storing the .js
scripts in version control with non-cleartext passwords; not ideal, but definitely better than cleartext. However, this simply does not work, i.e. fails with Error: Authentication failed.
For starters, I would assume setting digestPassword
to false
is appropriate when passing the password in cleartext; however, this fails (BUG #1 ?):
db.auth({ user: "project_admin", pwd: "def456", digestPassword: fails })
whereas this works (WTF ?!?):
db.auth({ user: "project_admin", pwd: "def456", digestPassword: true })
Setting the mechanism
to PLAIN
fails with Error: Missing expected field "mechanism"
, despite the field clearly being there (BUG #2 ?), no matter if digestPassword
is true
or false
:
db.auth({ user: "project_admin", pwd: "def456", digestPassword: true, mechanism: "PLAIN" })
Setting the mechanism
to the default SCRAM-SHA-1
seems to expose the same bug as above; this fails:
db.auth({ user: "project_admin", pwd: "def456", digestPassword: fails, mechanism: "SCRAM-SHA-1" })
whereas this works:
db.auth({ user: "project_admin", pwd: "def456", digestPassword: true, mechanism: "SCRAM-SHA-1" })
Encrypted / hashed / digested / whatever passwords can be obtained by starting the mongo
shell as root
, e.g.
mongo admin -u root -p abc123
and running db.system.users.find()
which returns something like this:
...
{
"_id": "project_db.project_admin",
"user": "project_admin",
"db": "project_db",
"credentials":
{
"SCRAM-SHA-1":
{
"iterationCount": 10000,
"salt": "WnKFmGs3BTbmkbUWi0RPnA==",
"storedKey": "EEIMqBEMUUOpoR3i3pgKz0iRumI=",
"serverKey": "HsSOxujNODlKcRiEdi1zkj83MRo="
}
},
"roles": [
{
"role": "dbOwner",
"db": "project_db"
}]
}
...
Using any of the three hashes (?) from the output as password with digestPassword
true
or false
fails. Without looking into the sources, I can only assume that there is some sha1(password + salt)
relationship with the credentials
above, but there seems to be no documentation whatsoever and the presumed bugs my attempts so far have exposed do not exactly encourage pursuing this any further.
custom approach
There might be a way to run JavaScript in ~/.mongorc.js
that takes the current username (from where ?) and looks up the password from an external source. But why would I have to implement credentials management for a supposed database solution ?
Questions:
- How do people handle credentials when working with MongoDB ?
- My experience with MongoDB so far has been so abyssmally bad that, considering MongoDB is being sold for production purposes, it seems wise to first search for the cause on my side. Am I doing something fundamentally wrong ? Are my expectations unjustified ? Do MongoDB users not care about password safety ?
I would greatly appreciate if someone could share their approaches and experiences.