Some checks are pending
verifier / verifier (push) Waiting to run
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
154 lines
6.5 KiB
YAML
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."
|