--- # 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=` 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."