aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSébastien Dailly <sebastien@chimrod.com>2021-11-18 15:14:25 +0100
committerSébastien Dailly <sebastien@dailly.me>2022-02-01 18:38:01 +0100
commit4074d0a6adbdef0591578f477d9068a07ba5fd94 (patch)
tree0e971bd4ac93c09b5f3879c859f36732a6f13c77
parent3dfa4d20256ca7f8599f1999956b3721942b2184 (diff)
Sauvegardes avec duplicity
-rw-r--r--content/Informatique/2022-09-cold.rst372
-rw-r--r--content/images/ovh_cold/config.pngbin0 -> 13672 bytes
-rw-r--r--content/images/ovh_cold/degel.pngbin0 -> 14892 bytes
-rw-r--r--content/images/ovh_cold/user.pngbin0 -> 28968 bytes
-rw-r--r--content/resources/backup.sh100
-rwxr-xr-xcontent/resources/backup/gen_config.sh88
-rwxr-xr-xcontent/resources/backup/gen_config_local.sh58
7 files changed, 518 insertions, 100 deletions
diff --git a/content/Informatique/2022-09-cold.rst b/content/Informatique/2022-09-cold.rst
new file mode 100644
index 0000000..00c265c
--- /dev/null
+++ b/content/Informatique/2022-09-cold.rst
@@ -0,0 +1,372 @@
+.. -*- mode: rst -*-
+.. -*- coding: utf-8 -*-
+
+====================================
+Les sauvegarde avec ovh et duplicity
+====================================
+
+:date: 3020-09-02
+:tags: sauvegarde, gpg
+:status: draft
+
+Le problème avec les sauvegardes est de savoir où les mettre. La plupart du
+temps nous n’en avons pas besoin (et le plus longtemps possible souhaitons le…)
+et les supports sur lesquels nous les entreposons vieillissent. À noter qu’il
+est également recommandé de réaliser doubler les sauvegardes (principe 3-2-1),
+ce qui s’avère fastidieux si l’on fait cela nous même à la maison.
+
+Une solution est de mettre les sauvegarde dans le cloud, mais cela implique
+alors un problème de sécurité : tout ce qui est en ligne est publique. De plus,
+cela met dans la balance la question du cout des sauvegardes, toutes les
+solutions ne se valent pas, et le volume n’et pas négligeable…
+
+Une solution pour cela est d’utiliser le principe des sauvegardes à froid : les
+sauvegardes sont en quelque sorte *givrées* et ne sont pas disponibles de
+suite en cas de besoin (quelques heures sont nécessaires pour les récupérer).
+Par contre, l’avantage est le cout qui est beaucoup moins onéreux qu’un espace
+de stockage disponible en ligne. Voici par exemple les tarifs pour le stockage
+à froid chez OVH :
+
+=========================== ========================
+Action Prix
+=========================== ========================
+Stockage froid sécurisé 0,002 € HT/mois/Go
+Trafic entrant 0,01 € HT/Go
+Trafic sortant 0,01 € HT/Go
+=========================== ========================
+
+L'inconvénient étant une non-disponibilité immédiate des fichiers archivés.
+Cela en soit ne pose pas de problème pour sauvegarder ses photos, mais cela
+empêche de mettre en place une sauvegarde incrémentale, puisqu'il est
+nécessaire pour l'outil de sauvegarde de pouvoir comparer la liste des fichiers
+archivés afin de savoir lesquels mettre à jour.
+
+Il faut donc trouver une solution qui, en plus de chiffrer les données avant de
+les envoyer en ligne, conserve un moyen de mettre à jour les sauvegardes sans
+avoir besoin de les dégeler avant.
+
+.. contents::
+ :depth: 1
+
+Présentation de la solution
+===========================
+
+Utiliser le stockage à froid pour y mettre les archives, et le stockage à chaud
+pour toutes les données d'index. De cette manière l'application est capable de
+récupérer les métadonnées afin d'identifier les données à mettre à jour en
+temps réel, et la récupération des sauvegardes, elle, se fera uniquement sur
+les données ayant été dégelées.
+
+Voir le guide d'OVH disponible à l'adresse suivante : https://docs.ovh.com/gb/en/storage/pca/duplicity/
+
+Création des accès
+==================
+
+Création
+--------
+
+Commencer par se créer un compte sur l'environnement d'OVH
+
+.. image:: {static}/images/ovh_cold/user.png
+ :class: floatright
+ :alt: Création d’un nouvel utilisateur
+
+Lors de la création de l'utilisateur, s'assurer que le role `ObjectStore
+Operator` est bien activé. Il n'est pas nécessaire que notre utilisateur ait
+tous les roles (nous voulons juste faire une sauvegarde).
+
+Identifiant
+-----------
+
+.. image:: {static}/images/ovh_cold/config.png
+ :class: floatleft
+ :alt: Récupération de l’identifiant
+
+L'ensemble des informations du compte peuvent ensuite être récupérées dans un
+fichier texte (sauf le mot de passe qui est à noter), ce fichier va nous servir
+à générer la configuration de duplicity
+
+Configuration
+=============
+
+Pour générer la configuration, le script `gen_config.sh` va produire le fichier
+json avec les valeurs nécessaires (URL, mot de passe, identifiant etc)
+
+.. admonition:: Sécurité
+ :class: danger
+
+ Attention ! Le fichier json contient le mot de passe en clair, attention à ne
+ pas conserver le fichier, ou restreindre les droits afin d’empêcher que
+ celui-ci ne soit exposé.
+
+.. figure:: {static}/images/mimetypes/application-x-executable.png
+ :alt: get the file
+ :align: center
+ :target: {static}/resources/backup/gen_config.sh
+
+ Télécharger
+
+Il faut lui donner en paramètre le fichier téléchargé, et la configuration à
+lancer :
+
+.. code-block:: console
+
+ $ ./gen_config.sh ./user_duplicity.sh config.json backup_name
+ Please enter your OpenStack Password: XXXXX
+ $
+
+Notre fichier json va devenir la destination de nos sauvegardes. Puisqu’il
+contient toutes les directives nécessaires, il suffit d’indiquer à duplicity de
+l’utiliser comme emplacement de sauvegarde et l’application sera capable
+d’orienter les fichiers vers le stockage à froid ou non automatiquement.
+
+Sauvegarder et consulter les sauvegardes
+========================================
+
+Sauvegarde
+----------
+
+Une fois le script généré, on peut lancer une syncronisation complète avec la
+commande suivante :
+
+.. code-block:: bash
+
+ duplicity \
+ --encrypt-key ${enc_key} \
+ --sign-key ${sign_key} \
+ --file-prefix-manifest 'hot_' \
+ --file-prefix-signature 'hot_' \
+ --file-prefix-archive 'cold_' \
+ full ${source} \
+ "multi:$(realpath config.json)?mode=mirror&onfail=abort"
+
+Les mises à jour (delta) uniquement sont lancé en remplaçant :literal:`full` par
+:literal:`incremental` :
+
+.. code-block:: bash
+
+ duplicity \
+ --encrypt-key ${enc_key} \
+ --sign-key ${sign_key} \
+ --file-prefix-manifest 'hot_' \
+ --file-prefix-signature 'hot_' \
+ --file-prefix-archive 'cold_' \
+ incremental ${source}" \
+ multi:$(realpath config.json)?mode=mirror&onfail=abort"
+
+Lister les sauvegardes
+----------------------
+
+Une fois la sauvegarde fait, nous avons la possibilité de consulter les
+sauvegarde réalisées en accédant juste aux métadonnées avec la commande
+:literal:`collection-status` :
+
+.. code-block:: console
+
+ $ duplicity \
+ --file-prefix-manifest 'hot_' \
+ --file-prefix-signature 'hot_' \
+ --file-prefix-archive 'cold_' \
+ collection-status \
+ "multi://$(realpath config.json)?mode=mirror&onfail=continue"
+ MultiBackend: pca://duplicity: 7 files
+ MultiBackend: swift://duplicity_hot: 14 files
+ Last full backup date: Wed Sep 2 09:36:13 2020
+ Collection Status
+ -----------------
+ Connecting with backend: BackendWrapper
+ Archive dir: …
+
+ Found 2 secondary backup chains.
+ …
+
+ Found primary backup chain with matching signature chain:
+ -------------------------
+ Chain start time: Wed Sep 2 09:36:13 2020
+ Chain end time: Wed Sep 2 12:01:17 2020
+ Number of contained backup sets: 5
+ Total number of contained volumes: 0
+ Type of backup set: Time: Num volumes:
+ Incremental Wed Sep 2 09:38:09 2020 0
+ Incremental Wed Sep 2 11:32:19 2020 0
+ Incremental Wed Sep 2 12:00:56 2020 0
+ Incremental Wed Sep 2 12:01:17 2020 0
+ -------------------------
+ No orphaned or incomplete backup sets found.
+
+Contrôles et restauration
+=========================
+
+Pour les opérations de contrôles et de restauration, je propose de commencer
+par récupérer les fichiers gelés et les conserver en local. De cette manière
+nous pourrons :
+
+1. Contrôler l’intégriter de la sauvegarde
+2. Procéder à une restauration de test de celle-ci.
+
+Dégeler les fichiers en masse
+-----------------------------
+
+L’interface d’OVH permet de dégeler un fichier, mais celle-ci n’est pas
+pratique pour procéder à cette opération en masse. Pour ce faire, nous allons
+nous connecter en SFTP directement, pour lancer l’opération de récupération.
+
+.. image:: {static}/images/ovh_cold/degel.png
+ :class: floatright
+ :alt: Attente du dégel des données
+
+Il faut se connecter au serveur suivant :
+
+.. code-block:: bash
+
+ . user_duplicity.sh
+ sftp pca@gateways.storage.${OS_REGION_NAME}.cloud.ovh.net
+
+Le mot de passe de l'utilisateur est composé ainsi :
+
+.. code-block:: bash
+
+ ${OS_TENANT_NAME}.${OS_USERNAME}.${OS_PASSWORD}
+
+.. admonition:: Compte d’accès
+ :class: note
+
+ L’utilisateur est toujours :literal:`pca`, c’est à travers le mot de passe
+ qu’OVH identifie les ressources auxquelles nous sommes censer accéder.
+
+Une fois connecté en sftp, il est possible de lancer le dégel de l'ensemble des
+fichiers dans un répertoire donné sans passer par l'interface. Cela peut être
+utile pour préparer une restauration (duplicity stocke ses fichiers en lots de
+200Mo)
+
+.. code-block:: console
+
+
+ > cd backup_name
+ > get *
+
+La commande va échouer (impossible de récupérer les fichiers car ceux-ci sont
+gelés), mais la demande sera transmise, et les fichiers vont passer en
+préparation pour restauration.
+
+Création d’une configuration locale
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+De la même manière que nous avions préparé un script pour générer un fichier de
+configuration pour duplicity afin de sauver nos données, nous allons générer un
+fichier qui va créer une configuration *locale* dans laquelle l’ensemble des
+données seront chargées à partir du répertoire dans lequel nous aurons sauvé
+nos fichiers.
+
+De cette manière, nous nous affranchissons des contraintes de temps (et du
+réseau) pour toutes les opérations qui ont besoin de charger chaque archive.
+
+.. figure:: {static}/images/mimetypes/application-x-executable.png
+ :alt: get the file
+ :align: center
+ :target: {static}/resources/backup/gen_config_local.sh
+
+ Télécharger
+
+Afficher les fichiers sauvegardés
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Pour lancer cette commande, il est nécessaire que les fichiers soient dégelés,
+ou travailler sur les fichiers locaux.
+
+.. code-block:: console
+
+ $ duplicity \
+ --file-prefix-manifest 'hot_' \
+ --file-prefix-signature 'hot_' \
+ --file-prefix-archive 'cold_' \
+ list-current-files \
+ "multi:$(realpath config.json)?mode=mirror"
+ MultiBackend: pca://duplicity: 7 files
+ MultiBackend: swift://duplicity_hot: 14 files
+ Synchronizing remote metadata to local cache...
+ GnuPG passphrase for decryption:
+ MultiBackend: pca://duplicity: 7 files
+ MultiBackend: swift://duplicity_hot: 14 files
+ Last full backup date: Wed Sep 2 09:36:13 2020
+ Wed Sep 2 09:28:55 2020 .
+ Sat Jan 27 17:44:34 2018 fichier1
+ Sat Jan 27 17:44:34 2018 fichier2
+
+.. admonition:: Mot de passe GPG
+ :class: note
+
+ Lors de la restauration, duplicity demande le mot de passe GPG. Nous pouvons
+ valider dans rien saisir à ce moment là car nous utilisons la clef GPG pour
+ déchiffrer le contenu des archives.
+
+Il est possible d'ajouter une version spécifique en précisant l'heure de la
+sauvegarde souhaitée :
+
+.. code-block:: console
+
+ $ duplicity \
+ --file-prefix-manifest 'hot_' \
+ --file-prefix-signature 'hot_' \
+ --file-prefix-archive 'cold_' \
+ list-current-files --time 20200902T072859Z \
+ "multi:$(realpath config.json)?mode=mirror"
+
+Restauration
+------------
+
+Sans paramètre donnés, c’est la restauration de la dernière sauvegarde qui
+est exécutée :
+
+.. code-block:: console
+
+ $ duplicity \
+ --encrypt-key ${enc_key} \
+ --sign-key ${sign_key} \
+ --file-prefix-manifest 'hot_' \
+ --file-prefix-signature 'hot_' \
+ --file-prefix-archive 'cold_' \
+ "multi:$(realpath config.json)?mode=mirror&onfail=continue" \
+ $(path/to/restore)
+
+.. admonition:: Restauration d’une version donnée
+ :class: note
+
+ Là encore, si nous ajouter la date de la sauvegarde, nous allons récupérer
+ une version donnée :
+
+ .. code-block:: console
+
+ $ duplicity \
+ --encrypt-key ${enc_key} \
+ --sign-key ${sign_key} \
+ --file-prefix-manifest 'hot_' \
+ --file-prefix-signature 'hot_' \
+ --file-prefix-archive 'cold_' \
+ --time 20200902T072859Z \
+ "multi:$(realpath config.json)?mode=mirror&onfail=continue" \
+ $(path/to/restore)
+
+
+Utiliser duply
+==============
+
+Duply est une application de sauvegarde basée sur duplicity. Elle permet de
+faciliter les commandes et la configuration en fournissant une interface plus
+simple — basée sur des fichiers de configuration plutôt que des lignes de
+commande à rallonge. Les commandes qui sont présentes ci-dessus restent
+valide !
+
+Duply permet par contre de sauver sa configuration dans un fichier unique, qui
+sera complété à l'aide de variables d'environnement dans bash. Cela permet donc
+de le configurer de manière assez simple.
+
+Restauration
+------------
+
+Pour restaurer, il suffit de récupérer les fichiers *gelés* les fichiers
+d'index peuvent rester sur le cloud.
+
+Le script de génération des fichiers de configuration a un cas particulier lors
+de la commande `RESTORE`, il va en effet génerer un fichier allant chercher les
+fichier en local plutôt que sur le serveur d'OVH.
diff --git a/content/images/ovh_cold/config.png b/content/images/ovh_cold/config.png
new file mode 100644
index 0000000..27e7288
--- /dev/null
+++ b/content/images/ovh_cold/config.png
Binary files differ
diff --git a/content/images/ovh_cold/degel.png b/content/images/ovh_cold/degel.png
new file mode 100644
index 0000000..73a9fca
--- /dev/null
+++ b/content/images/ovh_cold/degel.png
Binary files differ
diff --git a/content/images/ovh_cold/user.png b/content/images/ovh_cold/user.png
new file mode 100644
index 0000000..c52a7d4
--- /dev/null
+++ b/content/images/ovh_cold/user.png
Binary files differ
diff --git a/content/resources/backup.sh b/content/resources/backup.sh
deleted file mode 100644
index aaf16f4..0000000
--- a/content/resources/backup.sh
+++ /dev/null
@@ -1,100 +0,0 @@
-#!/bin/sh
-###########################################################
-# This script uses rsync to backup directories on a media
-# (ex: USB disk) with a copy and incremental method.
-###########################################################
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-############################################################
-#
-# Written by Spip
-# Adapted by Chimrod
-# added script path detection
-# delete with find
-# Version 1.0
-#
-############################################################
-#to be nice...
-ionice -c3 -p$$
-renice +15 -p $$
-THEDATE=`date +%F_%Hh%M`
-#FILE=`dirname $0`
-FILE=`dirname "$(cd "${0%/*}" 2>/dev/null; echo "$PWD"/"${0##*/}")"`
-LOGFILE=$FILE'/log/sauvegarde'
-
-SPLIT='====================================================================='
-#check logfile directory
-
-if ! [ -e $LOGFILE ]
-then
- /bin/mkdir -p $LOGFILE
-fi
-
-#check if the media is mounted
-
-if ! [ -e $FILE ]
-then
- echo "$THEDATE : péripherique non connecté. Pas de sauvegarde possible" >> "$LOGFILE/save.log"
- echo $SPLIT >> "$LOGFILE/save.log"
- exit 0
-fi
-
-#prevent a second task
-LOCKFILE="/var/lock/sauvegarde.lock"
-[ -f $LOCKFILE ] && exit 0
-
-#if the script are stoped, remove lockfile
-trap "rm -f $LOCKFILE" EXIT
-touch $LOCKFILE
-echo "$THEDATE : Début de sauvegarde" >> "$LOGFILE/save.log"
-# $1:time to keep files; $2: name of the backup; $3: target
-
-function save() {
-
- echo "$SPLIT" >> "$LOGFILE/$2.log"
- echo "$THEDATE" >> "$LOGFILE/$2.log"
-
- #Incremental & copie
- INC="$FILE/$2/INC/$THEDATE"
- BAK="$FILE/$2/BAK"
- TOSAVE="$3"
- mkdir -p $INC
-
- if ! [ -e "$BAK" ]
- then
- /bin/mkdir -p "$BAK"
- fi
-
- #a: archivage :recurcif, preserve dates, persmissions, groupes...
- #v: verbose mode
- #delete : supprime les fichiers n'etant plus chez l'émeteur >> copie conforme.
- /usr/bin/rsync -a --stats --delete --backup --backup-dir="$INC" "$TOSAVE" "$BAK" >> "$LOGFILE/$2.log"
-
- cd $FILE/$2/INC
- tar -czvf "$INC.tar.gz" "$THEDATE"
- echo tar -czvf "$INC.tar.gz" "$FILE/$2/INC/$THEDATE"
- rm -rf "$THEDATE"
- #Remove the file older than $1 days
- find "$FILE/$2/INC" -mtime +$1 -delete >> "$LOGFILE/save.log"
-
-}
-
-#List here all repositories to backup
-
-save 90 etc /etc/
-
-#to log the end of the backup
-THEDATE=`date +%F_%Hh%M`
-echo "$THEDATE : Sauvegarde effectuée" >> "$LOGFILE/save.log"
-exit 0
diff --git a/content/resources/backup/gen_config.sh b/content/resources/backup/gen_config.sh
new file mode 100755
index 0000000..8091bed
--- /dev/null
+++ b/content/resources/backup/gen_config.sh
@@ -0,0 +1,88 @@
+#!/bin/bash
+
+. ./$1
+OUTFILE=$2
+BACKUP_NAME=$3
+
+if test "x${PCA_OS_REGION_NAME}" = x; then
+ PCA_OS_REGION_NAME=${OS_REGION_NAME}
+fi
+
+if test "x${HOT_OS_REGION_NAME}" = x; then
+ HOT_OS_REGION_NAME=${OS_REGION_NAME}
+fi
+
+envsubst > "${OUTFILE}" << EOF
+[
+ {
+ "description": "Cold storage",
+ "url": "pca://${BACKUP_NAME}",
+ "env": [
+ {
+ "name": "PCA_AUTHURL",
+ "value": "${OS_AUTH_URL}"
+ },
+ {
+ "name": "PCA_AUTHVERSION",
+ "value": "${OS_IDENTITY_API_VERSION}"
+ },
+ {
+ "name": "PCA_PROJECT_DOMAIN_NAME",
+ "value": "Default"
+ },
+ {
+ "name": "PCA_TENANTID",
+ "value": "${OS_TENANT_ID}"
+ },
+ {
+ "name": "PCA_USERNAME",
+ "value": "${OS_USERNAME}"
+ },
+ {
+ "name": "PCA_PASSWORD",
+ "value": "${OS_PASSWORD}"
+ },
+ {
+ "name": "PCA_REGIONNAME",
+ "value": "${PCA_OS_REGION_NAME}"
+ }
+ ],
+ "prefixes": ["cold_"]
+ },
+ {
+ "description": "Hot storage",
+ "url": "swift://${BACKUP_NAME}_indexes",
+ "env": [
+ {
+ "name": "SWIFT_AUTHURL",
+ "value": "${OS_AUTH_URL}"
+ },
+ {
+ "name": "SWIFT_AUTHVERSION",
+ "value": "${OS_IDENTITY_API_VERSION}"
+ },
+ {
+ "name": "SWIFT_PROJECT_DOMAIN_NAME",
+ "value": "${OS_PROJECT_DOMAIN_NAME}"
+ },
+ {
+ "name": "SWIFT_TENANTID",
+ "value": "${OS_TENANT_ID}"
+ },
+ {
+ "name": "SWIFT_USERNAME",
+ "value": "${OS_USERNAME}"
+ },
+ {
+ "name": "SWIFT_PASSWORD",
+ "value": "${OS_PASSWORD}"
+ },
+ {
+ "name": "SWIFT_REGIONNAME",
+ "value": "${HOT_OS_REGION_NAME}"
+ }
+ ],
+ "prefixes": ["hot_"]
+ }
+]
+EOF
diff --git a/content/resources/backup/gen_config_local.sh b/content/resources/backup/gen_config_local.sh
new file mode 100755
index 0000000..4e6c0dc
--- /dev/null
+++ b/content/resources/backup/gen_config_local.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+
+. ./$1
+OUTFILE=$2
+BACKUP_NAME=$3
+
+if test "x${PCA_OS_REGION_NAME}" = x; then
+ PCA_OS_REGION_NAME=${OS_REGION_NAME}
+fi
+
+if test "x${HOT_OS_REGION_NAME}" = x; then
+ HOT_OS_REGION_NAME=${OS_REGION_NAME}
+fi
+
+envsubst > "${OUTFILE}" << EOF
+[
+ {
+ "description": "Cold storage",
+ "url": "file://${BACKUP_NAME}",
+ "prefixes": ["cold_"]
+ },
+ {
+ "description": "Hot storage",
+ "url": "swift://${BACKUP_NAME}_indexes",
+ "env": [
+ {
+ "name": "SWIFT_AUTHURL",
+ "value": "${OS_AUTH_URL}"
+ },
+ {
+ "name": "SWIFT_AUTHVERSION",
+ "value": "${OS_IDENTITY_API_VERSION}"
+ },
+ {
+ "name": "SWIFT_PROJECT_DOMAIN_NAME",
+ "value": "${OS_PROJECT_DOMAIN_NAME}"
+ },
+ {
+ "name": "SWIFT_TENANTID",
+ "value": "${OS_TENANT_ID}"
+ },
+ {
+ "name": "SWIFT_USERNAME",
+ "value": "${OS_USERNAME}"
+ },
+ {
+ "name": "SWIFT_PASSWORD",
+ "value": "${OS_PASSWORD}"
+ },
+ {
+ "name": "SWIFT_REGIONNAME",
+ "value": "${HOT_OS_REGION_NAME}"
+ }
+ ],
+ "prefixes": ["hot_"]
+ }
+]
+EOF