Set-OPS-Public/playbooks/maintenance/depot-hors-site.yml
Daniel Allaire 00cee67f02
Some checks are pending
verifier / verifier (push) Waiting to run
depot hors site : trois pieges payes en une manoeuvre
1. ansible -e cle=valeur DECOUPE SUR LES BLANCS — c est ainsi qu on passe
   plusieurs variables. Un chemin avec une espace y perd tout ce qui suit le
   premier blanc, et l erreur parle d un repertoire introuvable au nom tronque.
   Passage en JSON. Le depot avait deja appris ca pour le clonage de VM ; la
   lecon ne s etait pas propagee. Quatrieme fois.

2. /srv/restic est en 0711 — traversable, NON listable, pour qu un locataire
   n apprenne pas qui d autre depose ici. La consequence tombe sur rsync :
   opendir Permission denied, un message qui accuse un droit sans dire lequel.
   --rsync-path=sudo rsync fait lire la SOURCE en root.

3. rsync_opts ne protege pas ses elements : le shell coupait
   --rsync-path=sudo rsync en deux. Guillemets internes.

Resultat mesure : COPIE FIDELE, 233 fichiers, 95,7 Mo, empreintes identiques
une a une — comparees entre la source et la copie, pas supposees.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-09-05 16:33:26 -04:00

154 lines
6.5 KiB
YAML

