Je vous propose dans cet article de jouer un peu avec Docker pour monter une instance MongoDB sur un Ubuntu Server 14.04 et la rendre accessible depuis internet. Le but étant de fournir un base de données partagée entre plusieurs développeurs pour un projet assez simple.

Nous verrons donc comment récupérer une image Docker de Mongo qui va bien à travers Docker Hub et comment mettre en place quelques mécanismes de sécurité de base (authentification + connexions SSL).

L’article peut paraître assez long mais pas de panique, c’est dû aux explications et aux exemples. Le nombre de commandes à taper n’est pas très important.

C’est parti !

Table des matières

  1. Installer Docker
  2. Récupérer une image MongoDB
  3. Créer des dossiers permanents pour les bases de données MongoDB
  4. Lancer le server MongoDB en mode authentifié
  5. Lancer un client Mongo pour créer un super-admin
  6. Ajouter un utilisateur pour notre base de données
  7. Encrypter notre connexion avec SSL

Installer Docker

La première chose à faire est d’installer Docker sur notre Ubuntu Server.

Docker étant un projet qui bouge vite (très vite !), on n’utilisera pas directement un apt-get install classique mais la ligne de commande suivante :

wget -qO- https://get.docker.com/ | sh

Après avoir téléchargé pas mal de choses, Docker devrait être installé correctement. On peut tester avec :

sudo docker ps

Si ce n’est pas le cas et que vous avez une erreur du style

FATA[0000] Get http:///var/run/docker.sock/v1.17/containers/json: dial unix /var/run/docker.sock: no such file
or directory.
Are you trying to connect to a TLS-enabled daemon without TLS?

il faut un peu bidouiller en lançant une installation par apt classique puis une suppression par apt et enfin une réinstallation par la méthode ci-dessus…

sudo apt-get install docker.io
sudo docker version
sudo apt-get remove docker.io
wget -qO- https://get.docker.com/ | sh

Par défaut Docker s’exécute à travers un sudo mais on peut se simplifier un peu la vie en ajoutant notre utilisateur à un groupe docker (ex avec mon login pierrot):

sudo usermod -aG docker pierrot

On peut vérifier la bonne installation avec :

docker run hello-world

Si tout va bien vous devriez voir les lignes suivantes :

pierrot@dev:~$ docker run hello-world
Unable to find image 'hello-world:latest' locally
31cbccb51277: Pull complete
e45a5af57b00: Pull complete
511136ea3c5a: Already exists
hello-world:latest: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security.
Status: Downloaded newer image for hello-world:latest
Hello from Docker.
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(Assuming it was not already locally available.)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
For more examples and ideas, visit:
http://docs.docker.com/userguide/

sources : https://docs.docker.com/installation/ubuntulinux

Récupérer une image MongoDB

Un des gros avantages de Docker est son Hub qui met à disposition des milliers d’images prêtes à l’utilisation.

On va donc récupérer une image officielle Mongo dans sa dernière version (à ce jour 3.0) :

docker pull mongo:latest

Attention de bien spécifier un tag (ici latest) sinon Docker récupérera TOUTES les versions disponibles ! Et ça en fait quelques unes, je le sais, je n’avais pas mis de tag lors de mes premiers tests….

Docker récupère l’image Mongo :

pierrot@dev:~$ docker pull mongo:latest
4f903438061c: Pull complete
1265e16d0c28: Pull complete
...
9552b2f0723b: Pull complete
511136ea3c5a: Already exists
mongo:latest: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security.
Status: Downloaded newer image for mongo:latest

Il est toujours intéressant de jeter un œil à ce qui se passe en coulisse, on peut donc aller décortiquer le DockerFile associé à l’image téléchargée.

sources : https://registry.hub.docker.com/_/mongo/

Créer des dossiers permanents pour les bases de données MongoDB

Afin de pouvoirs sauvegarder les bases de données, on spécifiera à Docker un binding de /data/db (qui est le lieu de stockage par défaut de MongoDB) vers un chemin local.

