You can redefine the behaviour of the mongodb shell. In this post, we use this
to safeguard against accidental calls of destructive functions.
The same principle can also be used to remove access to any functionality of
the mongodb shell.
A couple of months ago, in one of our projects, during a live deployment, the
deployer accidentally dropped all indexes on a central collection.
I can’t blame him - this can happen to (almost) anyone given enough time, and
he’s very experienced with a history of splendid work.
We do post mortem analyses for incidents, so we tried to figure out some causes
for this one. We came up with:
- the wonderful tab completion the mongodb-shell has
- fingers being quicker than the brain
- working manually on the live database
There’s already some things to easily take away from this one:
- don’t perform manual work on the live database, write scripts that are tested on a test database
- if you need to manually apply migrations, do this in pairs, be attentive
Today, I want to present to you another quickwin we came up with to protect us
from this kind of thing happening again.
apply this knowledge 1:1 when querying the database. In contrast to SQL shells,
I have a context and state, so I can use the output of a query for mangling the
data or even as input for another query or even multiple queries. Overall, I’m
a happy camper and am not looking back to SQL shells. And you can configure it
via an rc file on a system (/etc/mongorc.js) and user (~/.mongorc.js) basis.
functions at runtime. We decided to implement a safeguard for destructive
functions with the goal of reducing errors due to tab completion and fingers
quicker than the brain.
In order to achieve that, we wanted to replace a function with a safeguard that
stopped the normal execution of the destructive function, but kept it available
in a convenient manner. We decided to republish the destructive function under
the same name, but with a random string appended. This way, tab completion will
always stop at the safeguard. Let’s see it in action:
Note that the name under which the function is republished (in this case
“dropIndexeskgk”) changes for each new instance of the mongo shell.
The code actually is quite simple:
Take it apart
So, let’s dissect this snippet.
In line 1, the whole functionality is wrapped in a so called
Since we want to append random strings to our function names, we define a
The main action happens in the obfuscate function (lines 7-24). First, we
safeguard against invalid input (objects without prototype, line 8, and
functions that don’t exist, line 13), so that if any object or function names
change in future MongoDB versions, the script will at least notify the user.
Then we publish the original function under the obfuscated name (line 17) and
then put the safeguard function in place of the original function (lines 18-20).
On lines 26-49, the destructive calls are defined and passed to the obfuscate
Wrap it up
We roll this out to all our servers using chef. Our experience has been
positive. It’s not too intrusive, but achieves its goal.
If you want to give your users the ability to skip the safeguarding, roll it
out to ~/.mongorc.js, sourcing of which can be prevented by calling the mongo
shell with the –norc parameter. Otherwise, roll it out to /etc/mongorc.js,
which will always be sourced.