---
# SORTIR LES SAUVEGARDES DU SITE, DU SITE.
#
# Le depot du site heberge trois choses irremplacables : la racine de son autorite de
# certification, la forge du genome, et l'etat de CHAQUE locataire. Tout cela vit sur une
# seule machine, dans un seul batiment. C'est le probleme du filet range dans la flotte
# qu'il protege — celui qu'on a mis deux jours a fermer pour Chezlepro — un etage plus haut.
#
# POURQUOI ON EMPORTE AUSSI LES DEPOTS DES LOCATAIRES. Si le site brule, les locataires
# perdent leurs sauvegardes avec lui, et EUX NE PEUVENT RIEN Y FAIRE : ils ont depose chez
# l'hebergeur, c'est a l'hebergeur de tenir cette promesse. 109 Mo au total — la question
# du volume ne se pose pas.
#
# CE QUE LE PORTEUR DE LA COPIE PEUT EN FAIRE : rien. Les depots sont chiffres par restic
# COTE CLIENT, avec des mots de passe qui vivent dans les voutes de chacun. Cette copie
# est un bloc opaque — c'est ce qui permet de la deposer chez un pair sans lui demander
# autre chose que de la disponibilite.
#
# ansible-playbook -i scripts/site_inventaire.py \
# playbooks/maintenance/depot-hors-site.yml -e vers=/media/…/setops-coffre
- name: Copier le depot du site hors du site
hosts: serveur_backup_site
gather_facts: false
become: true
vars:
racine: "{{ serveur_backup_site_parent | default('/srv/restic') }}"
pre_tasks:
- name: Exiger une destination
ansible.builtin.assert:
that:
- vers is defined
- vers | length > 0
fail_msg: >-
`-e vers=<repertoire>` requis. Viser un support amovible ou une machine tierce.
delegate_to: localhost
become: false
# UNE COPIE DANS L'INFRASTRUCTURE NE PROTEGE DE RIEN — elle boucle. Meme raisonnement
# que pour les cles : un coffre range dans ce qu'il protege n'est pas un coffre.
- name: Refuser une destination interne
ansible.builtin.assert:
that:
- not (vers is search('/srv/restic'))
- not (vers is search('Espace Chezlepro'))
fail_msg: >-
{{ vers }} est DANS l'infrastructure. Une copie qui partage le sort de
l'original ne protege de rien.
delegate_to: localhost
become: false
- name: La destination existe-t-elle ?
ansible.builtin.stat:
path: "{{ vers }}"
delegate_to: localhost
become: false
register: dest_etat
- name: Exiger une destination reelle
ansible.builtin.assert:
that:
- dest_etat.stat.exists
- dest_etat.stat.isdir
fail_msg: "{{ vers }} n'existe pas ou n'est pas un repertoire. Support monte ?"
delegate_to: localhost
become: false
tasks:
# L'EMPREINTE EST PRISE A LA SOURCE, AVANT LA COPIE. La comparer apres coup a une
# empreinte calculee sur la copie est la seule facon de savoir qu'on a emporte ce
# qu'on croit avoir emporte. Sans ca, on rentre chez soi avec un repertoire.
- name: Empreinte de ce qui est a la source
ansible.builtin.shell:
executable: /bin/bash
cmd: >-
set -o pipefail;
find {{ racine }} -type f -print0 | sort -z |
xargs -0 sha256sum | sed 's| {{ racine }}/| |'
register: empreinte_source
changed_when: false
- name: Combien, et quelle taille
ansible.builtin.shell:
executable: /bin/bash
cmd: "set -o pipefail; du -sb {{ racine }} | cut -f1"
register: taille_source
changed_when: false
# RSYNC LIT LA SOURCE EN ROOT, ET C'EST LA GARDE QUI L'EXIGE.
#
# `/srv/restic` est en 0711 : traversable, NON LISTABLE. C'est voulu — un locataire
# atteint son propre depot sans pouvoir apprendre qui d'autre depose ici. La liste
# des voisins est deja une information.
#
# La consequence tombe ici : le compte `ansible` ne peut pas ENUMERER la racine, et
# rsync echoue sur `opendir "/srv/restic/." failed: Permission denied`. Le message
# accuse un droit sans dire lequel, et on cherche du cote du compte alors que c'est
# la politique du repertoire.
#
# `--rsync-path=sudo rsync` fait lire la SOURCE en root, sans que la copie locale
# ait besoin de privileges. `become: false` reste donc juste pour le cote poste.
- name: Copier vers la destination
ansible.posix.synchronize:
src: "{{ racine }}/"
dest: "{{ vers }}/depot-du-site/"
mode: pull
archive: true
delete: true
rsync_opts:
# Les guillemets INTERNES sont necessaires : `rsync_opts` ne protege pas ses
# elements, et le shell local coupait `--rsync-path=sudo rsync` en deux.
- "--rsync-path='sudo rsync'"
become: false
# LE CHEMIN EST ENTRE GUILLEMETS, ET CE N'EST PAS DU ZELE. Ce depot vit sous
# « Espace Chezlepro » et les supports de l'exploitant portent des noms avec des
# blancs. Un chemin nu atteignant un shell se coupe au premier espace, et l'erreur
# parle d'un fichier introuvable au nom tronque — ce qui n'oriente vers rien.
# Quatrieme fois que ce depot paie ce piege.
- name: Empreinte de ce qui est arrive
ansible.builtin.shell:
executable: /bin/bash
cmd: >-
set -o pipefail;
find "{{ vers }}/depot-du-site" -type f -print0 | sort -z |
xargs -0 sha256sum | sed 's| {{ vers }}/depot-du-site/| |'
delegate_to: localhost
become: false
register: empreinte_copie
changed_when: false
- name: Exiger une copie fidele
ansible.builtin.assert:
that:
- empreinte_source.stdout == empreinte_copie.stdout
fail_msg: >-
LA COPIE DIFFERE DE LA SOURCE.
Source : {{ empreinte_source.stdout_lines | length }} fichier(s).
Copie : {{ empreinte_copie.stdout_lines | length }} fichier(s).
Ne pas s'en servir : une copie qu'on croit fidele et qui ne l'est pas est pire
qu'aucune copie.
success_msg: >-
COPIE FIDELE : {{ empreinte_source.stdout_lines | length }} fichier(s),
{{ (taille_source.stdout | int / 1048576) | round(1) }} Mo, empreintes
identiques une a une.
- name: Ce qu'il reste a faire, et qui n'est pas facultatif
ansible.builtin.debug:
msg:
- "La copie est OPAQUE : chiffree cote client, illisible par qui la porte."
- "Elle ne vaut que si elle vit AILLEURS que le site — un tiroir a cote du"
- "serveur ne protege que du disque, pas de l'incendie ni du vol."
- "A refaire quand l'etat change. `restic` ne recopie que les differences."