Si ce n’est pas très clair, pas de soucis ça le sera quelques lignes plus bas. Pour l’instant on crée juste le dossier qui va bien :

sudo mkdir -p /opt/mongodb/db

Lancer le server MongoDB en mode authentifié

docker run -p 27017:27017 -v /opt/mongodb/db:/data/db --name my-mongo-dev -d mongo mongod --auth

Explications :

  • docker run => on crée un container
  • -p 27017:27017 => dont le port 27017 (port par défaut de mongod) sera accessible en dehors du container
  • -v <local_rep>:<container_rep> => le dossier /data/db (localisation des bases MongoDB) sera en fait un “lien” vers le dossier que l’on a créé juste au-dessus
  • –name my-mongo-dev => nom personnalisé du container
  • -d => le container sera lancé en mode démon au sens linux donc en tâche de fond
  • mongo => nom de l’image associée au container (i.e. celle que l’on a téléchargé plus tôt)
  • mongod –auth => lancement du serveur mongo en mode authentification requise

Si tout va bien, un docker ps nous affiche notre container et un docker logs nous donne les logs de démarrage de notre serveur MongoDB :

pierrot@dev:~$ docker run -p 27017:27017 -v /opt/mongodb/db:/data/db --name my-mongo-dev -d mongo mongod --auth
5e90c0b309580aa47f1a6404e43071e6b50198d0ea8b383e4c32cdad18ddc990
pierrot@dev:~$ docker ps
CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS                      NAMES
5e90c0b30958        mongo:latest        "/entrypoint.sh mong   5 seconds ago       Up 5 seconds        0.0.0.0:27017-&gt;27017/tcp   my-mongo-dev  
pierrot@dev:~$ docker logs my-mongo-dev
2015-04-06T17:24:05.787+0000 I JOURNAL  [initandlisten] journal dir=/data/db/journal
2015-04-06T17:24:05.788+0000 I JOURNAL  [initandlisten] recover : no journal files present, no recovery needed
2015-04-06T17:24:07.636+0000 I JOURNAL  [initandlisten] preallocateIsFaster=true 17.56
...
2015-04-06T17:24:41.419+0000 I STORAGE  [FileAllocator] done allocating datafile /data/db/local.0, size: 64MB,  took 0.016 secs
2015-04-06T17:24:41.434+0000 I NETWORK  [initandlisten] waiting for connections on port 27017

sources :

Lancer un client Mongo pour créer un super-admin

À ce stade, il est possible de se connecter à notre serveur de l’extérieur mais nous n’aurons accès à aucune collection (i.e. table) ni en lecture ni en écriture (à part la collection test).

Pour remédier à cela, il nous faut créer un utilisateur “super-admin” qui servira à créer d’autres utilisateurs. Cela se fait avec un client mongo lancé sur le même système que le serveur mongod grâce à la “localhost exception“.

Mais comment accéder au même système que notre serveur qui n’est autre que le container docker lancé en mode démon ? Le plus simple est d’utiliser la commande exec introduite avec la version 1.3 de Docker afin de lancer un client mongo dans le même container :

docker exec -it my-mongo-dev mongo

Ensuite, on crée notre super-admin dans la console mongo :

> use admin
switched to db admin
> db.createUser(
... {
... user: "siteUserAdmin",
... pwd: "unPasswordQuiVaBien",
... roles: [{role: "userAdminAnyDatabase", db: "admin"}]
... }
... )
Successfully added user: {
"user" : "siteUserAdmin",
"roles" : [
{
"role" : "userAdminAnyDatabase",
"db" : "admin"
}
]
}

Nous voilà prêt pour créer d’autres utilisateurs.
sources :

Ajouter un utilisateur pour notre base de données

Il vaut mieux éviter de travailler directement avec le super-admin, on ne va l’utiliser que pour créer d’autres utilisateurs.

Pour avoir quelque chose d’assez simple, on ne va créer qu’un seul utilisateur qui aurait accès en lecture/écriture sur toute la base de données avec laquelle on veut travailler.

