replica set with auth
adalga opened this issue · comments
When I try to run mongodb with --replSet and passed env variables for auth it gives error like
Error: Couldn't add user: not master
#179 I tried kalpakrg's solution too. However, it doesn't help either.
Same mistake
Ah, this is an interesting edge case of those new variables. As a workaround, you'll probably need to specify just --auth
instead of supplying the environment variables, and then set up the users yourself after setting up the replica set (since that needs to be set up first, IIRC).
I'm not sure if there's a good way for us to deal with --replSet
+ auth automatically. 😞
It can only be corrected by overwriting the entrypoint
#!/usr/bin/env sh
# /custom-entrypoint.sh
if [ ! -f /data/db/.metadata/.replicaset ]; then
mongod --fork --dbpath /data/db --port 27017 --logpath /var/log/mongod.log
RET=1
while [ $RET -ne 0 ]
do
echo "=> Waiting for confirmation of MongoDB service startup"
sleep 5
mongo admin --eval "help" >/dev/null 2>&1
RET=$?
done
mongo admin --eval "db.createUser({user:'$MONGO_INITDB_ROOT_USERNAME',pwd:'$MONGO_INITDB_ROOT_PASSWORD',roles:[{role:'root',db:'admin'}]})"
mongod --shutdown && mongod --fork --logpath /var/log/mongod.log --keyFile /run/secrets/MONGODB_KEYFILE --replSet $RS_NAME --shardsvr --dbpath /data/db --port 27017
MYIP=192.168.1.10
CONFIG="{_id:\"$RS_NAME\",version:1,members:[{_id:0,host:\"$MYIP:27017\"}]}"
mongo -u $MONGO_INITDB_ROOT_USERNAME -p $MONGO_INITDB_ROOT_PASSWORD --authenticationDatabase admin --eval "printjson(rs.initiate($CONFIG))"
# members="192.168.1.11 192.168.1.12"
# for member in $members; do
# if [ "$member" != "$MYIP" ]; then
# mongo -u $MONGO_INITDB_ROOT_USERNAME -p $MONGO_INITDB_ROOT_PASSWORD --authenticationDatabase admin --eval "printjson(rs.add('$member:27017'))"
# sleep 5
# fi
# done
mkdir -p /data/db/.metadata
touch /data/db/.metadata/.replicaset
mongod --shutdown && mongod --keyFile /run/secrets/MONGODB_KEYFILE --replSet $RS_NAME --shardsvr --dbpath /data/db --port 27017
else
mongod --keyFile /run/secrets/MONGODB_KEYFILE --replSet $RS_NAME --shardsvr --dbpath /data/db --port 27017
fi
version: '2'
services:
mongod:
image: mongo:3.4.10
environment:
RS_NAME: $RS_NAME
MONGO_INITDB_ROOT_USERNAME: $MONGO_INITDB_ROOT_USERNAME
MONGO_INITDB_ROOT_PASSWORD: $MONGO_INITDB_ROOT_PASSWORD
entrypoint: /custom-entrypoint.sh
Right, the initial user must be configured without --replSet
, but then the daemon must be restarted with --replSet
in order to run rs.initiate()
.
Here's the simple one-liner I've been using to test with:
$ docker run -it --rm -e MONGO_INITDB_ROOT_USERNAME=admin -e MONGO_INITDB_ROOT_PASSWORD=example mongo --replSet test
The following patch fixes the issue (and allows the above command to complete successfully), but at the cost of removing --replSet
for that initial mongod
instance, which may or may not be acceptable:
diff --git a/3.4/docker-entrypoint.sh b/3.4/docker-entrypoint.sh
index e1d5642..f4fd398 100755
--- a/3.4/docker-entrypoint.sh
+++ b/3.4/docker-entrypoint.sh
@@ -76,26 +76,45 @@ _mongod_hack_ensure_arg() {
mongodHackedArgs+=( "$ensureArg" )
fi
}
-# _mongod_hack_ensure_arg_val '--some-arg' 'some-val' "$@"
+# _mongod_hack_ensure_no_arg '--some-arg' "$@"
# set -- "${mongodHackedArgs[@]}"
-_mongod_hack_ensure_arg_val() {
- local ensureArg="$1"; shift
- local ensureVal="$1"; shift
+_mongod_hack_ensure_no_arg() {
+ local ensureNoArg="$1"; shift
+ mongodHackedArgs=()
+ while [ "$#" -gt 0 ]; do
+ local arg="$1"; shift
+ if [ "$arg" = "$ensureNoArg" ]; then
+ continue
+ fi
+ mongodHackedArgs+=( "$arg" )
+ done
+}
+# _mongod_hack_ensure_no_arg '--some-arg' "$@"
+# set -- "${mongodHackedArgs[@]}"
+_mongod_hack_ensure_no_arg_val() {
+ local ensureNoArg="$1"; shift
mongodHackedArgs=()
while [ "$#" -gt 0 ]; do
local arg="$1"; shift
case "$arg" in
- "$ensureArg")
+ "$ensureNoArg")
shift # also skip the value
continue
;;
- "$ensureArg"=*)
+ "$ensureNoArg"=*)
# value is already included
continue
;;
esac
mongodHackedArgs+=( "$arg" )
done
+}
+# _mongod_hack_ensure_arg_val '--some-arg' 'some-val' "$@"
+# set -- "${mongodHackedArgs[@]}"
+_mongod_hack_ensure_arg_val() {
+ local ensureArg="$1"; shift
+ local ensureVal="$1"; shift
+ _mongod_hack_ensure_no_arg_val "$ensureArg" "$@"
mongodHackedArgs+=( "$ensureArg" "$ensureVal" )
}
# TODO what do to about "--config" ? :(
@@ -158,7 +177,11 @@ if [ "$originalArgOne" = 'mongod' ]; then
pidfile="$(mktemp)"
trap "rm -f '$pidfile'" EXIT
- _mongod_hack_ensure_arg_val --bind_ip 127.0.0.1 "$@"
+ # remove "--auth" and "--replSet" for our initial startup (see https://docs.mongodb.com/manual/tutorial/enable-authentication/#start-mongodb-without-access-control)
+ _mongod_hack_ensure_no_arg --auth "$@"
+ _mongod_hack_ensure_no_arg_val --replSet "${mongodHackedArgs[@]}"
+
+ _mongod_hack_ensure_arg_val --bind_ip 127.0.0.1 "${mongodHackedArgs[@]}"
_mongod_hack_ensure_arg_val --port 27017 "${mongodHackedArgs[@]}"
sslMode="$(_mongod_hack_have_arg '--sslPEMKeyFile' "$@" && echo 'allowSSL' || echo 'disabled')" # "BadValue: need sslPEMKeyFile when SSL is enabled" vs "BadValue: need to enable SSL via the sslMode flag when using SSL configuration parameters"
One concrete side-effect is that if anyone was using rs.initiate()
from within /docker-entrypoint-initdb.d
, then this would break that (since we've stripped --replSet
, and thus cannot initialize a replica set). Perhaps the fix would be to only remove --replSet
if we've got MONGO_INITDB_ROOT_USERNAME
and MONGO_INITDB_ROOT_PASSWORD
set (similar to other bits of this entrypoint).
For reference, here's that version:
diff --git a/3.4/docker-entrypoint.sh b/3.4/docker-entrypoint.sh
index e1d5642..871afa6 100755
--- a/3.4/docker-entrypoint.sh
+++ b/3.4/docker-entrypoint.sh
@@ -76,26 +76,45 @@ _mongod_hack_ensure_arg() {
mongodHackedArgs+=( "$ensureArg" )
fi
}
-# _mongod_hack_ensure_arg_val '--some-arg' 'some-val' "$@"
+# _mongod_hack_ensure_no_arg '--some-arg' "$@"
# set -- "${mongodHackedArgs[@]}"
-_mongod_hack_ensure_arg_val() {
- local ensureArg="$1"; shift
- local ensureVal="$1"; shift
+_mongod_hack_ensure_no_arg() {
+ local ensureNoArg="$1"; shift
+ mongodHackedArgs=()
+ while [ "$#" -gt 0 ]; do
+ local arg="$1"; shift
+ if [ "$arg" = "$ensureNoArg" ]; then
+ continue
+ fi
+ mongodHackedArgs+=( "$arg" )
+ done
+}
+# _mongod_hack_ensure_no_arg '--some-arg' "$@"
+# set -- "${mongodHackedArgs[@]}"
+_mongod_hack_ensure_no_arg_val() {
+ local ensureNoArg="$1"; shift
mongodHackedArgs=()
while [ "$#" -gt 0 ]; do
local arg="$1"; shift
case "$arg" in
- "$ensureArg")
+ "$ensureNoArg")
shift # also skip the value
continue
;;
- "$ensureArg"=*)
+ "$ensureNoArg"=*)
# value is already included
continue
;;
esac
mongodHackedArgs+=( "$arg" )
done
+}
+# _mongod_hack_ensure_arg_val '--some-arg' 'some-val' "$@"
+# set -- "${mongodHackedArgs[@]}"
+_mongod_hack_ensure_arg_val() {
+ local ensureArg="$1"; shift
+ local ensureVal="$1"; shift
+ _mongod_hack_ensure_no_arg_val "$ensureArg" "$@"
mongodHackedArgs+=( "$ensureArg" "$ensureVal" )
}
# TODO what do to about "--config" ? :(
@@ -158,7 +177,13 @@ if [ "$originalArgOne" = 'mongod' ]; then
pidfile="$(mktemp)"
trap "rm -f '$pidfile'" EXIT
- _mongod_hack_ensure_arg_val --bind_ip 127.0.0.1 "$@"
+ # remove "--auth" and "--replSet" for our initial startup (see https://docs.mongodb.com/manual/tutorial/enable-authentication/#start-mongodb-without-access-control)
+ _mongod_hack_ensure_no_arg --auth "$@"
+ if [ "$MONGO_INITDB_ROOT_USERNAME" ] && [ "$MONGO_INITDB_ROOT_PASSWORD" ]; then
+ _mongod_hack_ensure_no_arg_val --replSet "${mongodHackedArgs[@]}"
+ fi
+
+ _mongod_hack_ensure_arg_val --bind_ip 127.0.0.1 "${mongodHackedArgs[@]}"
_mongod_hack_ensure_arg_val --port 27017 "${mongodHackedArgs[@]}"
sslMode="$(_mongod_hack_have_arg '--sslPEMKeyFile' "$@" && echo 'allowSSL' || echo 'disabled')" # "BadValue: need sslPEMKeyFile when SSL is enabled" vs "BadValue: need to enable SSL via the sslMode flag when using SSL configuration parameters"
@@ -214,12 +239,6 @@ if [ "$originalArgOne" = 'mongod' ]; then
roles: [ { role: 'root', db: $(jq --arg 'db' "$rootAuthDatabase" --null-input '$db') } ]
})
EOJS
-
- mongo+=(
- --username="$MONGO_INITDB_ROOT_USERNAME"
- --password="$MONGO_INITDB_ROOT_PASSWORD"
- --authenticationDatabase="$rootAuthDatabase"
- )
fi
export MONGO_INITDB_DATABASE="${MONGO_INITDB_DATABASE:-test}"
(very interested in identifying more edge cases in this version, since it's something I'd be willing to accept as a PR if we can verify that most other currently-working use cases still work properly)
Hi, i think i have the same issues here, still very new to both docker and mongodb, i have a replica set all up and running, but i was going to enable auth mode on the replica set, so was just looking for the config file so i could add the line in to set the Key but there and switch it on, but could not find the conf file and i'm kinda stuck. not sure how to turn this on. any help / ideas ?
@adalga @yosifkit
Using dockerfile to execute a JS script successfully enables Mongo copy set permission settings.
The following code can be executed correctly
The contents of docker-compose.ym are as follows
content-mongo:
build:
context: ./content/docker/mongo
dockerfile: Dockerfile
volumes:
- /docker/content-mongo/db:/data/db
ports:
- 27018:27017
The contents of dockerfile are as follows
FROM mongo:3.4
COPY ./setup.js /etc/
RUN chmod +x /etc/setup.js
CMD ["/etc/setup.js"]
The contents of /etc/setup.js are as follows
#!/bin/bash
mongod --replSet replset0 --auth &
sleep 5
mongo admin<<EOF
var config = {
"_id": "replset0",
"members": [
{
"_id": 0,
"host": "192.168.1.233:27018"
}
]
};
rs.initiate(config)
EOF
sleep 3
mongo admin <<EOF
db.createUser({
user: "test",
pwd: "test",
roles: [ { role: "root", db: "admin" } ]
})
EOF
tail -f /dev/null
Where there is a problem, I hope to correct it. Thanks
@tianon - So, just to confirm, was the merge that closed this issue supposed to support replica sets with auth, or is the solution proposed by @ft0907 the recommended approach?
I have been able to get this to work when brewing my own similarly to the comment immediately above, but have not been able to do it with just additions to /docker-entrypoint-initdb.d
as per the documentation. See https://github.com/PeterParker/replica-set-mongo-in-docker-with-auth as an example of this.
the initial user must be configured without
--replSet
, but then the daemon must be restarted with--replSet
in order to runrs.initiate()
.
The PR that closed this issue was not to let /docker-entrypoint-initdb.d
do an rs.initiate
with --auth
(or the user/pass env vars). You need to have the --replSet
flag set and the PR removed it for the temporary mongod
that runs during /docker-entrypoint-initdb.d
if you are trying to create a user. The point of the PR was to remove options that don't work when both are turned on with the empty database.
Even if you do an rs.initiate
with the initdb.d scripts, it will not be correct: #339 (comment). (though it seems you have found a clever hack around that with extra_hosts: ["database-no-auth:127.0.0.1"]
in your compose file 😍)
@yosifkit - Okay, thanks, that clarifies things. It looks like a custom entrypoint is the way forward.
Also, by the way, many thanks for the work you and others have done on these containers -- they're awesome.
Just for clarification, how would I go about creating a replicaset that also has a user in it?
# docker-compose.yaml
...
mongo_1:
image: mongo:latest
deploy:
replicas: 1
update_config:
parallelism: 1
restart_policy:
condition: on-failure
environment:
MONGO_INITDB_ROOT_USERNAME: "{{ mongo_root_username }}"
MONGO_INITDB_ROOT_PASSWORD: "{{ mongo_root_password }}"
volumes:
- {{ mongo_1_data_dir }}:/data/db
- {{ token_dir }}:/tokens
command: "mongod --replSet {{ mongo_replica_set_name }} --keyFile /tokens/{{ mongo_replica_set_name }}.key"
ports:
- "{{ mongo_1_port }}:27017"
This creates a replica set with no users. I'm slightly confused at how to go about getting this to work. Can someone assist?
@coltenkrauter this might help
I've just spent 3h looking around to find out why this image cannot be setup correctly with both authentication and a replicaSet. That's why...
You may want to put a caveat in your documentation 🙃
I managed to create a replicaSet with an user on it by starting mongo as background process and then initiating the replicaSet and creating the user name.
services:
mongodb:
image : mongo:5.0.5
container_name: mongodb
command : [ "/bin/bash","-c", "chmod +x /conf/mongo-init.sh && /conf/mongo-init.sh"]
environment:
- PUID=1000
- PGID=1000
restart: always
env_file:
- ${ENVFILE}
networks:
- default
ports:
- 27017:27017
volumes:
- database:/data
- mongo-conf:/conf
--- mongo-init.sh
#!/bin/bash
logfile="mongo.out"
expected_message="Waiting for connections"
chmod 400 /conf/mongodb.key
mongod --quiet --logpath $logfile --replSet rs0 --bind_ip_all --port 27017 --dbpath /data/db/ --shardsvr --auth --keyFile /conf/mongodb.key &
while [ ! -f "$logfile" ]
do
sleep 1
done
while :
do
if grep -q "$expected_message" "$logfile"; then
echo "Found the expected message: $expected_message"
break
fi
sleep 1
done
mongo --eval '
rs.initiate({
_id: "rs0",
version: 1,
members: [
{ _id: 0, host: "localhost:27017" }
]
})
'
sleep 5
mongo admin --eval "db.createUser({ user: '${MONGO_INITDB_ROOT_USERNAME}', pwd: '${MONGO_INITDB_ROOT_PASSWORD}', roles: [ { role: 'root', db: 'admin' } ] })"
tail -f $logfile