Docker en opération

Pour ce cours, nous avons opté pour une solution self-hosting. Si on voulait mettre en production notre SGBDR, il y a quelques considérations :

  • L'emplacement physique de nos données

  • La sécurité de nos données at rest

  • La sécurité de nos communications avec le SGBDR

  • Les options de configuration de MariaDB

Pour suivre ces cours, vous allez créer une instance chez votre fournisseur cloud (e.g. Scaleway)

Installer Docker sur votre Serveur

Nous faisons un déploiement de MariaDB avec Docker. Jusqu'au présent, nous avons installé Docker sur un PC type Desktop, en utilisant Docker Desktop. Vous trouverez un guide d'installation ici.

Pour installer docker sur une instance Linux, il y aura quelques démarches à faire. Vous pouvez consulter la documentation officielle ici : https://docs.docker.com/engine/install/

Par exemple, sur Ubuntu, les étapes sont les suivantes (attention, à faire avec sudo ou en tant que root) :

# Désinstaller les anciennes versions de docker
sudo apt-get remove docker docker-engine docker.io containerd runc

# Mettre à jour les indexes des packages
sudo apt-get update

# Installer les packages tiers nécessaires pour ajouter Docker parmi nos indexes
sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
    
# Télécharger la clé publique de Docker qui va vérifier l'authenticité
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

# Installer le dépot Docker parmi les sources connues de notre distribution
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo chmod a+r /etc/apt/keyrings/docker.gpg
    
# Remettre à jour nos indexes
sudo apt-get update

# Installer Docker 
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin

Une fois installé, vous pouvez vérifier que le processus fonctionne correctement avec :

Accorder accès au groupe Docker

Certains de vos utilisateurs Linux auront le droit à contrôler les processus Docker, sans passer par sudo.

Pour leur donner ce droit, ajoutez les utilisateurs concernés au groupe docker :

L'emplacement des données

Lorsqu'on crée un Container pour tourner MariaDB, Docker crée par défaut un volume de stockage associé au Container. En revanche, ce volume sera supprimé avec le Container.

Il faudrait provisionner un emplacement fixe pour les données de MariaDB :

  1. qui est bien connu

  2. qui sera toujours présent, même si l'instance redémarre

  3. facilement montable sur une autre instance, si jamais la première disparaît

  4. idéalement, le volume sera crypté

Nous avons déjà vu comment provisionner un volume qui répond aux critères 1, 2 et 3 en montant un volume sur notre instance Ubuntu.

En revanche, comment faire une sorte que les données soient cryptées sur le disque dur ? Il y a trois niveaux de cryptage possibles :

  1. Au niveau du disque dur

  2. Par table par MariaDB

  3. Via votre API

Ici, nous regardons le premier niveau de cryptage, avec l'extension LUKS (Linux Unified Key Setup). Nous allons provisionner un volume qui est crypté par défaut.

D'abord, il faut provisionner un volume chez votre fournisseur, identifier le volume avec lsblk, et formater le volume :

Nous installons LUKS avec la commande cryptsetup :

Nous avons déjà vu que quand on parle de la cryptographie, il faudrait nécessairement un (ou plusieurs) clé(s) qui permettent de crypter et décrypter des données. Avec LUKS, nous allons créer et gérer ces clés.

Nous commençons par générer une clé forte, en utilisant l'outil openssh par exemple. Plus la clé est longue, plus elle est sécurisée :

Nous avons formaté le volume crypté. Ce volume ne peut pas être lu comme un volume normal. Il faudrait fournir la clé de cryptage. Donc avant de le monter, il faut configurer le périphérique :

Nous disposons maintenant d'un nouveau périphérique "virtuel" qui se trouve à /dev/mapper/mariadbdata. Ce périphérique peut être formaté et monté comme n'importe quel autre périphérique :

Nous pouvons donc naviguer à /mnt/mariadbdata et créer et modifier les fichiers. Nous pouvons aussi préciser ce chemin pour le stockage de nos données MariaDB dans notre docker-compose.yml par exemple.

Mais, avant de le faire, il y a un problème : le volume crypté n'existera uniquement tant que l'instance ne redémarre pas. Au redémarrage, il faudrait fournir de nouveau le mot de passe de cryptage.

L'avantage de LUKS est qu'on peut ajouter plusieurs clés en parallèle. Nous allons créer une autre clé qui sera plutôt stocké dans un fichier sur un autre volume. Au démarrage, nous allons utiliser ce fichier pour déverrouiller notre volume.

Nous ajoutons cette clé parmi les clés possibles pour LUKS :

A tout moment, nous pouvons visionner l'état de nos clés :

Testons cette nouvelle clé. D'abord, fermons le volume qui a été ouvert via la clé initiale :

Ensuite, montons le volume en précisant plutôt le fichier comme clé :

C'est super. Maintenant, on peut déverrouiller un volume sans fournir manuellement un mot de passe. Comment le faire au démarrage ?

