TX Printemps 2018 : Chiffrement des snapshots et externalisation

La configuration actuelle d’Alice et Bob est telle que de nombreuses machines virtuelles (VM) tournent sur chacune des deux machines. Afin de pouvoir récupérer des données en cas de défaillance, il est nécessaire d’effectuer des backups de ces VM afin de pouvoir les réinstaller ultérieurement. Cependant, la nécessité d’effectuer des backups se heurte à deux problématiques : le support de stockage et la quantité de stockage.

Proxmox fournit directement la possibilité d’effectuer des backups quotidiennement. La configuration lance un backup de chaque VM à 03h du matin, période avec peu d’activité sur ces machines. Il permet aussi une rotation des backups des VM. Cependant sans script supplémentaires, ce seul système présente plusieurs problèmes.

Tout d’abord, les backups sont stockés sur le même support sur lequel tourne les VM (les backups des VM d’Alice sont sur Alice, et celles de Bob sont sur Bob). En cas de défaillance physique d’Alice ou de Bob, les VM de la machine seront perdues ainsi que leur backup. Il ne sera donc pas possible de récupérer les VM concernées dans ce cas.

Enfin, les backups devraient pouvoir être accédés par les différents bénévoles de l’association. Cependant, ils ne doivent pas être en mesure de récupérer des informations critiques depuis ces backups. Il est donc nécessaire de chiffrer les VM que peuvent récupérer les bénévoles, et que l’accès aux backups chiffrés ne permette d’effectuer d’autres actions imprévues.

La solution implémentée est un script Python déclenché par Proxmox lors de la construction des backups de chaque VM. Ce script effectue deux opérations : il copie les backups présentes sur Alice et Bob et les copie sur l’autre machine. Ainsi toutes les backups d’Alice sont aussi présentes sur Bob et vice-versa. Cela permet de récupérer les données en cas de défaillance physique d’une des machines.

Afin de permettre aux utilisateurs de récupérer les backups chiffrés sans risque, un chroot a été créé sur Bob, permettant à l’utilisateur “snapshots” de se connecter et être en mesure de copier les backups chiffrés accessibles. Il n’est pas possible pour cet utilisateur d’accéder à une backup non chiffrée ou d’utiliser d’autres commandes quand il est connecté.

Une autre fonctionnalité implémentée est la rotation des backups chiffrés sur Alice et Bob, afin de préserver suffisamment d’espace libre sur chacune des machines. Les paramètres pour la rotation peuvent être modifiés depuis un fichier de configuration.

L’ensemble des fichiers nécessaires pour l’installation est situé dans ce répo Git. Son contenu peut être récupéré via la commande suivante :

git clone git@gitlab.utc.fr:picasoft/rotation-vm.git

Sur la machine visée pour l’installation, avec Proxmox déjà installé et fonctionnel, installez les paquets suivants :

apt-get install python3
apt-get install python3-pip

Ainsi que les paquets pythons suivants :

pip3 install rotate-backups
pip3 install python-gnupg

Note : attention à bien installer le paquet python-gnupg et non le paquet gnupg, qui malgré leurs similarités et noms proches fonctionnent différemment.

Ajoutez les fichiers du répo Git suivants sur la machine dans un même dossier :

  • hook_script.py
  • config.json

Editez le fichier /etc/vzdump.conf et modifiez la ligne suivante :

script: /root/scripts/hook_script.py

Modifiez le fichier de configuration config.json pour correspondre à l’usage nécessaire (champs du fichier de configuration détaillés plus bas dans cette page).

Le hookscript sera lancé automatiquement à chaque backup de Proxmox. Vous pouvez vérifier si ce dernier a bien fonctionné en vérifiant sur Proxmox dans la liste des tâches si le backup s’est déroulé correctement : ===== Configuration ===== ==== Proxmox ==== La configuration des backups de Proxmox s’effectue dans le fichier /etc/vzdump.conf de l’hyperviseur. On indique une directive script suivie du chemin vers le hook script afin qu’il utilise le script spécifié : <code> # vzdump default settings script: /root/scripts/hookscript.py </code>

En spéficiant cette ligne, le fichier /root/scripts/hook_script.py sera désormais appelé à chaque production de snapshot qu’elle soit déclenchée manuellement ou automatiquement.

La rotation des snapshots se réalise à l’aide du module Python rotate-backups.

La configuration de la rotation des snapshots s’effectue dans le fichier config.json. Ce fichier inclut les informations relatives aux VM et des informations essentielles pour le fonctionnement du hook_script associé à Proxmox. Voici un exemple :

