serveur_backup : ce qu un depot peut affirmer sans pouvoir lire

Les instantanes sont chiffres cote client : ce serveur heberge des octets qu il
ne peut pas ouvrir, donc pas juger. La verification suit la cle, et
client_backup la fait deja depuis chaque noeud.

Ce que la sonde ajoute : elle voit TOUT DE SUITE, et depuis la cause, ce que les
clients ne decouvriront qu a leur prochaine execution. Lecture seule apres une
erreur disque, volume plein, droits derives — le depot refuse alors tout le
monde, et neuf rouges epars ne designent pas une cause commune.

Elle ECRIT vraiment, sous l identite qui depose. Un test -w ment sur un montage
en lecture seule et sur un quota atteint.

Corrige aussi l en-tete de setops-sauvegardes.conf.j2, qui nommait encore
backup-01 comme pousseur — retire le 2026-09-02, et c est tout le sujet. Le code
avait suivi la decision, l en-tete non.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
This commit is contained in:
Daniel Allaire 2026-09-14 09:58:54 -04:00
parent 07216a5d30
commit 7348eb93d1
6 changed files with 116 additions and 2 deletions

View file

@ -76,7 +76,7 @@
| P61 | Schema du plan : il decrit tout ce que les plans contiennent | AFF-033 | ✅ OK | Le schema decrit 47 champ(s) sur 6 registres ; il couvre tout ce que les plans reels contiennent, et la FORME de chaque champ (scalaire / objet / table) corresp |
| P62 | Schema du plan : il decrit tout ce que le MOTEUR accepte | AFF-033 | ✅ OK | Les 4 validateurs n'acceptent aucun champ que le schema ignore (applications:8, bases_donnees:4, domaines_publics:5, serveurs:3 champ(s) lus par validateur). |
| P63 | cloud-init nait avec la VM et ne lui survit pas | — | ✅ OK | cloud-init est au gabarit (la premiere seconde), absent du socle (pas de va-et-vient), et retire par le durcissement — avec la garde qui verifie que le reseau s |
| P64 | Sondes de supervision : declarees ET deposees | — | ✅ OK | 30 sonde(s) declaree(s) ET deposee(s), chacune avec sa raison et son `ttl` : client_journal/journaux, client_metrique/metriques, client_pki/certificat, serveur_ |
| P64 | Sondes de supervision : declarees ET deposees | — | ✅ OK | 31 sonde(s) declaree(s) ET deposee(s), chacune avec sa raison et son `ttl` : client_journal/journaux, client_metrique/metriques, client_pki/certificat, serveur_ |
| P65 | Depots tiers : demandes au cache, jamais en HTTPS direct | — | ✅ OK | 4 depot(s) tiers relaye(s) par le cache, aucun role ne les vise en https:// ecrit en dur. |
| P66 | Clients OIDC : chaque URI vise un nom que le plan expose | — | ✅ OK | 4 client(s) OIDC, toutes leurs URI visent un FQDN que le plan expose (6 exposition(s)). |
| P67 | Nom public : le service porte celui du plan, pas celui du role | — | ✅ OK | 6 service(s) expose(s) portent le nom du plan (6 groupe(s) derive(s)). |

View file

@ -57,3 +57,13 @@ serveur_backup_noeuds_attendus: >-
{{ (client_backup_groupes_etat | default([]) | map('extract', groups)
| select('defined') | flatten | unique | list)
| intersect(groups['client_backup'] | default([])) }}
# --- SONDE DE SUPERVISION -------------------------------------------------------------
#
# L'ENDROIT QUE LA SONDE ESSAIE D'ECRIRE. C'est la racine du depot par defaut — celle que
# les clients atteignent en sftp.
#
# C'EST AUSSI LA MISE EN DEFAUT : pointer un chemin non inscriptible (ou inexistant) rend
# CRITIQUE sans toucher au vrai depot, ce qui rend la seconde preuve REJOUABLE. Une sonde
# se prouve deux fois — verte sur le sain, rouge sur le casse.
serveur_backup_sonde_racine: "{{ serveur_backup_racine }}"

View file

@ -0,0 +1,28 @@
---
# Supervision derivee du role. Voir docs/supervision-conception.md.
#
# UNE SEULE SONDE, ET ELLE NE JUGE RIEN DU CONTENU.
#
# Les instantanes sont chiffres cote client : ce serveur heberge des octets qu'il ne peut
# pas ouvrir, donc qu'il ne peut pas juger. « Cet instantane est-il recent, complet,
# restaurable ? » se demande a qui detient la cle — et `client_backup` le fait deja depuis
# chaque noeud, avec son propre minuteur. LA VERIFICATION SUIT LA CLE.
#
# CE QUE CELLE-CI AJOUTE, ET POURQUOI ELLE N'EST PAS UN DOUBLON : elle voit TOUT DE SUITE,
# et depuis la cause, ce que les clients ne decouvriront qu'a leur prochaine execution,
# chacun de son cote. Un systeme de fichiers remonte en lecture seule, un volume plein,
# des droits derives — le depot refuse alors tout le monde.
#
# NEUF ROUGES EPARS NE DESIGNENT PAS UNE CAUSE COMMUNE. C'est sa seule raison d'etre :
# dire UNE fois, ici, ce que neuf machines diront plus tard sans savoir qu'elles disent
# la meme chose.
#
# TTL de 5400 s pour un porteur qui passe aux 15 min : trois passages manques avant la
# peremption. Le silence alerte autant que l'echec.
sondes:
- nom: depot
ttl: 5400
raison: 'Le depot accepte-t-il encore une ecriture ? Il ne juge pas le contenu — chiffre
cote client, il ne peut pas l''ouvrir. Il constate que l''endroit prend encore : en
lecture seule, plein ou aux droits derives, il refuse tout le monde, et neuf clients
rougiront un par un sans designer la cause commune.'