On commence par lancer un client mongo sur n’importe quelle machine en se connectant avec notre super-admin :

mongo <ip_server> -u siteUserAdmin -p unPasswordQuiVaBien --authenticationDatabase admin

Puis, on switche sur la base de données que l’on veut utiliser (ici kanban) et on crée un admin :

> use kanban
switched to db kanban
> db.createUser(
... {
... user: "kanbanUser",
... pwd: "unAutrePasswordQuiVaBien",
... roles: ["dbOwner"]
... }
... )
Successfully added user: { "user" : "kanbanUser", "roles" : [ "dbOwner" ] }

On peut maintenant faire le test en se connectant directement sur la base kanban avec notre nouvel utilisateur :

mongo <ip_server>/kanban -u kanbanUser -p unAutrePasswordQuiVaBien --authenticationDatabase kanban
> db.card.insert(
... {
... name: "test"
... }
... )
WriteResult({ "nInserted" : 1 })
> db.card.find()
{ "_id" : ObjectId("5522d47b07994c60f49b8674"), "name" : "test" }

On peut faire le test en se connectant sans les informations du kanbanUser et voir que l’accès est bien restreint :

mongo <ip_server>/kanban
> db.card.find()
Error: error: { "$err" : "not authorized for query on kanban.card", "code" : 13 }

sources : http://docs.mongodb.org/manual/tutorial/add-user-to-database/

Encrypter notre connexion avec SSL

Le serveur MongoDB peut être lancé pour accepter des connexions cryptées avec SSL. Pour cela, il nous faut un certificat d’authentification et comme nous sommes ici en phase de développement, nous allons créer un certificat auto-signé avec OpenSSL.

On commence par se placer dans le dossier qui va bien et on passe en root.

cd /etc/ssl
sudo su

Puis on utilise la commande suivante pour générer un certificat et une clé.

openssl req -newkey rsa:2048 -new -x509 -days 365 -nodes -out mongodb-cert.crt -keyout mongodb-cert.key

Ensuite on concatène le certificat et la clé dans un fichier .pem et on quitte le mode root.

cat mongodb-cert.key mongodb-cert.crt > mongodb.pem
mkdir -p /opt/mongodb/cert
cp mongodb.pem /opt/mongodb/cert
exit

Il faut maintenant ajouter des arguments à notre serveur MongoDB. Comme nous avons fait en sorte de sauvegarder de manière permanente nos bases de données, on peut simplement supprimer le container existant et en recréer un.

pierrot@dev:~$ docker stop my-mongo-dev
08640a0f3ad8
pierrot@dev:~$ docker rm my-mongo-dev
08640a0f3ad8
pierrot@dev:/opt/mongodb$ docker run -p 27017:27017 -v /opt/mongodb/db:/data/db -v /opt/mongodb/cert:/data/cert --name my-mongo-dev -d mongo mongod --auth --sslMode requireSSL --sslPEMKeyFile /data/cert/mongodb.pem
9339de3f58786f6f46c25c9af1c3c49151936b10d8578ea9727028f8da77a209
pierrot@dev:/opt/mongodb$ docker logs my-mongo-dev
2015-04-09T18:34:41.040+0000 W CONTROL  No SSL certificate validation can be performed since no CA file has been provided; please specify an sslCAFile parameter
...
2015-04-09T18:34:41.147+0000 I CONTROL  [initandlisten] options: { net: { ssl: { PEMKeyFile: "/data/cert/mongodb.pem", mode: "requireSSL" } }, security: { authorization: "enabled" } }
2015-04-09T18:34:41.173+0000 I NETWORK  [initandlisten] waiting for connections on port 27017 ssl

Voilà, notre serveur MongoDB utilise une connexion SSL.

Il ne nous reste plus qu’à modifier la façon de lancer notre client MongoDB en ajoutant les arguments qui vont bien pour un certificat auto-signé.

mongo <ip_server>/kanban -u kanbanUser -p unAutrePasswordQuiVaBien --authenticationDatabase kanban --ssl --sslAllowInvalidCertificates

sources :