Vous vous souvenez du fichier /etc/fstab ? Nous avons l'équivalent pour les volumes cryptés : /etc/crypttab, qui prend le format suivant :

Les options sont :

  • target name : le nom de notre volume, pour nous mariadbdata

  • source device : le UUID de notre volume. On peut trouver le UUID avec blkid /dev/sda. Dans mon exemple, j'ai trouvé le UUID 9096ac61-20bf-4c88-a770-35e6b71897b7.

  • key-file : le chemin absolu du fichier avec la clé, pour nous /etc/.luks-mariadbdata-key

  • options : les options du système de cryptographie. Pout nous luks

Nous ajoutons alors la ligne suivante :

Cette procédure assurera que le périphérique virtuel est créé (le volume crypté est ouvert). En revanche, il faut quand même monter le volume au démarrage, comme nous avons fait avec un volume normal. On modifie donc /etc/fstab :

Vérifiez bien que vous votre volume monte correctement :

Ensuite, redémarrez votre instance pour tester.

Références:

docker-compose.yml

Nous allons créer un sous-répertoire sur notre volume crypté pour stocker les données de notre SGBDR:

Nous sommes prêts maintenant à rédiger une docker-compose.yml pour production.

Avant, nous avons marqué le mot de passe pour l'utilisateur root directement dans le docker-compose.yml. Ceci est une très mauvaise idée en production, car on pourrait récupérer le mot de passe en lisant le fichier docker-compose.yml.

Nous allons plutôt créer des variables d'environnement dans notre shell (qui persisteront uniquement la durée de notre SHELL) :

Dans le docker-compose.yml, nous utilisons ces variables avec ${ NOM_DE_VARIABLE }.

On lance notre DB avec :

Testez la connexion à votre base de données déployée (attention, ici, on utilise le port 7100) !

Obliger une connexion sécurisée

Actuellement, toute communication avec notre SGBDR sera non crypté. Nous ne l'avons pas obligé, et en production, ceci est une très mauvaise pratique ! N'importe qui pourrait sniffer nos communications et dériver nos mots de passes ou nos données secrètes.

Idéalement, on sécurise des communications avec TLS. C'est l'équivalent de la connexion SSL qu'on voit dans les navigateurs. Non seulement on établit des connexions cryptées, mais aussi, on établit un niveau de confiance auprès de notre base de données.

En revanche, créer des certificats exige un nom de domaine, qu'on ne va pas créer pendant ce cours. Je vous encourage quand même à suivre le guide marque dessus.

Pour ce cours, nous nous contenterons à obliger des connexions via SSH exclusivement. C'est-à-dire, nous devons d'abord établir un tunnel SSH vers l'instance hôte, et ensuite envoyer des commandes via ce tunnel crypté.

Ceci est facile avec Docker ! Nous modifions notre docker-compose.yml, en ajoutant 127.0.0.1 devant le port qu'on a ouvert :

En ajoutant 127.0.0.1, nous signalons à Docker qu'il faut accepter uniquement les connexions provenant de l'hôte même, et pas du monde extérieur.

Essayer : modifiez vous-même votre docker-compose.yml, redémarrez votre service, et essayez de vous connecter via votre client local.

La seule façon d'établir une connexion du monde extérieur est d’établir d'abord une connexion SSH à l'instance. Dans votre client, vous auriez l'option de configurer la connexion SSH et fournir la clé privée nécessaire pour la connexion.

Sécuriser MariaDB

MySQL (et MariaDB) contient par défaut un script qui permet d’affecter des règles de sécurité de base

  • Désactiver les connexions anonymes

  • Assurer des utilisateurs admin/root ne peuvent connexion uniquement par la machine locale

  • Assurer que l’utilisateur root a un mot de passe (et qu’il est suffisamment sécurisé)

  • Supprimer la base de données de test (parfois installé par défaut)

Options de production MariaDB

Parfois, il est pertinent de modifier la configuration de MariaDB pour mieux répondre à la charge sur le SGBDR. Par exemple :

  • Le nombre de connexions concurrentes autorisées

  • La durée de vie des connexions avant de les fermer

Par défaut, un fichier de configuration se trouve déjà dans l'image MariaDB à /etc/mysql/mariadb.cnf.

Nous pouvons remplacer ce fichier, simplement en le recréant sur notre instance locale (à côté de docker-compose.yml) :

Nous ajoutons les deux dernières lignes :

  • max_connections : fixer le nombre de connexions total à 1000

  • wait_timeout : fermez les connexions après 130 secondes

Afin d'appliquer ces réglages, il suffit d'ajouter ce fichier dans la partie volumes de docker-compose.yml :

Il faut redémarrer votre service pour que la modification soit prise en compte.

À tout moment, on peut récupérer la liste de sous-processus en train de traiter des requêtes, avec (en tant que root) :

Mis à jour