View file

@ -42,3 +42,21 @@
- name: Vérification locale des dépôts (là où ce dépôt peut les ouvrir)
ansible.builtin.include_tasks: verification.yml
when: serveur_backup_verification_locale | bool
# LE ROLE QUI POSSEDE LA VERITE DEPOSE SA PROPRE SONDE. `client_sante` la fait tourner et
# pousse le resultat ; il n'a pas a savoir ce qu'elle mesure.
- name: Assurer le repertoire des sondes de supervision
ansible.builtin.file:
path: /usr/local/lib/setops/sondes
state: directory
owner: root
group: root
mode: "0755"
- name: Deposer la sonde « depot »
ansible.builtin.template:
src: sonde-depot.sh.j2
dest: /usr/local/lib/setops/sondes/depot.sh
owner: root
group: root
mode: "0750"

View file

@ -0,0 +1,50 @@
#!/bin/bash
# GENERE par Set-OPS (role serveur_backup). Ne pas editer a la main.
#
# SONDE « depot » — le depot accepte-t-il encore qu'on y depose ?
#
# Contrat : docs/supervision-conception.md (API des greffons Nagios) — une ligne, 0/1/2.
# Mise en defaut PAR PARAMETRE : `serveur_backup_sonde_racine` (chemin non inscriptible).
#
# CE QU'ELLE NE MESURE PAS, ET C'EST LA DOCTRINE LA PLUS STRICTE DE CE DEPOT.
#
# Elle ne juge RIEN du contenu. Les instantanes sont chiffres cote client : ce serveur
# heberge des octets qu'il ne peut pas ouvrir, donc qu'il ne peut pas juger. « Cet
# instantane est-il recent, complet, restaurable ? » se demande a qui detient la cle, et
# `client_backup` le fait deja depuis chaque noeud. LA VERIFICATION SUIT LA CLE.
#
# CE QU'ELLE MESURE, ET QUE PERSONNE D'AUTRE NE VOIT A TEMPS : que l'endroit accepte une
# ECRITURE. Un systeme de fichiers remonte en lecture seule apres une erreur disque, un
# volume plein, des droits qui ont derive — dans les trois cas le depot refuse tout, et
# les clients ne l'apprennent qu'a leur prochaine execution, chacun de son cote.
#
# NEUF ROUGES EPARS NE DESIGNENT PAS UNE CAUSE COMMUNE. C'est la seule raison d'etre de
# cette sonde : dire UNE fois, ici, ce que neuf machines diront plus tard sans savoir
# qu'elles disent la meme chose.
#
# ON ECRIT VRAIMENT, ET SOUS L'IDENTITE QUI DEPOSE. Un `test -w` ment sur un montage en
# lecture seule et sur un quota atteint. Seule une ecriture reelle, faite par le compte
# que les clients empruntent, prouve que le depot prend encore.
set -uo pipefail
RACINE={{ serveur_backup_sonde_racine }}
UTIL={{ serveur_backup_utilisateur }}
[[ -d "${RACINE}" ]] || { echo "Le depot ${RACINE} n'existe pas."; exit 2; }
temoin="${RACINE}/.setops-temoin-sonde"
if ! sudo -u "${UTIL}" install -m 0600 /dev/null "${temoin}" 2>/dev/null; then
echo "Le depot ${RACINE} refuse l'ecriture pour ${UTIL} — plein, en lecture seule, ou droits derives."
exit 2
fi
sudo -u "${UTIL}" rm -f "${temoin}" 2>/dev/null
# CE CHIFFRE EST UN CONSTAT, PAS UN SEUIL. L'occupation appartient a Prometheus ; on la
# publie ici en donnee de performance pour que le graphe existe, sans jamais en faire un
# verdict. Icinga repond a une seule question — est-ce casse ?
lisible=$(df -h --output=avail "${RACINE}" 2>/dev/null | tail -1 | tr -d ' ')
octets=$(df -B1 --output=avail "${RACINE}" 2>/dev/null | tail -1 | tr -d ' ')
depots=$(sudo -u "${UTIL}" find "${RACINE}" -maxdepth 1 -mindepth 1 -type d 2>/dev/null | wc -l)
echo "Le depot accepte l'ecriture — ${depots} depot(s) heberge(s), ${lisible} libres.|libre=${octets:-0}B depots=${depots}"
exit 0

View file

@ -10,7 +10,15 @@
* fichiers qui definissent le meme `object Host` font rejeter TOUTE la configuration.
* Ce fichier n'attache que des services.
*
* Les resultats sont POUSSES par `backup-01`, qui est le seul a pouvoir lire ses depots.
* QUI POUSSE : CHAQUE NOEUD, POUR SON PROPRE DEPOT. Cette ligne a dit le contraire
* jusqu'au 2026-09-14 — elle nommait `backup-01`, « le seul a pouvoir lire ses depots ».
* Or `backup-01` a ete RETIRE le 2026-09-02, et c'est tout le sujet : le site heberge des
* octets chiffres cote client, il ne peut pas les ouvrir, donc pas les juger.
*
* LA VERIFICATION SUIT LA CLE. Chaque noeud verifie SON depot distant et rapporte
* lui-meme, par `client_backup` et son minuteur. Le code de ce fichier avait suivi la
* decision — il attache les services aux vrais noeuds — mais l'en-tete decrivait encore
* un acteur disparu, a celui-la meme qui l'ouvrirait pour comprendre qui pousse.
* Le `ttl` porte dans chaque envoi fait la fraicheur : sans nouvelle, Icinga bascule tout
* seul en « expire ». C'est le SILENCE qui doit alerter, pas seulement l'echec — le
* silence est precisement ce qui n'a alerte personne.