2026-07-07 03:19:51 -04:00
|
|
|
---
|
|
|
|
|
# make valider — RECETTE D'ACCEPTATION : les services FONCTIONNENT (pas juste déployés).
|
|
|
|
|
# Lecture seule / faible impact. Complète les *-verifier (statiques, sur le plan)
|
|
|
|
|
# et verifier-hote (niveau hôte) par une vérif FONCTIONNELLE de bout en bout.
|
|
|
|
|
#
|
|
|
|
|
# v1 : cibles Prometheus UP + endpoints HTTPS exposés répondent.
|
|
|
|
|
# À venir : courriel bout-en-bout (envoi→IMAP), restauration de sauvegarde.
|
|
|
|
|
|
|
|
|
|
- name: "Recette — Prometheus : toutes les cibles de supervision UP"
|
|
|
|
|
hosts: serveur_prometheus
|
|
|
|
|
gather_facts: false
|
|
|
|
|
become: false
|
|
|
|
|
tasks:
|
|
|
|
|
- name: Interroger l'API des cibles actives
|
|
|
|
|
ansible.builtin.uri:
|
|
|
|
|
url: "http://localhost:9090/api/v1/targets?state=active"
|
|
|
|
|
return_content: true
|
|
|
|
|
register: prom_targets
|
|
|
|
|
|
|
|
|
|
- name: Analyser up / down
|
|
|
|
|
vars:
|
|
|
|
|
cibles: "{{ (prom_targets.content | from_json).data.activeTargets }}"
|
|
|
|
|
en_panne: "{{ cibles | rejectattr('health', 'equalto', 'up') | list }}"
|
|
|
|
|
ansible.builtin.set_fact:
|
|
|
|
|
recette_prom_total: "{{ cibles | length }}"
|
|
|
|
|
recette_prom_down: "{{ en_panne | map(attribute='labels.instance') | list }}"
|
|
|
|
|
|
|
|
|
|
- name: "✔ Prometheus"
|
|
|
|
|
ansible.builtin.assert:
|
|
|
|
|
that: "recette_prom_down | length == 0"
|
|
|
|
|
success_msg: "OK — {{ recette_prom_total }} cibles, toutes UP."
|
|
|
|
|
fail_msg: "ÉCHEC — cibles DOWN : {{ recette_prom_down }}"
|
|
|
|
|
|
|
|
|
|
- name: "Recette — Edge : les vhosts HTTPS exposés répondent"
|
|
|
|
|
hosts: serveur_nginx
|
|
|
|
|
gather_facts: false
|
|
|
|
|
become: true
|
|
|
|
|
tasks:
|
|
|
|
|
- name: Lister les valeurs de la directive server_name (première colonne exacte)
|
|
|
|
|
ansible.builtin.shell:
|
|
|
|
|
cmd: >-
|
Mise en conformité prouvable : registre d'affirmations + make prouver
Le dépôt fait / explique / prouve ce qu'il affirme, vérifiable en une commande.
- Phase 1 : docs/audit/affirmations.md — 54 affirmations publiques tracées vers
une commande de preuve et un statut (✅/🟡/❌/⚪).
- Phase 2 : CLAUDE.md réduit à un pointeur mince ; contradiction SSH levée (le
code applique déjà PasswordAuthentication no + AuthenticationMethods publickey,
conforme à AGENTS.md) ; section AGENTS « Codex » → « agents IA ».
- Phase 3 : parcours démarrage réparé (QUICKSTART renvoyait à un modèle absent,
chemins de voûte faux, commandes make périmées) ; make verifier vert
(ansible-lint 33 → 0 : site.yml généré nommé, pipefail, name[template]) ;
voûte Proxmox unifiée lue par le clonage (all/vault.yml).
- Phase 4 : make prouver → docs/audit/preuve-<date>.md, harnais rejouable qui
rappelle l'outillage existant (aucune validation réimplémentée).
- Phase 5 : parcours QUICKSTART prouvé hors-ligne sur le socle ; modèle socle
rendu valide (autorite interne → auto-heberge) ; split-brain d'inventaire
corrigé (repli sur le répertoire existant, pas principal/).
make prouver : 15 OK, 0 échec, 1 sautée (voûte). ansible-lint : 0 failure.
Écarts découverts en cours de traitement (AFF-097..100) : tous résolus.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 19:53:18 -04:00
|
|
|
set -o pipefail;
|
2026-07-07 03:19:51 -04:00
|
|
|
nginx -T 2>/dev/null | awk '$1=="server_name"{for(i=2;i<=NF;i++){gsub(/;/,"",$i);
|
|
|
|
|
print $i}}' | sort -u
|
Mise en conformité prouvable : registre d'affirmations + make prouver
Le dépôt fait / explique / prouve ce qu'il affirme, vérifiable en une commande.
- Phase 1 : docs/audit/affirmations.md — 54 affirmations publiques tracées vers
une commande de preuve et un statut (✅/🟡/❌/⚪).
- Phase 2 : CLAUDE.md réduit à un pointeur mince ; contradiction SSH levée (le
code applique déjà PasswordAuthentication no + AuthenticationMethods publickey,
conforme à AGENTS.md) ; section AGENTS « Codex » → « agents IA ».
- Phase 3 : parcours démarrage réparé (QUICKSTART renvoyait à un modèle absent,
chemins de voûte faux, commandes make périmées) ; make verifier vert
(ansible-lint 33 → 0 : site.yml généré nommé, pipefail, name[template]) ;
voûte Proxmox unifiée lue par le clonage (all/vault.yml).
- Phase 4 : make prouver → docs/audit/preuve-<date>.md, harnais rejouable qui
rappelle l'outillage existant (aucune validation réimplémentée).
- Phase 5 : parcours QUICKSTART prouvé hors-ligne sur le socle ; modèle socle
rendu valide (autorite interne → auto-heberge) ; split-brain d'inventaire
corrigé (repli sur le répertoire existant, pas principal/).
make prouver : 15 OK, 0 échec, 1 sautée (voûte). ansible-lint : 0 failure.
Écarts découverts en cours de traitement (AFF-097..100) : tous résolus.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 19:53:18 -04:00
|
|
|
executable: /bin/bash
|
2026-07-07 03:19:51 -04:00
|
|
|
register: recette_server_names
|
|
|
|
|
changed_when: false
|
|
|
|
|
|
|
|
|
|
- name: Retenir les vhosts du domaine interne (exclut défauts/placeholders)
|
|
|
|
|
ansible.builtin.set_fact:
|
|
|
|
|
recette_fqdns: >-
|
|
|
|
|
{{ recette_server_names.stdout_lines
|
|
|
|
|
| select('search', '\.' ~ (domaine_interne | regex_escape) ~ '$') | list }}
|
|
|
|
|
|
|
|
|
|
- name: Interroger chaque vhost exposé en HTTPS (via l'edge)
|
|
|
|
|
ansible.builtin.uri:
|
|
|
|
|
url: "https://{{ item }}/"
|
|
|
|
|
validate_certs: false
|
|
|
|
|
status_code: [200, 301, 302, 401, 403]
|
|
|
|
|
follow_redirects: none
|
|
|
|
|
timeout: 10
|
|
|
|
|
loop: "{{ recette_fqdns }}"
|
|
|
|
|
register: recette_http
|
|
|
|
|
failed_when: false
|
|
|
|
|
|
|
|
|
|
- name: "✔ Endpoints HTTPS"
|
|
|
|
|
vars:
|
|
|
|
|
echecs: "{{ recette_http.results | rejectattr('status', 'in', [200, 301, 302, 401, 403]) | list }}"
|
|
|
|
|
ansible.builtin.assert:
|
|
|
|
|
that: "echecs | length == 0"
|
|
|
|
|
success_msg: "OK — {{ recette_http.results | length }} vhosts répondent : {{ recette_fqdns }}"
|
|
|
|
|
fail_msg: "ÉCHEC — vhosts KO : {{ echecs | map(attribute='item') | list }} (statuts {{ echecs | map(attribute='status') | list }})"
|
2026-07-07 03:25:24 -04:00
|
|
|
|
|
|
|
|
- name: "Recette — Courriel bout-en-bout (MTA -> LMTP -> Maildir)"
|
|
|
|
|
hosts: serveur_dovecot
|
|
|
|
|
gather_facts: true
|
|
|
|
|
become: true
|
|
|
|
|
vars:
|
|
|
|
|
recette_mail_dest: "testmail@{{ domaine_interne }}"
|
|
|
|
|
recette_mail_from: "recette@{{ domaine_interne }}"
|
|
|
|
|
tasks:
|
|
|
|
|
- name: Jeton unique du message de test
|
|
|
|
|
ansible.builtin.set_fact:
|
|
|
|
|
recette_mail_token: "SETOPS-VALIDER-{{ ansible_date_time.iso8601_basic_short }}-{{ 999999 | random }}"
|
|
|
|
|
|
|
|
|
|
- name: Envoyer via le MTA (sendmail — flux réel postfix -> LDAP -> LMTP -> Dovecot)
|
|
|
|
|
ansible.builtin.shell:
|
|
|
|
|
cmd: >-
|
Mise en conformité prouvable : registre d'affirmations + make prouver
Le dépôt fait / explique / prouve ce qu'il affirme, vérifiable en une commande.
- Phase 1 : docs/audit/affirmations.md — 54 affirmations publiques tracées vers
une commande de preuve et un statut (✅/🟡/❌/⚪).
- Phase 2 : CLAUDE.md réduit à un pointeur mince ; contradiction SSH levée (le
code applique déjà PasswordAuthentication no + AuthenticationMethods publickey,
conforme à AGENTS.md) ; section AGENTS « Codex » → « agents IA ».
- Phase 3 : parcours démarrage réparé (QUICKSTART renvoyait à un modèle absent,
chemins de voûte faux, commandes make périmées) ; make verifier vert
(ansible-lint 33 → 0 : site.yml généré nommé, pipefail, name[template]) ;
voûte Proxmox unifiée lue par le clonage (all/vault.yml).
- Phase 4 : make prouver → docs/audit/preuve-<date>.md, harnais rejouable qui
rappelle l'outillage existant (aucune validation réimplémentée).
- Phase 5 : parcours QUICKSTART prouvé hors-ligne sur le socle ; modèle socle
rendu valide (autorite interne → auto-heberge) ; split-brain d'inventaire
corrigé (repli sur le répertoire existant, pas principal/).
make prouver : 15 OK, 0 échec, 1 sautée (voûte). ansible-lint : 0 failure.
Écarts découverts en cours de traitement (AFF-097..100) : tous résolus.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 19:53:18 -04:00
|
|
|
set -o pipefail;
|
2026-07-07 03:25:24 -04:00
|
|
|
printf 'Subject: %s\nFrom: %s\nTo: %s\n\nRecette d acceptation Set-OPS.\n'
|
|
|
|
|
'{{ recette_mail_token }}' '{{ recette_mail_from }}' '{{ recette_mail_dest }}'
|
|
|
|
|
| /usr/sbin/sendmail -f '{{ recette_mail_from }}' '{{ recette_mail_dest }}'
|
Mise en conformité prouvable : registre d'affirmations + make prouver
Le dépôt fait / explique / prouve ce qu'il affirme, vérifiable en une commande.
- Phase 1 : docs/audit/affirmations.md — 54 affirmations publiques tracées vers
une commande de preuve et un statut (✅/🟡/❌/⚪).
- Phase 2 : CLAUDE.md réduit à un pointeur mince ; contradiction SSH levée (le
code applique déjà PasswordAuthentication no + AuthenticationMethods publickey,
conforme à AGENTS.md) ; section AGENTS « Codex » → « agents IA ».
- Phase 3 : parcours démarrage réparé (QUICKSTART renvoyait à un modèle absent,
chemins de voûte faux, commandes make périmées) ; make verifier vert
(ansible-lint 33 → 0 : site.yml généré nommé, pipefail, name[template]) ;
voûte Proxmox unifiée lue par le clonage (all/vault.yml).
- Phase 4 : make prouver → docs/audit/preuve-<date>.md, harnais rejouable qui
rappelle l'outillage existant (aucune validation réimplémentée).
- Phase 5 : parcours QUICKSTART prouvé hors-ligne sur le socle ; modèle socle
rendu valide (autorite interne → auto-heberge) ; split-brain d'inventaire
corrigé (repli sur le répertoire existant, pas principal/).
make prouver : 15 OK, 0 échec, 1 sautée (voûte). ansible-lint : 0 failure.
Écarts découverts en cours de traitement (AFF-097..100) : tous résolus.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 19:53:18 -04:00
|
|
|
executable: /bin/bash
|
2026-07-07 03:25:24 -04:00
|
|
|
delegate_to: "{{ groups['serveur_postfix'][0] }}"
|
|
|
|
|
changed_when: true
|
|
|
|
|
|
|
|
|
|
- name: Attendre la remise dans la boîte (doveadm, LMTP asynchrone)
|
|
|
|
|
ansible.builtin.command:
|
|
|
|
|
cmd: "doveadm search -u {{ recette_mail_dest }} mailbox INBOX subject {{ recette_mail_token }}"
|
|
|
|
|
register: recette_mail_search
|
|
|
|
|
until: recette_mail_search.stdout | trim | length > 0
|
|
|
|
|
retries: 10
|
|
|
|
|
delay: 3
|
|
|
|
|
changed_when: false
|
|
|
|
|
failed_when: false
|
|
|
|
|
|
|
|
|
|
- name: "✔ Courriel"
|
|
|
|
|
ansible.builtin.assert:
|
|
|
|
|
that: "recette_mail_search.stdout | trim | length > 0"
|
|
|
|
|
success_msg: "OK — courriel remis à {{ recette_mail_dest }} (envoi -> LMTP -> Maildir prouvé)."
|
|
|
|
|
fail_msg: "ÉCHEC — courriel NON remis à {{ recette_mail_dest }} après 30 s."
|
|
|
|
|
|
|
|
|
|
- name: Nettoyer le message de test (best-effort)
|
|
|
|
|
ansible.builtin.command:
|
|
|
|
|
cmd: "doveadm expunge -u {{ recette_mail_dest }} mailbox INBOX subject {{ recette_mail_token }}"
|
|
|
|
|
changed_when: true
|
|
|
|
|
failed_when: false
|
2026-07-07 03:40:37 -04:00
|
|
|
|
|
|
|
|
- name: "Recette — Sauvegardes : le dernier snapshot restic se restaure"
|
|
|
|
|
hosts: client_backup
|
|
|
|
|
gather_facts: false
|
|
|
|
|
become: true
|
|
|
|
|
tasks:
|
recette : la donnee REVIENT — restauration eprouvee, pas seulement sauvegarde
On savait que la donnee partait et arrivait. Pas qu'elle revenait.
Trois charges critiques eprouvees :
- cles de l'AC (infra-pki-01) : restauration + comparaison OCTET POUR OCTET,
12 fichiers, 11 identiques, root_ca_key et intermediate_ca_key compris. Seul
ecart : db/000000.vlog, le journal badger de step-ca, qui avance a chaque
emission. Attendu.
- annuaire (idm-01) : slapadd -u (essai a blanc) sur le LDIF restaure —
REJOUABLE, 7 entrees dont uid=sysadmin.
- bases (data-sql-01) : section forgejo REJOUEE dans une base d'epreuve —
0 erreur, 130 tables, comptes reels. Production verifiee intacte apres.
Controles negatifs : un LDIF corrompu fait sortir slapadd en 1 ; la garde SQL a
refuse une section mal decoupee.
LE PIEGE pg_dumpall : il ecrit CREATE DATABASE <suivante> AVANT le \connect
correspondant. Decouper « du \connect X au \connect suivant » emporte un ordre
visant une AUTRE base. Ma premiere decoupe l'a fait ; la garde a refuse de
rejouer. Consigne dans runbooks-exploitation.md §5.
valider.yml ne ment plus : il exigeait une restauration de TOUS les noeuds
client_backup, donc echouait sur ceux qui ne detiennent rien et sur les depots
vides. Quatre verdicts desormais (OK / A CONFIRMER / SANS OBJET / ECHEC),
ALIGNES sur ceux de la supervision. Et il prouve que l'annuaire est rejouable,
pas seulement present.
make valider : 0 echec sur toute la flotte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 09:50:51 -04:00
|
|
|
# Un nœud du groupe peut légitimement ne rien détenir de non régénérable (nginx,
|
|
|
|
|
# PowerDNS, Icinga : leur configuration se REDÉPLOIE). `client_backup` n'y installe
|
|
|
|
|
# alors aucune sauvegarde — et exiger une restauration là serait exiger de restaurer
|
|
|
|
|
# le néant. On distingue donc « rien à sauvegarder » de « sauvegarde cassée ».
|
2026-07-07 03:40:37 -04:00
|
|
|
- name: Restaurer le dernier snapshot dans un dossier temporaire (lecture seule sur le dépôt)
|
|
|
|
|
ansible.builtin.shell:
|
|
|
|
|
cmd: |
|
|
|
|
|
set -o pipefail
|
recette : la donnee REVIENT — restauration eprouvee, pas seulement sauvegarde
On savait que la donnee partait et arrivait. Pas qu'elle revenait.
Trois charges critiques eprouvees :
- cles de l'AC (infra-pki-01) : restauration + comparaison OCTET POUR OCTET,
12 fichiers, 11 identiques, root_ca_key et intermediate_ca_key compris. Seul
ecart : db/000000.vlog, le journal badger de step-ca, qui avance a chaque
emission. Attendu.
- annuaire (idm-01) : slapadd -u (essai a blanc) sur le LDIF restaure —
REJOUABLE, 7 entrees dont uid=sysadmin.
- bases (data-sql-01) : section forgejo REJOUEE dans une base d'epreuve —
0 erreur, 130 tables, comptes reels. Production verifiee intacte apres.
Controles negatifs : un LDIF corrompu fait sortir slapadd en 1 ; la garde SQL a
refuse une section mal decoupee.
LE PIEGE pg_dumpall : il ecrit CREATE DATABASE <suivante> AVANT le \connect
correspondant. Decouper « du \connect X au \connect suivant » emporte un ordre
visant une AUTRE base. Ma premiere decoupe l'a fait ; la garde a refuse de
rejouer. Consigne dans runbooks-exploitation.md §5.
valider.yml ne ment plus : il exigeait une restauration de TOUS les noeuds
client_backup, donc echouait sur ceux qui ne detiennent rien et sur les depots
vides. Quatre verdicts desormais (OK / A CONFIRMER / SANS OBJET / ECHEC),
ALIGNES sur ceux de la supervision. Et il prouve que l'annuaire est rejouable,
pas seulement present.
make valider : 0 echec sur toute la flotte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 09:50:51 -04:00
|
|
|
if [ ! -x /usr/local/sbin/setops-sauvegarder.sh ]; then
|
|
|
|
|
echo "sansobjet=1 snapshots=0 restored_files=0 ldif_rejouable=-1"; exit 0
|
|
|
|
|
fi
|
2026-07-07 03:40:37 -04:00
|
|
|
repo=$(grep -oP 'RESTIC_REPOSITORY="\K[^"]+' /usr/local/sbin/setops-sauvegarder.sh)
|
|
|
|
|
export RESTIC_REPOSITORY="$repo" RESTIC_PASSWORD_FILE=/etc/setops/restic.pass
|
|
|
|
|
n=$(restic snapshots 2>/dev/null | grep -cE '^[0-9a-f]{8} ' || echo 0)
|
recette : la donnee REVIENT — restauration eprouvee, pas seulement sauvegarde
On savait que la donnee partait et arrivait. Pas qu'elle revenait.
Trois charges critiques eprouvees :
- cles de l'AC (infra-pki-01) : restauration + comparaison OCTET POUR OCTET,
12 fichiers, 11 identiques, root_ca_key et intermediate_ca_key compris. Seul
ecart : db/000000.vlog, le journal badger de step-ca, qui avance a chaque
emission. Attendu.
- annuaire (idm-01) : slapadd -u (essai a blanc) sur le LDIF restaure —
REJOUABLE, 7 entrees dont uid=sysadmin.
- bases (data-sql-01) : section forgejo REJOUEE dans une base d'epreuve —
0 erreur, 130 tables, comptes reels. Production verifiee intacte apres.
Controles negatifs : un LDIF corrompu fait sortir slapadd en 1 ; la garde SQL a
refuse une section mal decoupee.
LE PIEGE pg_dumpall : il ecrit CREATE DATABASE <suivante> AVANT le \connect
correspondant. Decouper « du \connect X au \connect suivant » emporte un ordre
visant une AUTRE base. Ma premiere decoupe l'a fait ; la garde a refuse de
rejouer. Consigne dans runbooks-exploitation.md §5.
valider.yml ne ment plus : il exigeait une restauration de TOUS les noeuds
client_backup, donc echouait sur ceux qui ne detiennent rien et sur les depots
vides. Quatre verdicts desormais (OK / A CONFIRMER / SANS OBJET / ECHEC),
ALIGNES sur ceux de la supervision. Et il prouve que l'annuaire est rejouable,
pas seulement present.
make valider : 0 echec sur toute la flotte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 09:50:51 -04:00
|
|
|
if [ "$n" -lt 1 ]; then echo "sansobjet=0 snapshots=0 restored_files=-1 ldif_rejouable=-1"; exit 0; fi
|
2026-07-07 03:40:37 -04:00
|
|
|
tmp=$(mktemp -d)
|
recette : un depot occupe n est pas une sauvegarde cassee
make valider rendait ECHEC sur data-sql-01, et la meme commande rejouee a la main
restaurait 5 fichiers. restic verrouille son depot pendant qu il ecrit : une recette
qui croise la fenetre de sauvegarde lit un echec de RESTAURATION la ou il n y a
qu une attente. Verdict juste sur l instant, faux sur le fond, et DEPENDANT DE
L HEURE a laquelle on la lance.
ON REESSAIE, MAIS ON NE MASQUE PAS. Trois tentatives espacees couvrent un verrou
d ecriture ; au-dela, l echec est REEL et la recette rend la RAISON que restic a
donnee, au lieu d un -1 muet qui obligeait a se connecter pour comprendre.
CONTROLE VERIFIE SUR LA MACHINE : cle de dechiffrement retiree ->
« ECHEC apres 3 tentatives — restic dit : Fatal: Resolving password failed ».
La recette attrape toujours une vraie panne, et elle en donne la cause.
DEUX FAUTES A MOI EN CHEMIN, CORRIGEES.
`[ "$f" -lt 0 ] && printf ...` : la DERNIERE commande d un script decide de son code
de sortie, et un test faux rend 1. La tache echouait donc exactement sur les
machines ou la restauration avait REUSSI — trois hotes verts declares en echec.
Puis la ligne `raison=` n etait emise qu en cas d echec : le gabarit cherchait un
champ absent, rendait None, et explosait sur les DIX machines saines. Elle est
desormais TOUJOURS emise, vide en cas de succes. Un champ toujours present coute un
octet et supprime un cas.
make valider : rc=0. make verifier : vert. make prouver : 55 OK, 0 echec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-31 20:41:39 -04:00
|
|
|
# LE DEPOT PEUT ETRE OCCUPE, ET CE N'EST PAS UNE SAUVEGARDE CASSEE (2026-08-31).
|
|
|
|
|
#
|
|
|
|
|
# `restic` verrouille son depot pendant qu'il ecrit. Une recette qui croise la
|
|
|
|
|
# fenetre de sauvegarde lit donc un echec de RESTAURATION la ou il n'y a qu'une
|
|
|
|
|
# attente — verdict juste sur l'instant, faux sur le fond, et dependant de
|
|
|
|
|
# l'heure a laquelle on l'a lancee. Constate sur `data-sql-01`, dont la
|
|
|
|
|
# sauvegarde de 02h38 tenait encore le depot : `ECHEC` au premier passage,
|
|
|
|
|
# `OK — 5 fichiers` en rejouant la meme commande a la main.
|
|
|
|
|
#
|
|
|
|
|
# ON REESSAIE, MAIS ON NE MASQUE PAS. Trois tentatives espacees couvrent
|
|
|
|
|
# largement un verrou d'ecriture ; au-dela, l'echec est REEL et on rend la
|
|
|
|
|
# RAISON que restic a donnee, au lieu d'un `-1` muet qui laisse chercher.
|
|
|
|
|
f=-1
|
|
|
|
|
err=""
|
|
|
|
|
for essai in 1 2 3; do
|
|
|
|
|
if err=$(restic restore latest --target "$tmp" 2>&1 >/dev/null); then
|
|
|
|
|
f=$(find "$tmp" -type f | wc -l)
|
|
|
|
|
break
|
|
|
|
|
fi
|
|
|
|
|
rm -rf "$tmp"; tmp=$(mktemp -d)
|
|
|
|
|
[ "$essai" -lt 3 ] && sleep 20
|
|
|
|
|
done
|
|
|
|
|
# La raison voyage sur une ligne a part : le verdict la cite, et l'exploitant
|
|
|
|
|
# n'a pas a se connecter pour savoir ce que restic a dit.
|
|
|
|
|
#
|
|
|
|
|
# `if` ET NON `[ ... ] && ...` : la DERNIERE commande d'un script decide de son
|
|
|
|
|
# code de sortie, et un test faux rend 1. Ecrit en `&&`, cette ligne faisait
|
|
|
|
|
# echouer la tache exactement sur les machines ou la restauration avait REUSSI
|
|
|
|
|
# — trois hotes verts declares en echec (2026-08-31).
|
|
|
|
|
# TOUJOURS EMISE, vide en cas de succes. Une ligne parfois absente oblige son
|
|
|
|
|
# lecteur a gerer l'absence, et c'est la que le gabarit s'est casse : la
|
|
|
|
|
# recherche rendait `None` et le verdict explosait sur les DIX machines saines
|
|
|
|
|
# (2026-08-31). Un champ toujours present coute un octet et supprime un cas.
|
|
|
|
|
printf 'raison=%s\n' "$(printf '%s' "$err" | tr '\n' ' ' | cut -c1-160)"
|
recette : la donnee REVIENT — restauration eprouvee, pas seulement sauvegarde
On savait que la donnee partait et arrivait. Pas qu'elle revenait.
Trois charges critiques eprouvees :
- cles de l'AC (infra-pki-01) : restauration + comparaison OCTET POUR OCTET,
12 fichiers, 11 identiques, root_ca_key et intermediate_ca_key compris. Seul
ecart : db/000000.vlog, le journal badger de step-ca, qui avance a chaque
emission. Attendu.
- annuaire (idm-01) : slapadd -u (essai a blanc) sur le LDIF restaure —
REJOUABLE, 7 entrees dont uid=sysadmin.
- bases (data-sql-01) : section forgejo REJOUEE dans une base d'epreuve —
0 erreur, 130 tables, comptes reels. Production verifiee intacte apres.
Controles negatifs : un LDIF corrompu fait sortir slapadd en 1 ; la garde SQL a
refuse une section mal decoupee.
LE PIEGE pg_dumpall : il ecrit CREATE DATABASE <suivante> AVANT le \connect
correspondant. Decouper « du \connect X au \connect suivant » emporte un ordre
visant une AUTRE base. Ma premiere decoupe l'a fait ; la garde a refuse de
rejouer. Consigne dans runbooks-exploitation.md §5.
valider.yml ne ment plus : il exigeait une restauration de TOUS les noeuds
client_backup, donc echouait sur ceux qui ne detiennent rien et sur les depots
vides. Quatre verdicts desormais (OK / A CONFIRMER / SANS OBJET / ECHEC),
ALIGNES sur ceux de la supervision. Et il prouve que l'annuaire est rejouable,
pas seulement present.
make valider : 0 echec sur toute la flotte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 09:50:51 -04:00
|
|
|
# L'annuaire est la seule charge dont on peut prouver ICI qu'elle est REJOUABLE,
|
|
|
|
|
# sans rien écrire : `slapadd -u` est un essai à blanc. Éprouvé le 2026-08-12,
|
|
|
|
|
# avec contrôle négatif (un LDIF corrompu sort en 1).
|
|
|
|
|
ldif=$(find "$tmp" -name 'annuaire.ldif' 2>/dev/null | head -1)
|
|
|
|
|
rejouable=-1
|
|
|
|
|
if [ -n "$ldif" ]; then
|
|
|
|
|
base=$(sed -n 's/^dn: \(dc=.*\)$/\1/p' "$ldif" | head -1)
|
|
|
|
|
if slapadd -u -b "$base" -l "$ldif" >/dev/null 2>&1; then rejouable=1; else rejouable=0; fi
|
|
|
|
|
fi
|
2026-07-07 03:40:37 -04:00
|
|
|
rm -rf "$tmp"
|
recette : la donnee REVIENT — restauration eprouvee, pas seulement sauvegarde
On savait que la donnee partait et arrivait. Pas qu'elle revenait.
Trois charges critiques eprouvees :
- cles de l'AC (infra-pki-01) : restauration + comparaison OCTET POUR OCTET,
12 fichiers, 11 identiques, root_ca_key et intermediate_ca_key compris. Seul
ecart : db/000000.vlog, le journal badger de step-ca, qui avance a chaque
emission. Attendu.
- annuaire (idm-01) : slapadd -u (essai a blanc) sur le LDIF restaure —
REJOUABLE, 7 entrees dont uid=sysadmin.
- bases (data-sql-01) : section forgejo REJOUEE dans une base d'epreuve —
0 erreur, 130 tables, comptes reels. Production verifiee intacte apres.
Controles negatifs : un LDIF corrompu fait sortir slapadd en 1 ; la garde SQL a
refuse une section mal decoupee.
LE PIEGE pg_dumpall : il ecrit CREATE DATABASE <suivante> AVANT le \connect
correspondant. Decouper « du \connect X au \connect suivant » emporte un ordre
visant une AUTRE base. Ma premiere decoupe l'a fait ; la garde a refuse de
rejouer. Consigne dans runbooks-exploitation.md §5.
valider.yml ne ment plus : il exigeait une restauration de TOUS les noeuds
client_backup, donc echouait sur ceux qui ne detiennent rien et sur les depots
vides. Quatre verdicts desormais (OK / A CONFIRMER / SANS OBJET / ECHEC),
ALIGNES sur ceux de la supervision. Et il prouve que l'annuaire est rejouable,
pas seulement present.
make valider : 0 echec sur toute la flotte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 09:50:51 -04:00
|
|
|
echo "sansobjet=0 snapshots=$n restored_files=$f ldif_rejouable=$rejouable"
|
2026-07-07 03:40:37 -04:00
|
|
|
executable: /bin/bash
|
|
|
|
|
register: recette_restore
|
|
|
|
|
changed_when: false
|
|
|
|
|
|
|
|
|
|
- name: "✔ Sauvegardes"
|
|
|
|
|
vars:
|
recette : la donnee REVIENT — restauration eprouvee, pas seulement sauvegarde
On savait que la donnee partait et arrivait. Pas qu'elle revenait.
Trois charges critiques eprouvees :
- cles de l'AC (infra-pki-01) : restauration + comparaison OCTET POUR OCTET,
12 fichiers, 11 identiques, root_ca_key et intermediate_ca_key compris. Seul
ecart : db/000000.vlog, le journal badger de step-ca, qui avance a chaque
emission. Attendu.
- annuaire (idm-01) : slapadd -u (essai a blanc) sur le LDIF restaure —
REJOUABLE, 7 entrees dont uid=sysadmin.
- bases (data-sql-01) : section forgejo REJOUEE dans une base d'epreuve —
0 erreur, 130 tables, comptes reels. Production verifiee intacte apres.
Controles negatifs : un LDIF corrompu fait sortir slapadd en 1 ; la garde SQL a
refuse une section mal decoupee.
LE PIEGE pg_dumpall : il ecrit CREATE DATABASE <suivante> AVANT le \connect
correspondant. Decouper « du \connect X au \connect suivant » emporte un ordre
visant une AUTRE base. Ma premiere decoupe l'a fait ; la garde a refuse de
rejouer. Consigne dans runbooks-exploitation.md §5.
valider.yml ne ment plus : il exigeait une restauration de TOUS les noeuds
client_backup, donc echouait sur ceux qui ne detiennent rien et sur les depots
vides. Quatre verdicts desormais (OK / A CONFIRMER / SANS OBJET / ECHEC),
ALIGNES sur ceux de la supervision. Et il prouve que l'annuaire est rejouable,
pas seulement present.
make valider : 0 echec sur toute la flotte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 09:50:51 -04:00
|
|
|
sansobjet: "{{ recette_restore.stdout | regex_search('sansobjet=(\\d+)', '\\1') | first | int }}"
|
2026-07-07 03:40:37 -04:00
|
|
|
restaures: "{{ recette_restore.stdout | regex_search('restored_files=(-?\\d+)', '\\1') | first | int }}"
|
|
|
|
|
snaps: "{{ recette_restore.stdout | regex_search('snapshots=(\\d+)', '\\1') | first | int }}"
|
recette : la donnee REVIENT — restauration eprouvee, pas seulement sauvegarde
On savait que la donnee partait et arrivait. Pas qu'elle revenait.
Trois charges critiques eprouvees :
- cles de l'AC (infra-pki-01) : restauration + comparaison OCTET POUR OCTET,
12 fichiers, 11 identiques, root_ca_key et intermediate_ca_key compris. Seul
ecart : db/000000.vlog, le journal badger de step-ca, qui avance a chaque
emission. Attendu.
- annuaire (idm-01) : slapadd -u (essai a blanc) sur le LDIF restaure —
REJOUABLE, 7 entrees dont uid=sysadmin.
- bases (data-sql-01) : section forgejo REJOUEE dans une base d'epreuve —
0 erreur, 130 tables, comptes reels. Production verifiee intacte apres.
Controles negatifs : un LDIF corrompu fait sortir slapadd en 1 ; la garde SQL a
refuse une section mal decoupee.
LE PIEGE pg_dumpall : il ecrit CREATE DATABASE <suivante> AVANT le \connect
correspondant. Decouper « du \connect X au \connect suivant » emporte un ordre
visant une AUTRE base. Ma premiere decoupe l'a fait ; la garde a refuse de
rejouer. Consigne dans runbooks-exploitation.md §5.
valider.yml ne ment plus : il exigeait une restauration de TOUS les noeuds
client_backup, donc echouait sur ceux qui ne detiennent rien et sur les depots
vides. Quatre verdicts desormais (OK / A CONFIRMER / SANS OBJET / ECHEC),
ALIGNES sur ceux de la supervision. Et il prouve que l'annuaire est rejouable,
pas seulement present.
make valider : 0 echec sur toute la flotte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 09:50:51 -04:00
|
|
|
rejouable: "{{ ((recette_restore.stdout | regex_search('ldif_rejouable=(-?\\d+)', '\\1')) or [-1]) | first | int }}"
|
recette : un depot occupe n est pas une sauvegarde cassee
make valider rendait ECHEC sur data-sql-01, et la meme commande rejouee a la main
restaurait 5 fichiers. restic verrouille son depot pendant qu il ecrit : une recette
qui croise la fenetre de sauvegarde lit un echec de RESTAURATION la ou il n y a
qu une attente. Verdict juste sur l instant, faux sur le fond, et DEPENDANT DE
L HEURE a laquelle on la lance.
ON REESSAIE, MAIS ON NE MASQUE PAS. Trois tentatives espacees couvrent un verrou
d ecriture ; au-dela, l echec est REEL et la recette rend la RAISON que restic a
donnee, au lieu d un -1 muet qui obligeait a se connecter pour comprendre.
CONTROLE VERIFIE SUR LA MACHINE : cle de dechiffrement retiree ->
« ECHEC apres 3 tentatives — restic dit : Fatal: Resolving password failed ».
La recette attrape toujours une vraie panne, et elle en donne la cause.
DEUX FAUTES A MOI EN CHEMIN, CORRIGEES.
`[ "$f" -lt 0 ] && printf ...` : la DERNIERE commande d un script decide de son code
de sortie, et un test faux rend 1. La tache echouait donc exactement sur les
machines ou la restauration avait REUSSI — trois hotes verts declares en echec.
Puis la ligne `raison=` n etait emise qu en cas d echec : le gabarit cherchait un
champ absent, rendait None, et explosait sur les DIX machines saines. Elle est
desormais TOUJOURS emise, vide en cas de succes. Un champ toujours present coute un
octet et supprime un cas.
make valider : rc=0. make verifier : vert. make prouver : 55 OK, 0 echec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-31 20:41:39 -04:00
|
|
|
# Ce que restic a dit, quand il a refuse. Vide sinon — la ligne est toujours la.
|
|
|
|
|
raison: "{{ ((recette_restore.stdout | regex_search('raison=(.*)', '\\1')) or ['']) | first | trim }}"
|
recette : la donnee REVIENT — restauration eprouvee, pas seulement sauvegarde
On savait que la donnee partait et arrivait. Pas qu'elle revenait.
Trois charges critiques eprouvees :
- cles de l'AC (infra-pki-01) : restauration + comparaison OCTET POUR OCTET,
12 fichiers, 11 identiques, root_ca_key et intermediate_ca_key compris. Seul
ecart : db/000000.vlog, le journal badger de step-ca, qui avance a chaque
emission. Attendu.
- annuaire (idm-01) : slapadd -u (essai a blanc) sur le LDIF restaure —
REJOUABLE, 7 entrees dont uid=sysadmin.
- bases (data-sql-01) : section forgejo REJOUEE dans une base d'epreuve —
0 erreur, 130 tables, comptes reels. Production verifiee intacte apres.
Controles negatifs : un LDIF corrompu fait sortir slapadd en 1 ; la garde SQL a
refuse une section mal decoupee.
LE PIEGE pg_dumpall : il ecrit CREATE DATABASE <suivante> AVANT le \connect
correspondant. Decouper « du \connect X au \connect suivant » emporte un ordre
visant une AUTRE base. Ma premiere decoupe l'a fait ; la garde a refuse de
rejouer. Consigne dans runbooks-exploitation.md §5.
valider.yml ne ment plus : il exigeait une restauration de TOUS les noeuds
client_backup, donc echouait sur ceux qui ne detiennent rien et sur les depots
vides. Quatre verdicts desormais (OK / A CONFIRMER / SANS OBJET / ECHEC),
ALIGNES sur ceux de la supervision. Et il prouve que l'annuaire est rejouable,
pas seulement present.
make valider : 0 echec sur toute la flotte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 09:50:51 -04:00
|
|
|
# « L'instantané n'emporte aucun fichier » n'est PAS un échec de restauration : la
|
|
|
|
|
# restauration a parfaitement fonctionné, il n'y avait rien à rendre. La machine ne
|
|
|
|
|
# peut pas distinguer « les données ont disparu » de « il n'y en a pas encore ».
|
|
|
|
|
# La supervision (serveur_backup -> Icinga) tranche pareil : AVERTISSEMENT, à
|
|
|
|
|
# confirmer par un humain. Deux verdicts opposés sur le même fait apprendraient à
|
|
|
|
|
# ignorer l'un des deux.
|
2026-07-07 03:40:37 -04:00
|
|
|
ansible.builtin.assert:
|
recette : la donnee REVIENT — restauration eprouvee, pas seulement sauvegarde
On savait que la donnee partait et arrivait. Pas qu'elle revenait.
Trois charges critiques eprouvees :
- cles de l'AC (infra-pki-01) : restauration + comparaison OCTET POUR OCTET,
12 fichiers, 11 identiques, root_ca_key et intermediate_ca_key compris. Seul
ecart : db/000000.vlog, le journal badger de step-ca, qui avance a chaque
emission. Attendu.
- annuaire (idm-01) : slapadd -u (essai a blanc) sur le LDIF restaure —
REJOUABLE, 7 entrees dont uid=sysadmin.
- bases (data-sql-01) : section forgejo REJOUEE dans une base d'epreuve —
0 erreur, 130 tables, comptes reels. Production verifiee intacte apres.
Controles negatifs : un LDIF corrompu fait sortir slapadd en 1 ; la garde SQL a
refuse une section mal decoupee.
LE PIEGE pg_dumpall : il ecrit CREATE DATABASE <suivante> AVANT le \connect
correspondant. Decouper « du \connect X au \connect suivant » emporte un ordre
visant une AUTRE base. Ma premiere decoupe l'a fait ; la garde a refuse de
rejouer. Consigne dans runbooks-exploitation.md §5.
valider.yml ne ment plus : il exigeait une restauration de TOUS les noeuds
client_backup, donc echouait sur ceux qui ne detiennent rien et sur les depots
vides. Quatre verdicts desormais (OK / A CONFIRMER / SANS OBJET / ECHEC),
ALIGNES sur ceux de la supervision. Et il prouve que l'annuaire est rejouable,
pas seulement present.
make valider : 0 echec sur toute la flotte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 09:50:51 -04:00
|
|
|
that:
|
|
|
|
|
- restaures | int >= 0
|
|
|
|
|
- rejouable | int != 0
|
|
|
|
|
success_msg: >-
|
|
|
|
|
{{ (sansobjet | int == 1) | ternary(
|
|
|
|
|
'SANS OBJET — ce nœud ne détient aucun état non régénérable.',
|
|
|
|
|
(restaures | int == 0) | ternary(
|
|
|
|
|
'À CONFIRMER — restauration OK mais l instantané n emporte AUCUN fichier ('
|
|
|
|
|
~ snaps ~ ' snapshot(s)). Légitime si ce nœud n a pas encore de données.',
|
|
|
|
|
'OK — ' ~ snaps ~ ' snapshot(s), restauration vérifiée (' ~ restaures ~ ' fichiers)'
|
|
|
|
|
~ ((rejouable | int == 1) | ternary(', annuaire REJOUABLE (slapadd -u).', '.')))) }}
|
|
|
|
|
fail_msg: >-
|
recette : un depot occupe n est pas une sauvegarde cassee
make valider rendait ECHEC sur data-sql-01, et la meme commande rejouee a la main
restaurait 5 fichiers. restic verrouille son depot pendant qu il ecrit : une recette
qui croise la fenetre de sauvegarde lit un echec de RESTAURATION la ou il n y a
qu une attente. Verdict juste sur l instant, faux sur le fond, et DEPENDANT DE
L HEURE a laquelle on la lance.
ON REESSAIE, MAIS ON NE MASQUE PAS. Trois tentatives espacees couvrent un verrou
d ecriture ; au-dela, l echec est REEL et la recette rend la RAISON que restic a
donnee, au lieu d un -1 muet qui obligeait a se connecter pour comprendre.
CONTROLE VERIFIE SUR LA MACHINE : cle de dechiffrement retiree ->
« ECHEC apres 3 tentatives — restic dit : Fatal: Resolving password failed ».
La recette attrape toujours une vraie panne, et elle en donne la cause.
DEUX FAUTES A MOI EN CHEMIN, CORRIGEES.
`[ "$f" -lt 0 ] && printf ...` : la DERNIERE commande d un script decide de son code
de sortie, et un test faux rend 1. La tache echouait donc exactement sur les
machines ou la restauration avait REUSSI — trois hotes verts declares en echec.
Puis la ligne `raison=` n etait emise qu en cas d echec : le gabarit cherchait un
champ absent, rendait None, et explosait sur les DIX machines saines. Elle est
desormais TOUJOURS emise, vide en cas de succes. Un champ toujours present coute un
octet et supprime un cas.
make valider : rc=0. make verifier : vert. make prouver : 55 OK, 0 echec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
2026-08-31 20:41:39 -04:00
|
|
|
ÉCHEC — la RESTAURATION elle-même a échoué après 3 tentatives
|
|
|
|
|
(snapshots={{ snaps }}){{ raison | ternary(' — restic dit : ' ~ raison, '') }}{{
|
recette : la donnee REVIENT — restauration eprouvee, pas seulement sauvegarde
On savait que la donnee partait et arrivait. Pas qu'elle revenait.
Trois charges critiques eprouvees :
- cles de l'AC (infra-pki-01) : restauration + comparaison OCTET POUR OCTET,
12 fichiers, 11 identiques, root_ca_key et intermediate_ca_key compris. Seul
ecart : db/000000.vlog, le journal badger de step-ca, qui avance a chaque
emission. Attendu.
- annuaire (idm-01) : slapadd -u (essai a blanc) sur le LDIF restaure —
REJOUABLE, 7 entrees dont uid=sysadmin.
- bases (data-sql-01) : section forgejo REJOUEE dans une base d'epreuve —
0 erreur, 130 tables, comptes reels. Production verifiee intacte apres.
Controles negatifs : un LDIF corrompu fait sortir slapadd en 1 ; la garde SQL a
refuse une section mal decoupee.
LE PIEGE pg_dumpall : il ecrit CREATE DATABASE <suivante> AVANT le \connect
correspondant. Decouper « du \connect X au \connect suivant » emporte un ordre
visant une AUTRE base. Ma premiere decoupe l'a fait ; la garde a refuse de
rejouer. Consigne dans runbooks-exploitation.md §5.
valider.yml ne ment plus : il exigeait une restauration de TOUS les noeuds
client_backup, donc echouait sur ceux qui ne detiennent rien et sur les depots
vides. Quatre verdicts desormais (OK / A CONFIRMER / SANS OBJET / ECHEC),
ALIGNES sur ceux de la supervision. Et il prouve que l'annuaire est rejouable,
pas seulement present.
make valider : 0 echec sur toute la flotte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 09:50:51 -04:00
|
|
|
(rejouable | int == 0) | ternary(', et l annuaire restauré N EST PAS rejouable', '') }}.
|