sauvegarde : la verification suit la cle, pas le depot
Some checks are pending
verifier / verifier (push) Waiting to run
Some checks are pending
verifier / verifier (push) Waiting to run
serveur_backup verifiait pour tout le monde — juste tant que le depot vivait dans l ecosysteme. Depuis qu ils deposent chez leur hebergeur, le site heberge des octets chiffres COTE CLIENT : il ne peut ni les lire ni dire s ils valent quelque chose. La verification revient donc au seul qui detient la cle, le noeud. client_backup verifie SON depot distant — pas le fait d avoir lance sa sauvegarde. Une unite verte sur un depot vide est ce qui a menti un mois. serveur_icinga n exige plus un hote serveur_backup et se branche sur deux modeles : depot local (services sur son hote, nommes sauvegarde: <noeud>) ou pas de depot (services sur chaque noeud, nommes sauvegarde). Le 404 qui n etait pas une absence : les noeuds recevaient No objects found alors que icinga2 object list montrait le service charge. Le filtre du compte d API ne portait que la premiere forme de nom — c est la PERMISSION qui refusait, avec les mots d une absence. Aussi : ingress 5665 depuis client_backup, le pair ne nommait que serveur_backup. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
This commit is contained in:
parent
cccb4e5b43
commit
6653f49f2e
12 changed files with 408 additions and 6 deletions
78
CHANGELOG.md
78
CHANGELOG.md
|
|
@ -1,5 +1,83 @@
|
|||
# CHANGELOG — Set-OPS
|
||||
|
||||
## 2026-09-02 (5) — La verification suit la cle : chaque noeud constate SON depot
|
||||
|
||||
**56 preuves. `make valider` : 0 echec sur 13 hotes**, test de restitution compris.
|
||||
|
||||
`backup-01` est retire du plan de Chezlepro et sa VM detruite. Elle ne gardait plus rien :
|
||||
son `/srv/restic` ne contenait que les fichiers de demarrage du compte `restic`, aucun
|
||||
depot, aucun instantane — et sa verification rapportait consciencieusement « tout va
|
||||
bien ». **Une supervision creuse est pire qu'aucune : elle est verte.**
|
||||
|
||||
### Pourquoi la verification a change de main
|
||||
|
||||
`serveur_backup` verifiait pour tout le monde, et c'etait juste : un noeud sait qu'il a
|
||||
LANCE sa sauvegarde, il ne sait pas qu'elle a ABOUTI — le depot etait le seul a voir ce
|
||||
qui arrivait vraiment.
|
||||
|
||||
Depuis que les ecosystemes deposent chez leur HEBERGEUR, ce n'est plus vrai. Le site
|
||||
heberge des octets chiffres COTE CLIENT, avec un mot de passe qui ne quitte pas la voute
|
||||
du locataire : il ne peut ni les lire, ni les ouvrir, ni dire s'ils valent quelque chose.
|
||||
C'est la propriete qui rend la mutualisation acceptable, et elle deplace la verification
|
||||
chez le seul qui detient la cle — **le noeud lui-meme**.
|
||||
|
||||
Il verifie donc SON depot distant, pas le fait d'avoir lance sa sauvegarde. La nuance est
|
||||
tout : une unite verte sur un depot vide est exactement ce qui a menti pendant un mois
|
||||
(2026-07-03 -> 2026-08-11).
|
||||
|
||||
### Deux modeles, et le role sait desormais dans lequel il est
|
||||
|
||||
`serveur_icinga` exigeait un hote `serveur_backup` dans l'ecosysteme et attachait les
|
||||
services `sauvegarde: <noeud>` a cet hote. Sans depot local, il refusait de se deployer.
|
||||
|
||||
Il se branche maintenant :
|
||||
|
||||
- **depot local** — il rapporte pour tous, les services vivent sur SON hote, et leur nom
|
||||
dit de quel noeud on parle : `sauvegarde: idm-01` ;
|
||||
- **pas de depot** — chaque noeud rapporte le sien, le service vit SUR LUI, et s'appelle
|
||||
simplement `sauvegarde`. Repeter le nom donnerait « idm-01 / sauvegarde: idm-01 ». Et
|
||||
c'est plus juste : la sauvegarde d'`idm-01` est un attribut d'`idm-01`.
|
||||
|
||||
Ce qui reste exige, c'est le secret d'API — sans lui, personne ne peut rien rapporter.
|
||||
|
||||
### Le 404 qui n'etait pas une absence
|
||||
|
||||
Les noeuds recevaient `{"error":404,"status":"No objects found."}` — le message d'un objet
|
||||
ABSENT. `icinga2 object list` montrait pourtant `idm-01!sauvegarde` charge et vivant.
|
||||
|
||||
Le filtre du compte d'API portait `match("sauvegarde: *", service.name)` : la premiere
|
||||
forme de nom seulement. C'etait la PERMISSION qui refusait, et elle le disait avec les
|
||||
mots d'une absence. Le filtre accepte desormais les deux formes, sans s'elargir au-dela :
|
||||
ce compte ne peut poser un resultat que sur un service de sauvegarde.
|
||||
|
||||
`serveur_icinga` declare aussi `ingress 5665` depuis `client_backup` — le pair ne
|
||||
nommait que `serveur_backup`, qui n'existe plus chez ce locataire.
|
||||
|
||||
### Ce que la supervision dit maintenant, chez le locataire
|
||||
|
||||
collab-01 OK : instantane il y a 20 h, 108 fichier(s)
|
||||
data-sql-01 OK : instantane il y a 20 h, 1 fichier(s)
|
||||
edge-mta-01 OK : instantane il y a 20 h, 138 fichier(s)
|
||||
forge-01 OK : instantane il y a 20 h, 29 fichier(s)
|
||||
idm-01 OK : instantane il y a 20 h, 1 fichier(s)
|
||||
infra-mail-01 OK : instantane il y a 20 h, 7 fichier(s)
|
||||
infra-pki-01 OK : instantane il y a 20 h, 12 fichier(s)
|
||||
web-dorsal-01 N'EMPORTE RIEN : instantane sans aucun fichier — a confirmer
|
||||
web-frontal-01 N'EMPORTE RIEN : instantane sans aucun fichier — a confirmer
|
||||
|
||||
Les deux avertissements sont honnetes : ces repertoires sont reellement vides, aucune
|
||||
webapp n'est deployee. La machine ne peut pas distinguer « les donnees ont disparu » de
|
||||
« il n'y en a pas encore » — c'est a un humain de trancher, mais il doit le VOIR.
|
||||
|
||||
### Constat non corrige
|
||||
|
||||
Retirer une VM du plan ne la detruit pas : `make raser` ne connait que les hotes DU plan,
|
||||
donc plus celle-ci. Il a fallu la detruire a la main sur l'hyperviseur. Un hote retire du
|
||||
plan devient un orphelin qu'aucune cible ne ramasse.
|
||||
|
||||
Et Prometheus a continue de scruter son exportateur jusqu'a ce que `serveur_prometheus`
|
||||
soit rejoue — `make valider` l'a attrape, ce qui est exactement son role.
|
||||
|
||||
## 2026-09-02 (4) — Le site a un temoin ; et une panne dormait depuis des semaines dans un mot
|
||||
|
||||
**56 preuves.** L'hebergeur a desormais sa propre supervision : `site-mon-01` (VLAN 36,
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@
|
|||
| `serveur_grafana` | ingress | 3000 | tcp | edge | clair | Interface web servie via l'edge (TLS terminé à l'edge). |
|
||||
| `serveur_grafana` | egress | 443 | tcp | edge | tls-requis | Authentification OIDC auprès de Keycloak (via son FQDN publié à l'edge). |
|
||||
| `serveur_icinga` | ingress | 5665 | tcp | localhost | clair | API Icinga 2 consommée en local par Icinga Web 2 co-localisé. |
|
||||
| `serveur_icinga` | ingress | 5665 | tcp | client_backup | tls-requis | Rapport passif de chaque detenteur d'etat sur SON depot distant : le depot du site heberge du chiffre et ne peut pas le juger. |
|
||||
| `serveur_icinga` | ingress | 5665 | tcp | serveur_backup | tls-requis | Le depot de sauvegarde depose ses resultats passifs (portee : process-check-result sur « sauvegarde: * »). |
|
||||
| `serveur_icinga` | egress | 5432 | tcp | serveur_postgresql | tls-requis | Base relationnelle du moteur Icinga (verify-full). |
|
||||
| `serveur_icingaweb2` | ingress | 8080 | tcp | edge | clair | Interface web servie via l'edge (TLS terminé à l'edge ; SSO possible via oauth2-proxy). |
|
||||
|
|
@ -113,4 +114,4 @@
|
|||
- **starttls** : 6 flux
|
||||
- **tls** : 9 flux
|
||||
- **tls-cible** : 2 flux
|
||||
- **tls-requis** : 33 flux
|
||||
- **tls-requis** : 34 flux
|
||||
|
|
|
|||
|
|
@ -89,3 +89,34 @@ client_backup_jobs: >-
|
|||
|
||||
# La liste EN CLAIR des memes groupes vit dans `vars/main.yml` — sans Jinja, pour que la
|
||||
# supervision et P36 puissent la lire par `include_vars` sans rendre ce catalogue.
|
||||
|
||||
# --- LE NOEUD VERIFIE SON PROPRE DEPOT ----------------------------------------
|
||||
#
|
||||
# POURQUOI ICI, ALORS QUE `serveur_backup` LE FAISAIT DEJA.
|
||||
#
|
||||
# Le depot etait le seul a voir ce qui etait REELLEMENT arrive : un noeud sait qu'il a
|
||||
# lance sa sauvegarde, il ne sait pas qu'elle a abouti. C'etait juste tant que le depot
|
||||
# appartenait au meme ecosysteme.
|
||||
#
|
||||
# Depuis que les ecosystemes deposent chez leur HEBERGEUR, ca ne l'est plus : le site
|
||||
# heberge des octets chiffres par restic COTE CLIENT, avec un mot de passe qui ne quitte
|
||||
# pas la voute du locataire. Il ne peut ni les lire, ni les ouvrir, ni dire s'ils valent
|
||||
# quelque chose. C'est la propriete qui rend la mutualisation acceptable — et elle
|
||||
# deplace la verification chez le seul qui detient la cle : le noeud lui-meme.
|
||||
#
|
||||
# Il verifie donc SON depot distant, pas le fait d'avoir lance sa sauvegarde. La nuance
|
||||
# est tout : une unite systemd verte sur un depot vide est exactement ce qui a menti
|
||||
# pendant un mois (2026-07-03 -> 2026-08-11).
|
||||
client_backup_icinga_hote: "{{ (groups['serveur_icinga'] | default([]) | first) | default('') }}"
|
||||
client_backup_icinga_utilisateur: "setops-depot"
|
||||
client_backup_icinga_motdepasse: "{{ vault_icinga_api_depot | default('') }}"
|
||||
client_backup_ca_verification: "/etc/setops/icinga-ca.crt"
|
||||
client_backup_icinga_ca_source: "/var/lib/icinga2/ca/ca.crt"
|
||||
|
||||
# `ttl` du resultat passif : au-dela, Icinga perime le service de lui-meme — c'est ce qui
|
||||
# fait que le SILENCE alerte. Memes valeurs que cote depot : une verification toutes les
|
||||
# 4 h, une execution peut etre manquee sans fausse alerte, deux ne le peuvent pas.
|
||||
client_backup_verification_horaire: "*-*-* 01/4:00:00"
|
||||
client_backup_ttl_icinga: 21600
|
||||
client_backup_age_warn_h: 26
|
||||
client_backup_age_crit_h: 50
|
||||
|
|
|
|||
|
|
@ -150,3 +150,29 @@
|
|||
- /etc/systemd/system/setops-sauvegarde.service
|
||||
- /etc/systemd/system/setops-sauvegarde.timer
|
||||
notify: Recharger systemd
|
||||
|
||||
# --- LE NOEUD VERIFIE SON PROPRE DEPOT ----------------------------------------
|
||||
#
|
||||
# Conditionne a l'existence d'un `serveur_icinga` : sans destinataire, le rapport
|
||||
# n'irait nulle part, et un timer qui echoue chaque nuit apprend a ignorer le rouge.
|
||||
#
|
||||
# Et conditionne a `client_backup_jobs` : un noeud qui n'emporte rien n'a pas de depot
|
||||
# a verifier — c'est la meme liste qui decide des deux, pour qu'elles ne divergent pas.
|
||||
- name: Vérifier mon dépôt distant, et le dire à Icinga
|
||||
ansible.builtin.include_tasks: verifier.yml
|
||||
when:
|
||||
- client_backup_jobs | length > 0
|
||||
- client_backup_icinga_hote | length > 0
|
||||
|
||||
# DIRE CE QU'ON NE FAIT PAS. Sans supervision dans l'ecosysteme, la sauvegarde tourne et
|
||||
# personne ne constate jamais qu'elle a abouti. Ce n'est pas une erreur — c'est une dette
|
||||
# qui doit se voir au deploiement plutot que le jour de la restauration.
|
||||
- name: Dire que personne ne constatera cette sauvegarde
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
Aucun `serveur_icinga` dans cet écosystème : la sauvegarde de {{ inventory_hostname }}
|
||||
tournera sans que personne ne constate qu'elle aboutit. Une unité verte sur un dépôt
|
||||
vide est exactement ce qui a menti pendant un mois.
|
||||
when:
|
||||
- client_backup_jobs | length > 0
|
||||
- client_backup_icinga_hote | length == 0
|
||||
|
|
|
|||
78
roles/client_backup/tasks/verifier.yml
Normal file
78
roles/client_backup/tasks/verifier.yml
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
---
|
||||
# CE NOEUD VERIFIE SON PROPRE DEPOT DISTANT.
|
||||
#
|
||||
# Inclus seulement quand un `serveur_icinga` existe dans l'ecosysteme : sans destinataire,
|
||||
# le rapport n'irait nulle part, et poser un timer qui echoue chaque nuit apprendrait aux
|
||||
# gens a ignorer une unite rouge.
|
||||
- name: Exiger de quoi rapporter à Icinga
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- client_backup_icinga_motdepasse | length > 0
|
||||
fail_msg: >-
|
||||
`vault_icinga_api_depot` requis : un hôte `serveur_icinga` existe, mais aucun
|
||||
secret d'API. Le nœud verrait l'état de son dépôt sans pouvoir le dire.
|
||||
|
||||
- name: Déposer le mot de passe d'API Icinga
|
||||
ansible.builtin.copy:
|
||||
content: "{{ client_backup_icinga_motdepasse }}\n"
|
||||
dest: /etc/setops/icinga-api.pass
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0600"
|
||||
no_log: true
|
||||
|
||||
# L'AC d'ICINGA, PAS CELLE DE step-ca : Icinga refuse de servir un certificat qu'il n'a
|
||||
# pas émis (il renouvelle tout ce qui expire sous 30 jours, nos certificats vivent 24 h).
|
||||
# Domaine de confiance fermé, pair authentifié malgré tout — ce qui était le vrai enjeu.
|
||||
- name: L'AC d'Icinga est-elle déjà disponible ?
|
||||
ansible.builtin.stat:
|
||||
path: "{{ client_backup_icinga_ca_source }}"
|
||||
delegate_to: "{{ client_backup_icinga_hote }}"
|
||||
register: client_backup_ca_presente
|
||||
|
||||
- name: Récupérer l'AC d'Icinga depuis l'hôte de supervision
|
||||
when: client_backup_ca_presente.stat.exists
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ client_backup_icinga_ca_source }}"
|
||||
delegate_to: "{{ client_backup_icinga_hote }}"
|
||||
register: client_backup_ca_icinga
|
||||
|
||||
- name: Déposer l'AC d'Icinga pour la vérification du pair
|
||||
when: client_backup_ca_presente.stat.exists
|
||||
ansible.builtin.copy:
|
||||
content: "{{ client_backup_ca_icinga.content | b64decode }}"
|
||||
dest: "{{ client_backup_ca_verification }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
- name: Installer curl pour le rapport passif
|
||||
ansible.builtin.apt:
|
||||
name: curl
|
||||
state: present
|
||||
|
||||
- name: Déployer le contrôle de mon dépôt
|
||||
ansible.builtin.template:
|
||||
src: verifier-mon-depot.sh.j2
|
||||
dest: /usr/local/sbin/setops-verifier-mon-depot.sh
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0700"
|
||||
|
||||
- name: Déployer l'unité et le timer de vérification
|
||||
ansible.builtin.template:
|
||||
src: "{{ item.s }}"
|
||||
dest: "/etc/systemd/system/{{ item.d }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
loop:
|
||||
- { s: setops-verification-depot.service.j2, d: setops-verification-depot.service }
|
||||
- { s: setops-verification-depot.timer.j2, d: setops-verification-depot.timer }
|
||||
|
||||
- name: Activer le timer de vérification
|
||||
ansible.builtin.systemd_service:
|
||||
name: setops-verification-depot.timer
|
||||
enabled: true
|
||||
state: started
|
||||
daemon_reload: true
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# Géré par Set-OPS (rôle client_backup). Ne pas éditer à la main.
|
||||
[Unit]
|
||||
Description=Vérification de MON dépôt distant et rapport passif vers Icinga
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/local/sbin/setops-verifier-mon-depot.sh
|
||||
Nice=10
|
||||
IOSchedulingClass=idle
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# Géré par Set-OPS (rôle client_backup). Ne pas éditer à la main.
|
||||
#
|
||||
# DÉCALÉ D'UNE HEURE APRÈS LA SAUVEGARDE (02:30) : vérifier avant que l'instantané du
|
||||
# jour ne soit déposé rendrait un « EN RETARD » chaque matin, sur une sauvegarde qui se
|
||||
# porte bien. Une alerte qui crie tous les jours cesse d'être lue.
|
||||
[Unit]
|
||||
Description=Planification de la vérification du dépôt distant
|
||||
|
||||
[Timer]
|
||||
OnCalendar={{ client_backup_verification_horaire }}
|
||||
RandomizedDelaySec=300
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
81
roles/client_backup/templates/verifier-mon-depot.sh.j2
Normal file
81
roles/client_backup/templates/verifier-mon-depot.sh.j2
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#!/bin/bash
|
||||
# GENERE par Set-OPS (role client_backup). Ne pas editer a la main.
|
||||
#
|
||||
# CE NOEUD VERIFIE SON PROPRE DEPOT, ET PAS SON PROPRE TIMER.
|
||||
#
|
||||
# Un noeud sait qu'il a LANCE sa sauvegarde ; il ne sait pas qu'elle a ABOUTI. La
|
||||
# difference n'est pas theorique : onze machines ont lance chaque nuit un timer vert
|
||||
# pendant un mois sur des depots qui n'emportaient rien (2026-07-03 -> 2026-08-11).
|
||||
#
|
||||
# Ce controle interroge donc le DEPOT DISTANT — l'instantane existe-t-il, est-il RECENT,
|
||||
# n'est-il pas VIDE. Le noeud est desormais le seul a pouvoir le faire : le depot vit
|
||||
# chez l'hebergeur, qui heberge des octets chiffres cote client et ne detient aucune de
|
||||
# nos cles.
|
||||
set -uo pipefail
|
||||
|
||||
export RESTIC_PASSWORD_FILE="/etc/setops/restic.pass"
|
||||
export RESTIC_REPOSITORY="{{ client_backup_repo }}"
|
||||
API="https://{{ client_backup_icinga_hote }}.{{ domaine_interne }}:5665"
|
||||
TTL={{ client_backup_ttl_icinga }}
|
||||
AGE_WARN={{ client_backup_age_warn_h }}
|
||||
AGE_CRIT={{ client_backup_age_crit_h }}
|
||||
MOI="{{ inventory_hostname }}"
|
||||
MOTDEPASSE="$(cat /etc/setops/icinga-api.pass)"
|
||||
|
||||
rapporter() { # $1=code $2=texte
|
||||
local charge reponse
|
||||
charge=$(python3 -c 'import json,sys; print(json.dumps({
|
||||
"type": "Service", "service": sys.argv[1], "exit_status": int(sys.argv[2]),
|
||||
"plugin_output": sys.argv[3], "ttl": int(sys.argv[4])}))' \
|
||||
"${MOI}!sauvegarde" "$1" "$2" "${TTL}")
|
||||
reponse=$(curl -sS --cacert "{{ client_backup_ca_verification }}" --max-time 20 \
|
||||
-u "{{ client_backup_icinga_utilisateur }}:${MOTDEPASSE}" \
|
||||
-H 'Accept: application/json' -H 'Content-Type: application/json' \
|
||||
-X POST "${API}/v1/actions/process-check-result" -d "${charge}" 2>&1)
|
||||
if ! printf '%s' "${reponse}" | grep -q '"code": *200'; then
|
||||
echo "ECHEC du rapport Icinga : ${reponse}" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
maintenant=$(date +%s)
|
||||
|
||||
json=$(restic snapshots --latest 1 --json 2>/dev/null)
|
||||
horodatage=$(printf '%s' "${json}" | python3 -c \
|
||||
'import sys,json;d=json.load(sys.stdin);print(d[0]["time"] if d else "")' 2>/dev/null)
|
||||
if [[ -z "${horodatage}" ]]; then
|
||||
# ON NE DISTINGUE PAS ICI « depot vide » de « depot injoignable », et c'est voulu :
|
||||
# dans les deux cas il n'y a PAS de sauvegarde exploitable, et c'est ce qui compte.
|
||||
# Le detail est dans le journal du noeud, pas dans un verdict qui se voudrait plus
|
||||
# precis qu'il ne peut l'etre.
|
||||
rapporter 2 "AUCUN INSTANTANE lisible dans ${RESTIC_REPOSITORY} — depot vide ou injoignable."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
secondes=$(date -d "$(printf '%s' "${horodatage}" | sed -E 's/\.[0-9]+//')" +%s 2>/dev/null || echo 0)
|
||||
age_h=$(( (maintenant - secondes) / 3600 ))
|
||||
|
||||
taille=$(restic stats latest --mode raw-data --json 2>/dev/null | python3 -c \
|
||||
'import sys,json;print(json.load(sys.stdin).get("total_size",0))' 2>/dev/null || echo 0)
|
||||
|
||||
fichiers=$(restic ls latest --json 2>/dev/null | python3 -c \
|
||||
'import sys,json
|
||||
n=0
|
||||
for l in sys.stdin:
|
||||
try: d=json.loads(l)
|
||||
except ValueError: continue
|
||||
if d.get("struct_type")=="node" and d.get("type")=="file": n+=1
|
||||
print(n)' 2>/dev/null || echo 0)
|
||||
|
||||
if (( fichiers == 0 )); then
|
||||
# AVERTISSEMENT et non CRITIQUE : la machine ne peut pas distinguer « les donnees ont
|
||||
# disparu » de « il n'y en a pas encore » (un /srv/web sans webapp). C'est a un humain
|
||||
# de trancher — mais il doit le VOIR.
|
||||
rapporter 1 "N'EMPORTE RIEN : instantane sans aucun fichier (${taille} octets). Legitime si ce noeud n'a pas encore de donnees — a confirmer."
|
||||
elif (( age_h >= AGE_CRIT )); then
|
||||
rapporter 2 "PERIME : dernier instantane il y a ${age_h} h (seuil ${AGE_CRIT} h), ${fichiers} fichier(s)."
|
||||
elif (( age_h >= AGE_WARN )); then
|
||||
rapporter 1 "EN RETARD : dernier instantane il y a ${age_h} h (seuil ${AGE_WARN} h), ${fichiers} fichier(s)."
|
||||
else
|
||||
rapporter 0 "OK : instantane il y a ${age_h} h, ${fichiers} fichier(s), ${taille} octets."
|
||||
fi
|
||||
|
|
@ -8,6 +8,21 @@ flux:
|
|||
pair: localhost
|
||||
chiffrement: clair
|
||||
raison: "API Icinga 2 consommée en local par Icinga Web 2 co-localisé."
|
||||
# LES NOEUDS RAPPORTENT EUX-MEMES DEPUIS LE 2026-09-02.
|
||||
#
|
||||
# Tant que le depot vivait dans l'ecosysteme, il rapportait pour tout le monde et
|
||||
# `serveur_backup` suffisait comme pair. Depuis que les ecosystemes deposent chez leur
|
||||
# HEBERGEUR — qui heberge du chiffre et ne peut rien juger — c'est chaque detenteur
|
||||
# d'etat qui verifie SON depot et le rapporte. Le pair, c'est donc `client_backup`.
|
||||
#
|
||||
# Les deux coexistent : un ecosysteme qui garde son propre depot continue de le laisser
|
||||
# rapporter. `serveur_backup` reste declare juste en dessous.
|
||||
- sens: ingress
|
||||
port: 5665
|
||||
protocole: tcp
|
||||
pair: client_backup
|
||||
chiffrement: tls-requis
|
||||
raison: "Rapport passif de chaque detenteur d'etat sur SON depot distant : le depot du site heberge du chiffre et ne peut pas le juger."
|
||||
- sens: ingress
|
||||
port: 5665
|
||||
protocole: tcp
|
||||
|
|
|
|||
|
|
@ -135,14 +135,24 @@
|
|||
ansible.builtin.set_fact:
|
||||
client_backup_groupes_etat: "{{ _catalogue_sauvegarde.client_backup_groupes_etat }}"
|
||||
|
||||
- name: Exiger le secret d'API du depot (Vault)
|
||||
# UN ECOSYSTEME PEUT N'AVOIR AUCUN DEPOT A LUI, ET C'EST LE CAS NORMAL DEPUIS QU'ILS
|
||||
# DEPOSENT CHEZ LEUR HEBERGEUR (2026-09-02).
|
||||
#
|
||||
# Cette assertion exigeait un hote `serveur_backup` dans l'ecosysteme. C'etait juste tant
|
||||
# que le depot y vivait : il etait le seul a voir ce qui arrivait vraiment, et il
|
||||
# rapportait pour tout le monde.
|
||||
#
|
||||
# Depuis la bascule vers le depot du SITE, l'ecosysteme n'en a plus. Le site, lui, ne
|
||||
# peut pas ouvrir ces depots — restic chiffre chez le client. C'est donc chaque NOEUD qui
|
||||
# verifie le sien (`client_backup/tasks/verifier.yml`) et rapporte ici. Ce qui reste
|
||||
# exige, c'est le secret d'API : sans lui, personne ne peut rien rapporter.
|
||||
- name: Exiger le secret d'API pour les rapports passifs (Vault)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- serveur_icinga_api_motdepasse | length > 0
|
||||
- serveur_icinga_hote_sauvegarde | length > 0
|
||||
fail_msg: >-
|
||||
vault_icinga_api_depot requis, et un hote du groupe `serveur_backup` doit exister :
|
||||
sans l'un ou l'autre, les sauvegardes ne seraient surveillees par personne.
|
||||
vault_icinga_api_depot requis : sans lui, ni le depot ni les noeuds ne peuvent
|
||||
rapporter l'etat de leurs sauvegardes, et personne ne les surveille.
|
||||
|
||||
- name: Deployer le compte d'API du depot de sauvegarde
|
||||
ansible.builtin.template:
|
||||
|
|
|
|||
|
|
@ -10,7 +10,26 @@ object ApiUser "{{ serveur_icinga_api_utilisateur }}" {
|
|||
permissions = [
|
||||
{
|
||||
permission = "actions/process-check-result"
|
||||
filter = {{ '{{' }} match("sauvegarde: *", service.name) {{ '}}' }}
|
||||
{#
|
||||
DEUX FORMES DE NOM, PARCE QU'IL Y A DEUX MODELES (2026-09-02).
|
||||
|
||||
« sauvegarde: <noeud> » — l'ecosysteme a son propre depot, qui rapporte pour
|
||||
tout le monde : les services vivent sur SON hote, le nom doit donc dire de quel
|
||||
noeud on parle.
|
||||
|
||||
« sauvegarde » — l'ecosysteme depose chez son hebergeur, qui heberge du chiffre
|
||||
et ne peut rien juger. Chaque noeud verifie le sien : le service vit SUR LUI, et
|
||||
repeter son nom donnerait « idm-01 / sauvegarde: idm-01 ».
|
||||
|
||||
Le filtre n'acceptait que la premiere forme. Les noeuds recevaient
|
||||
`{"error":404,"status":"No objects found."}` — le message d'un objet ABSENT,
|
||||
alors qu'il existait et que c'etait la PERMISSION qui refusait. Une heure a
|
||||
chercher un objet qui etait la.
|
||||
|
||||
La portee reste etroite : ce compte ne peut poser un resultat que sur un service
|
||||
de sauvegarde, jamais ailleurs.
|
||||
#}
|
||||
filter = {{ '{{' }} match("sauvegarde: *", service.name) || service.name == "sauvegarde" {{ '}}' }}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@ object CheckCommand "setops-passif" {
|
|||
command = [ "/bin/true" ]
|
||||
}
|
||||
|
||||
{% if serveur_icinga_hote_sauvegarde %}
|
||||
{#
|
||||
L'ECOSYSTEME A SON PROPRE DEPOT : c'est LUI qui rapporte, parce qu'il est le seul a
|
||||
voir ce qui est reellement arrive. Les services s'attachent donc a son hote.
|
||||
#}
|
||||
object Host "{{ serveur_icinga_hote_sauvegarde }}" {
|
||||
check_command = "hostalive"
|
||||
address = "{{ serveur_icinga_hote_sauvegarde }}.{{ domaine_interne }}"
|
||||
|
|
@ -34,3 +39,35 @@ object Service "sauvegarde: {{ noeud }}" {
|
|||
vars.setops_source = "{{ noeud }}"
|
||||
}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{#
|
||||
AUCUN DEPOT DANS CET ECOSYSTEME — le cas normal depuis qu'ils deposent chez leur
|
||||
HEBERGEUR (2026-09-02).
|
||||
|
||||
Le site heberge des octets chiffres COTE CLIENT : il ne peut ni les lire, ni dire
|
||||
s'ils valent quelque chose. La verification revient donc au seul qui detient la cle,
|
||||
le NOEUD lui-meme — et le service s'attache a lui, ce qui est d'ailleurs plus juste :
|
||||
la sauvegarde de `idm-01` est un attribut de `idm-01`, pas d'une machine tierce.
|
||||
|
||||
Le service n'est plus nomme « sauvegarde: <noeud> » mais simplement « sauvegarde » :
|
||||
le nom du noeud est desormais porte par l'hote. Repeter le nom dans le service
|
||||
donnerait « idm-01 / sauvegarde: idm-01 ».
|
||||
#}
|
||||
{% for noeud in serveur_icinga_sauvegarde_attendue | sort %}
|
||||
object Host "{{ noeud }}" {
|
||||
check_command = "hostalive"
|
||||
address = "{{ noeud }}.{{ domaine_interne }}"
|
||||
vars.role = "detenteur d etat"
|
||||
}
|
||||
|
||||
object Service "sauvegarde" {
|
||||
host_name = "{{ noeud }}"
|
||||
check_command = "setops-passif"
|
||||
enable_active_checks = false
|
||||
enable_passive_checks = true
|
||||
volatile = false
|
||||
max_check_attempts = 1
|
||||
vars.setops_source = "{{ noeud }}"
|
||||
}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
|
|
|||
Loading…
Reference in a new issue