backup_vm.json
{
    "BACKUP_FOLDER":"/save_folder",
    "ENCRYPTED_BACKUP_FOLDER":"/save_folder/chroot_folder/encrypted_backup_folder",
    "DESTINATION_IP":"192.168.XXX.YYY",
    "DESTINATION_FOLDER":"/save_folder/chroot_folder/encrypted_backup_folder",
    "VMs":{
        "101":
        {
            "Name": "pica01", 
            "Hypervisor": "Alice", 
            "Backup-Rota":
            {
                "Day" : 4, 
                "Week": 0,
                "Month": 0
            }
        },
        "102":
        {
            "Name": "nom_VM", 
            "Hypervisor": "Bob", 
            "Backup-Rota":
            {
                "Day" : 4, 
                "Week": 0,
                "Month": 0
            }
        }
    }
}    

Sa structure est très simple :

{
    "BACKUP_FOLDER": <backup_folder>,
    "ENCRYPTED_BACKUP_FOLDER": <encrypted backup folder>,
    "DESTINATION_IP": <IP de destination>,
    "DESTINATION_FOLDER": <dossier sur la machine distante>,
    "VMs":{ 
        "<id_vm>":
        {
        "Name": "<nom_VM>", 
        "Hypervisor": "<nom_hyperviseur>",
            "Backup-Rota":
            {
                "Day": <jour>, 
                "Week": <semaine>,  
                "Month": <mois>
            }
        }
    }
}      

Les champs spécifiées sont utilisés par le hook_script afin de localiser les différents éléments nécessaires à son fonctionnement :

  • <backup folder> : chemin du dossier dans lequel sont positionnées les backups enregistrées par Proxmox
  • <encrypted backup folder> : chemin du dossier dans lequel le hook_script doit enregistrer les backups chiffrées
  • <IP de destination> : Adresse IP de la machine distante avec laquelle effectuer le rsync
  • <dossier sur la machine distante> : chemin du dossier sur la machine distante dans lequel enregistrer les backups chiffrées transmises par le rsync

Pour chaque VM inclue dans VM sont définis les paramètres suivants :

  • <idvm> : doit correspondre à l’id de la VM telle que définie par Proxmox * <nomVM> : nom de la VM sauvegardée qui sera indiqué dans le nom du fichier chiffré
  • <nom_hyperviseur> : nom de l’hyperviseur qui sera indiqué dans le nom du fichier chiffré
  • <jour> : nombre de jours sur lesquels on veut garder les snapshots
  • <semaine> : nombre de semaines sur lesquelles on veut garder les snapshots (le plus récent de la semaine sera conservé)
  • <mois> : nombre de mois sur lesquels on veut conserver les snapshots (le plus récent du mois sera conservé)

L’accès sécurisé aux snapshots chiffrés s’effectue à l’aide de l’utilisateur snapshots. Les clés ssh des personnes autorisées à se connecter avec cet utilisateur doivent être situées dans le fichier /home/snasphots/.ssh/authorized_keys.

Le hook script peut être testé en déclenchant une sauvegarde manuelle à partir de l’interface de Proxmox.

Afin de garantir une exécution rapide du plan de reprise d’activité en cas de sinistre, il a été décidé de conserver les snapshots du jour à la fois en clair et chiffrés. Les snapshots présents en clair sont situés dans le dossier /SAVE/dump. Les snapshots chiffrés sont situés dans le dossier /SAVE/dump/public_snapshots. Tout snapshot effectué plus tard que le jour même est donc conservé uniquement sous forme chiffré. Les snapshots en clair sont des archives compressées au format .vma.gz ou .lzo. Les snapshots chiffrés sont des archives au format tar.

Un snapshot comporte plusieurs phases :

  • job-start
  • backup-start
  • pre-stop
  • pre-restart
  • post-restart
  • backup-end
  • log-end
  • job-end

Le nom de la phase est passé en argument par Proxmox lors de l’appel du script. Nous utilisons la phase “backend-end” : à ce moment là, le snapshot a complètement été écrit sur le disque, dans le dossier /SAVE/dump/ de l’hyperviseur. Chaque phase comporte différentes variables d’environnement que l’on peut récupérer dans le script Python. Le script se sert uniquement de la variable TARFILE qui comporte le nom du fichier du snapshot.

Un découpage de l’archive de la snapshot est effectué car il est impossible de faire tenir l’ensemble du fichier en mémoire RAM. Pour ce faire, un générateur Python est utilisé et permet d’obtenir des morceaux de tailles fixes du snapshot, dans notre cas 1 Go.
Le chiffrement des snapshots utilise le module python-gnupg.

Chaque morceau du snapshot est chiffré, puis écrit dans un dossier temporaire. À ce stade, le dossier temporaire est composé d’une multitude de fichiers ayant pour nom “part.X” où X représente l’indice du morceau (de 1 à n). Ce dossier est ensuite compressé en une archive au format tar. L’archive est alors prête à être synchronisée sur l’autre hyperviseur et externalisée.

