ajoute les sauvegardes chiffrées vers Dedibackup (rôle Ansible, timer quotidien, rotation)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Adam Belkacemi 2026-10-07 11:08:22 +02:00
parent f5a5e8d8a9
commit e879a214a9
9 changed files with 328 additions and 0 deletions

View file

@ -66,3 +66,42 @@ Durcir un serveur, c'est fondamentalement risqué pour l'accès qu'on a déjà :
**Suite**
Étape 3 : sauvegardes vers Dedibackup (archives `tar.zst` chiffrées `age`, rotation 7 quotidiennes + 4 hebdomadaires, timer systemd).
## 2026-10-07 — Étape 3 : sauvegardes vers Dedibackup
**Fait**
- Rôle Ansible `sauvegardes` (`ansible/roles/sauvegardes/`) + playbook `ansible/sauvegardes.yml`. Pas de risque réseau/pare-feu/SSH ici, donc appliqué directement sans attendre d'accord explicite (règle 3).
- Paire de clés **age** générée sur le serveur (`/root/.age/cle-sauvegardes.txt`, `600`) : la clé privée ne quitte jamais le serveur.
- Script `/usr/local/sbin/sauvegarde-dedibackup.sh` : archive `/etc`, `/home`, `/root` en `tar.zst`, chiffre en flux avec `age`, envoie en FTP vers Dedibackup avec `lftp`, duplique en hebdomadaire le dimanche, applique la rotation (7 quotidiennes + 4 hebdomadaires, suppression du surplus sur Dedibackup).
- Timer systemd `sauvegarde-dedibackup.timer` : tous les jours à 03:15 (±5 min).
- `docs/runbooks/sauvegardes.md` écrit (déclenchement manuel, liste, restauration, rappel sur la clé age).
**Commandes clés**
```bash
cd ansible && script -q /dev/null ansible-playbook sauvegardes.yml -i inventory.ini
ssh paillasse "sudo systemctl start sauvegarde-dedibackup.service"
# restauration testée : voir docs/runbooks/sauvegardes.md
```
**Problèmes rencontrés et solution**
- 1er envoi FTP : `530 Bad credentials`. Diagnostic sans jamais afficher les identifiants (longueur et forme des variables seulement). Cause probable : valeurs non protégées par des guillemets dans `/root/.dedibackup`, risque de troncature silencieuse par `bash` si le mot de passe contient `#`, `$` ou une espace. **Adam a corrigé le fichier lui-même** (jamais Claude, le fichier contient un secret). Après correction, l'envoi a réussi.
- Extraction de test avec un chemin `./etc/...` : échec (`Not found in archive`), car `tar` retire le `/` initial à la création (`etc/...`, sans `./`). Corrigé dans le runbook.
- `etc/os-release` n'a pas pu être lu après extraction : c'est un lien symbolique vers `../usr/lib/os-release`, hors du périmètre sauvegardé (`/etc`, `/home`, `/root` seulement) — comportement normal, pas un bug.
**Critère vérifié**
- Sauvegarde envoyée : `quotidienne-20261007.tar.zst.age`, confirmée présente sur Dedibackup (`lftp cls`).
- Fichier restauré avec succès dans `/tmp` : `etc/hostname` extrait et relu, contenu conforme (`sd-124443`). Dossier de test nettoyé après coup.
**Comprendre**
Chiffrer avant d'envoyer (plutôt que de compter sur un transport chiffré) a un avantage : Dedibackup n'a accès qu'à un FTP en clair, mais comme le contenu est déjà chiffré par `age` avant de partir, intercepter le transfert ne révèle rien d'utile. La contrepartie, c'est qu'il ne faut jamais perdre la clé privée `age` : sans elle, toutes les sauvegardes passées deviennent illisibles pour toujours, même si elles sont bien là sur Dedibackup. D'où le rappel à Adam de garder une copie de cette clé ailleurs que sur le serveur.
**⚠️ Reste à faire par Adam** : copier `/root/.age/cle-sauvegardes.txt` en dehors du serveur (gestionnaire de mots de passe, clé USB chiffrée…) — voir `docs/runbooks/sauvegardes.md`.
**Suite**
Étape 4 : réseau pour OpenStack (`dummy0`, paire veth, NAT). Touche le réseau → accord explicite d'Adam obligatoire avant d'appliquer, filet anti-coupure, et redémarrage du serveur seulement avec son accord.

View file

