six demon bag
Wind, fire, all that kind of thing!
2020-02-29
Non-interactive MongoDB Commandline
MongoDB provides an interactive command shell for working with the database. Which is all nice and dandy, but from an admin and automation perspective it's desirable to also be able to run commands non-interactively. The mongo
commandline tool does have a parameter --eval
that kind of allows you to do that:
--eval <javascript>
Evaluates a JavaScript expression that is specified as an argument. mongo does not load its own environment when evaluating code. As a result many options of the shell environment are not available.
except that it doesn't play nice when you also want to automatically authenticate via the config file .mongorc.js
.
root@server:~# mongo --ssl --sslAllowInvalidHostnames --eval 'rs.status()'
MongoDB shell version v3.4.10
connecting to: mongodb://127.0.0.1:27017
2018-04-11T08:39:28.250+0000 W NETWORK [thread1] The server certificate does not match the host name. Hostname: 127.0.0.1 does not match SAN(s): *.example.com example.com
MongoDB server version: 3.4.10
{
"ok" : 0,
"errmsg" : "not authorized on admin to execute command { replSetGetStatus: 1.0 }",
"code" : 13,
"codeName" : "Unauthorized"
}
Essentially, non-interactive invocation ignores .mongorc.js
and requires explicit credentials:
mongo --username alice --password abc123 --authenticationDatabase admin ...
However, if you echo
the statement into the mongo
command it authenticates and executes just fine:
root@server:~# echo 'rs.status()' | mongo --ssl --sslAllowInvalidHostnames
MongoDB shell version v3.4.23
connecting to: mongodb://127.0.0.1:27017
2020-02-26T19:33:07.603+0000 W NETWORK [thread1] The server certificate does not match the host name. Hostname: 127.0.0.1 does not match SAN(s): ...
{
...
}
To work around this issue create a script (e.g. mongocmd
) with the following content:
#!/bin/bash
declare -a mongo_args=(
'--ssl'
'--sslAllowInvalidHostnames'
'--quiet'
)
if [ "$#" -eq 0 ]; then
mongo "${mongo_args[@]}" < /dev/stdin
else
for cmd in "$@"; do
echo "$cmd" | mongo "${mongo_args[@]}"
done
fi
The script will take commands either as arguments:
mongocmd 'rs.status()' 'db.hostInfo()'
or (if no arguments were provided) read them from STDIN:
echo 'rs.status()' | mongocmd
mongocmd <<EOF
rs.status()
db.hostInfo()
EOF
The parameter --sslAllowInvalidHostnames
is used because the host certificate didn't include a Subject Alternative Name for 127.0.0.1.
The .mongorc.js
for automatic login should look like this:
function authRequired() {
try {
return (db.serverCmdLineOpts().code == 13);
} catch (err) {
return false;
}
}
if (authRequired()) {
try {
rs.slaveOk()
var prev_db = db
db = db.getSiblingDB('admin')
db.auth('admin', 'PASSWORD')
db = db.getSiblingDB(prev_db)
} catch (err) {
abort('Unknown error')
}
}
Posted 01:29 [permalink]