La synchronisation sur l’autre hyperviseur utilise l’outil rsync via un appel à la commande bash associée. À chaque appel du hook script, l’ensemble du dossier /SAVE/dump est synchronisé sur l’autre hyperviseur, donc aussi bien les snapshots en clair que les snapshots chiffrés. La commande rsync est appelé grâce au module Python sh qui permet notamment de gérer les erreurs au sein d’exceptions Python. En cas d’une erreur renvoyée par rsync, celle-ci figurera dans la partie logging de l’interface de Proxmox.

La solution d’externalisation mise en place utilise un chroot du dossier /SAVE/dump/publicsnapshots. Un chroot permet de définir le dossier publicsnapshots comme la racine du système de fichiers accessible par l’utilisateur. Le répertoire public_snapshots est uniquement accessible avec les droits de lecture et d’exécution par l’utilisateur snapshots (chmod 775 avec root défini comme propriétaire).

L’utilisateur snapshots est créé avec la commande adduser. Un répertoire /home/snapshots est également créé et un mot de passe est défini par sécurité, mais il ne sera pas utilisé car la connexion ssh se fera uniquement par clé.

L’ensemble des clés autorisées à se connecter avec cet utilisateur est défini au sein du dossier /home/snapshots/.ssh dans un fichier authorized_keys.
L’utilisateur snapshots a ensuite été rajouté au sein du groupe chrootjail.

Pour pouvoir ensuite “chrooter” tous les utilisateurs de ce groupe au sein d’un dossier /SAVE/dump/publicsnapshots, et interdire la connexion par mot de passe, il faut rajouter la configuration suivante au fichier /etc/ssh/sshdconfig :

sshd_config
Match group chrootjail
	chrootDirectory /SAVE/dump/public_snapshots
        PasswordAuthentication no

Une fois ces lignes de configuration ajoutées, et après redémarrage du serveur ssh service ssh restart l’utilisateur sera donc directement “chrooté” au sein du dossier /SAVE/dump/public_snapshots lors d’une connexion ssh réussie. Il verra ce dossier comme la racine du système de fichiers.

À ce stade, néanmoins, l’utilisateur n’a pas accès à un shell. Il pourra se connecter en ssh, mais sera directement déconnecté. Il faut dans un premier temps lui donner accès à /bin/bash, ce qui lui donnera accès aux commandes intégrées à bash telles que cd ou echo. Il faut également lui donner accès aux commandes rsync ou ls qui sont nécessaires pour permettre l’externalisation. Pour automatiser la mise à disposition de nouvelles commandes bash, le script suivant est utilisé :

get_command.sh
#!/bin/bash
# This script can be used to create simple chroot environment
# Written by LinuxCareer.com <http://linuxcareer.com/>
# (c) 2013 LinuxCareer under GNU GPL v3.0+
 
#!/bin/bash
 
CHROOT='/SAVE/dump/public_snapshots'
mkdir $CHROOT
 
for i in $( ldd $* | grep -v dynamic | cut -d " " -f 3 | sed 's/://' | sort | uniq )
  do
    cp --parents $i $CHROOT
  done
 
# ARCH amd64
if [ -f /lib64/ld-linux-x86-64.so.2 ]; then
   cp --parents /lib64/ld-linux-x86-64.so.2 /$CHROOT
fi
 
# ARCH i386
if [ -f  /lib/ld-linux.so.2 ]; then
   cp --parents /lib/ld-linux.so.2 /$CHROOT
fi
 
echo "Chroot jail is ready. To access it execute: chroot $CHROOT"

Source : https://linuxconfig.org/how-to-automatically-chroot-jail-selected-ssh-user-logins

Le script se configure en spécifiant le dossier racine du chroot dans la variable CHROOT. L’ajout de nouvelles commandes du shell qui seront autorisées à l’utilisateur “chrooté” se fait comme suit :

./chroot.sh /bin/{ls,cat,echo,rm,bash} /usr/bin/vi 

Dans l’exemple ci-dessus, les commande ls, cat, echo, rm, bash et vi seront donc ajoutées aux commandes shell autorisées à l’utilisateur.

Le script effectue les actions suivantes :

  • Création du dossier qui comporte le chroot, si le dossier existe déjà il n’est pas créé.
  • Copie des bibliothèques partagées (obtenues avec la commande ldd) dans un dossier lib ou lib64 présents dans le dossier comportant le chroot
  • Copie de l’exécutable de la commande dans le dossier bin (ou un sous-dossier comme usr/bin selon la commande) du chroot

Après l’ajout de commandes, le dossier du chroot contient les dossiers suivants :

bin/
lib/
lib64/
usr/bin 

L’externalisation des snapshots est maintenant parfaitement fonctionnelle.

  • txs/infra/backups_p18/snapshots.txt
  • de 127.0.0.1