@ -0,0 +1,101 @@
# Runbook — Sauvegardes vers Dedibackup
## Principe
Chaque jour à 03:15 (±5 min aléatoires), le timer systemd `sauvegarde-dedibackup.timer`
déclenche `sauvegarde-dedibackup.service`, qui exécute
`/usr/local/sbin/sauvegarde-dedibackup.sh` (déployé par le rôle Ansible
`sauvegardes`, voir `ansible/roles/sauvegardes/`).
Le script :
1. Archive `/etc`, `/home`, `/root` en `tar.zst` (streaming, sans fichier intermédiaire en clair).
2. Chiffre le flux avec **age**, avec la clé publique du serveur (`/root/.age/cle-sauvegardes.txt`).
3. Envoie l'archive chiffrée en FTP vers Dedibackup (`dedibackup-dc3.online.net`, joignable uniquement depuis ce serveur).
4. Le dimanche (jour 7), duplique aussi l'archive du jour comme hebdomadaire.
5. Applique la rotation : garde les 7 quotidiennes et 4 hebdomadaires les plus récentes, supprime le reste sur Dedibackup (qui est limité à 1000 fichiers).
Le FTP de Dedibackup n'est pas chiffré en transport, mais le contenu de
l'archive l'est déjà (age) avant l'envoi : une interception du transfert ne
révèle rien.
## Où est la clé de déchiffrement
- Clé privée age : `/root/.age/cle-sauvegardes.txt`, permissions `600`, **ne quitte jamais le serveur**.
- Clé publique (sans risque à diffuser) : voir la sortie de `age-keygen` lors du déploiement, ou :
```
sudo grep 'public key' /root/.age/cle-sauvegardes.txt
```
**⚠️ Rappel pour Adam (pas fait automatiquement par Claude) : garder une copie de
`/root/.age/cle-sauvegardes.txt` en dehors du serveur** (gestionnaire de mots de
passe, clé USB chiffrée, etc.). Si le serveur est perdu, cette clé est la seule
façon de déchiffrer les sauvegardes existantes. Exemple pour la récupérer (à
lancer depuis le Mac, le fichier arrive en clair sur le Mac donc à stocker
immédiatement dans un endroit sûr puis à effacer du téléchargement) :
```
scp paillasse:/root/.age/cle-sauvegardes.txt ~/Desktop/
# puis la mettre immédiatement dans un gestionnaire de mots de passe,
# et supprimer la copie en clair sur le Desktop.
```
## Identifiants Dedibackup
Dans `/root/.dedibackup` (permissions `600`, jamais dans Git) :
`DEDIBACKUP_HOST`, `DEDIBACKUP_USER`, `DEDIBACKUP_PASS` — **valeurs entre
guillemets simples** si elles contiennent des caractères spéciaux (`#`, `$`,
espace…), sinon `bash` les tronque silencieusement en les lisant.
## Lancer une sauvegarde manuellement
```
ssh paillasse
sudo systemctl start sauvegarde-dedibackup.service
sudo journalctl -u sauvegarde-dedibackup.service --no-pager | tail -20
```
## Lister les sauvegardes présentes sur Dedibackup
```
sudo bash -c '
source /root/.dedibackup
lftp -u "$DEDIBACKUP_USER,$DEDIBACKUP_PASS" "$DEDIBACKUP_HOST" \
-e "set ftp:ssl-allow no; cls -1 *.tar.zst.age; quit"
'
```
## Restaurer un fichier (procédure testée le 2026-10-07)
```
ssh paillasse
sudo bash -c '
set -e
source /root/.dedibackup
RESTORE_DIR=/tmp/restauration-$(date +%Y%m%d-%H%M)
mkdir -p "$RESTORE_DIR"
# 1. Télécharger l'archive voulue depuis Dedibackup
lftp -u "$DEDIBACKUP_USER,$DEDIBACKUP_PASS" "$DEDIBACKUP_HOST" \
-e "set ftp:ssl-allow no; get quotidienne-AAAAMMJJ.tar.zst.age -o $RESTORE_DIR/archive.tar.zst.age; quit"
# 2. Déchiffrer puis extraire (tout le contenu, ou un chemin précis en argument de tar)
age -d -i /root/.age/cle-sauvegardes.txt "$RESTORE_DIR/archive.tar.zst.age" \
| tar -I zstd -xf - -C "$RESTORE_DIR" etc/un/fichier/precis
ls -la "$RESTORE_DIR"
'
```
Pour tout restaurer (sans préciser de chemin après `-xf -`), **ne jamais le
faire directement vers `/etc`, `/home` ou `/root`** sans réfléchir : ça
écraserait l'état actuel. Toujours restaurer vers un répertoire temporaire
(`/tmp/...`) puis copier à la main ce qui est nécessaire.
Les chemins dans l'archive n'ont **pas** de `/` ni de `./` au début (ex.
`etc/hostname`, pas `/etc/hostname` ni `./etc/hostname`) — `tar` les a retirés
à la création.
## Vérifié le 2026-10-07
- Sauvegarde envoyée : `quotidienne-20261007.tar.zst.age`, confirmée présente sur Dedibackup via `cls`.
- Restauration testée : `etc/hostname` extrait dans `/tmp/test-restauration-20261007/`, contenu conforme (`sd-124443`).