Recently I’m working on a project that has been containerized already. However, the database has not been integrated into this bundle yet. This time I will use the official mongodb image from docker hub and initialize a database, credentials for the app.
Goals:
- Start a mongodb container and initialize a mongodb database.
- Add a new user in the database with `readWrite` permission.
- Pass all credentials into the container securely.
Let’s check out the official mongodb image page first https://hub.docker.com/_/mongo. Fortunately, the image allows us to pass environment variables to initialize the root user of mongodb.
$ docker run -d --name some-mongo -e MONGO_INITDB_ROOT_USERNAME=mongoadmin -e MONGO_INITDB_ROOT_PASSWORD=secret mongo
This is not recommended. We should never pass sensitive information as plain text into the container. docker inspect <container id>
command can show all environment variables you have defined in the run command.
Docker provides alternative way to safely pass sensitive information to containers.
As an alternative to passing sensitive information via environment variables, _FILE may be appended to the previously listed environment variables, causing the initialization script to load the values for those variables from files present in the container.
Let’s create a docker-compose.yml
file.
version: "3.5"
services:
mongodb:
build: ./mongodb
ports:
- 27017:27017
environment:
MONGO_INITDB_ROOT_PASSWORD_FILE: /run/secrets/mongodb_root_password
MONGO_INITDB_ROOT_USERNAME_FILE: /run/secrets/mongodb_root_username
MONGO_INITDB_DATABASE: admin
MONGO_USERNAME_FILE: /run/secrets/mongodb_username
MONGO_PASSWORD_FILE: /run/secrets/mongodb_password
MONGO_DATABASE: parrot
secrets:
- mongodb_root_password
- mongodb_root_username
- mongodb_password
- mongodb_usernamesecrets:
mongodb_root_password:
file: /mongodb/.mongodb_root_password
mongodb_root_username:
file: /mongodb/.mongodb_root_username
mongodb_password:
file: /mongodb/.mongodb_password
mongodb_username:
file: /mongodb/.mongodb_username
Docker compose defines 4 secrets. You can either use docker secret create ...
command or create 4 files in the same dictionary with the docker-compose file. Contents of 4 files are confidential strings.
.mongodb_root_password
only includes password for root user of mongodb.
password_for_root
You can tell the contents of rest of files by their file names. Each file only has one line of string.
These files will be mounted into the container via docker TLS when we start the service. They can be found under /run/secrets/
in the running container. *_FILE
variables under the environment
have paths to those files. More details are here: https://docs.docker.com/engine/swarm/secrets/.
MONGO_INITDB_DATABASE
allows you to specify the name of a database to be used for creation scripts in /docker-entrypoint-initdb.d/*.js
MONGO_DATABASE
is the name of the database for the app. In this case it’s parrot
.
Official mongodb image will only create a root user. We need to create a database parrot
and a user who can only perform read and write operations on it.
To organize the files, we create a dictionary mongodb
in the root dictionary and copy secret files into it.
Now create a script to initialize the mongodb for the app
./mongodb/mongo-init.sh
#!/bin/bash
file_env() {
local var="$1"
local fileVar="${var}_FILE"
local def="${2:-}"
if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
exit 1
fi
local val="$def"
if [ "${!var:-}" ]; then
val="${!var}"
elif [ "${!fileVar:-}" ]; then
val="$(< "${!fileVar}")"
fi
export "$var"="$val"
unset "$fileVar"
}file_env "MONGO_USERNAME"
file_env "MONGO_PASSWORD"mongo -- ${MONGO_INITDB_DATABASE} <<EOF
const MONGO_INITDB_ROOT_USERNAME = '$MONGO_INITDB_ROOT_USERNAME';
const MONGO_INITDB_ROOT_PASSWORD = '$MONGO_INITDB_ROOT_PASSWORD';
const MONGO_DATABASE = '$MONGO_DATABASE';
const MONGO_USERNAME = '$MONGO_USERNAME';
const MONGO_PASSWORD = '$MONGO_PASSWORD';
db.auth(MONGO_INITDB_ROOT_USERNAME, MONGO_INITDB_ROOT_PASSWORD);
var db = db.getSiblingDB(MONGO_DATABASE)
db.createUser({user: MONGO_USERNAME, pwd: MONGO_PASSWORD, roles: [{role: 'readWrite', db: MONGO_DATABASE}]});
EOF
Let’s go through the script.
file_env
will get value of "MONGO_USERNAME"
from the secret file and export it into the environment of the container.
mongo
command will execute the script inside the EOF block. The script helps log in as the root user, create parrot
database, then create a user.
Last step, create the Dockerfile
to pull the image and copy the mongo-init.sh
to the container.
FROM mongo:latestADD ./mongo-init.sh /docker-entrypoint-initdb.d/
pull the latest mongodb image and copy the initialization script into the container. When the mongodb container is up, app have the access to the database.
MONGODB_URI=mongodb://<username>:<password>@mongodb:27017/parrot
mongodb
before the port is the service name we defined in the docker-compose file.
docker-compose up
will start a mongodb container which maps its 27017 to 27017 of the host.