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.
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.
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 function.
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.