Set-OPS-Public/roles/client_backup/defaults/main.yml
Daniel Allaire f04e790d2c icinga : l'AC et l'historique survivent a la reconstruction ; temoins par cle
L'historique d'Icinga DB etait exclu de la restauration (2026-09-30) : son
environnement derive de l'AC d'Icinga, recreee a chaque reconstruction. L'AC est
desormais un jeu de sauvegarde, remis avant api setup ; la base d'Icinga est
restauree, et son schema n'est importe que sur une base vierge.

Les temoins comparaient PostgreSQL par nombre de lignes et n'ont pas vu 412
lignes d'historique remplacees par 380 neuves. Ils comparent desormais par cle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 15:18:40 -04:00

172 lines
9.2 KiB
YAML

---
# Sauvegarde applicative (logique) d'un nœud vers la cible restic hors-nœud.
# Chaque nœud déclare ses "jobs" ; restic chiffre côté client + applique la rétention.
client_backup_paquets:
- restic
# `setops-restaurer` remet les repertoires par `rsync --delete` (absent du gabarit).
- rsync
# Cible (nœud serveur_backup) + transport SSH.
client_backup_cible: "backup-01.{{ domaine_interne }}"
client_backup_utilisateur_distant: "restic"
client_backup_ssh_key: "/etc/setops/backup_ed25519"
# Dépôt restic (un sous-dossier par nœud, relatif au home de l'utilisateur distant).
client_backup_repo: "sftp:{{ client_backup_utilisateur_distant }}@{{ client_backup_cible }}:{{ inventory_hostname }}"
client_backup_password: "{{ vault_restic_password | default('') }}"
client_backup_ssh_privkey: "{{ vault_backup_ssh_privkey | default('') }}"
# Dossier de préparation des dumps (pg_dump, slapcat, forgejo dump...).
client_backup_staging: "/var/backups/setops"
# Rétention (restic forget).
client_backup_retention: "--keep-daily 7 --keep-weekly 4 --keep-monthly 6"
# L'instantane d'avant rasage (`make sauvegarder-maintenant`) porte cette etiquette, et la
# retention la garde EN PLUS de la regle ci-dessus : jusqu'a la reconstruction suivante
# (decision de l'exploitant, 2026-10-07). Voir `templates/sauvegarder.sh.j2`.
client_backup_etiquette_avant_raser: "avant-raser"
# Planification (timer systemd).
client_backup_horaire: "*-*-* 02:30:00"
# Jobs déclaratifs. Chaque job :
# nom : identifiant
# commande : (optionnel) dump écrivant dans {{ staging }}/{{ nom }}
# chemins : liste de chemins à inclure dans le snapshot restic
#
# CATALOGUE PAR GROUPE — c'est le rôle qui POSSEDE la donnée qui dit comment la sortir.
# On sauvegarde l'ETAT NON REGENERABLE, pas ce que le code reconstruit : ni les zones
# PowerDNS ni les tableaux de bord Grafana ne figurent ici, ils se redéploient.
#
# Les chemins reprennent les defauts du role proprietaire, mais NE PEUVENT PAS y faire
# reference : `make deployer` deroule un play par groupe, et les defaults de
# `serveur_forgejo` ne sont pas charges pendant le play de `client_backup`. D'ou la forme
# `var | default(litteral)` — la variable gagne si l'inventaire la definit, sinon le
# defaut du role proprietaire, recopie ici et a garder aligne avec lui.
client_backup_catalogue:
serveur_step_ca:
nom: step_ca
chemins:
- "{{ serveur_step_ca_steppath | default('/etc/step-ca') }}"
serveur_openldap:
nom: openldap
# slapcat lit la base a plat : pas d'authentification, coherent meme slapd arrete.
commande: >-
slapcat -b '{{ serveur_openldap_base_dn | default("dc=" + domaine_interne.split(".") | join(",dc=")) }}'
> {{ client_backup_staging }}/openldap/annuaire.ldif
chemins: ["{{ client_backup_staging }}/openldap"]
serveur_postgresql:
nom: postgresql
# pg_dumpall : TOUTES les bases + les roles et leurs mots de passe. Une base oubliee
# ici serait une base perdue — d'ou le dump global plutot qu'une liste a maintenir.
commande: >-
runuser -u postgres -- pg_dumpall --clean
> {{ client_backup_staging }}/postgresql/toutes-bases.sql
chemins: ["{{ client_backup_staging }}/postgresql"]
serveur_dovecot:
nom: courriel
chemins: ["{{ serveur_dovecot_vmail_base | default('/var/vmail') }}"]
serveur_forgejo:
nom: forgejo
chemins:
- "{{ serveur_forgejo_data | default('/var/lib/forgejo') }}"
- "{{ serveur_forgejo_config_dir | default('/etc/forgejo') }}"
serveur_nextcloud:
nom: nextcloud
chemins:
- "{{ serveur_nextcloud_data_dir | default('/var/www/nextcloud/data') }}"
- "{{ (serveur_nextcloud_racine | default('/var/www/nextcloud')) + '/config' }}"
serveur_rspamd:
# Le bayes APPRIS est de l'etat : reconstruire le noeud reinstalle rspamd, pas ce
# qu'il a appris. Les regles, elles, se redeploient et ne sont pas ici.
nom: rspamd
chemins: ["/var/lib/rspamd"]
# PAS DE `serveur_web_frontal` (retire le 2026-09-30) : le frontal RELAIE, il ne sert
# rien lui-meme — ses vhosts, son WAF et sa page 404 se redeploient depuis le depot.
# `/srv/web` venait de l'epoque ou il servait du statique ; ses instantanes etaient vides.
serveur_icinga:
# L'AC D'ICINGA FAIT L'IDENTITE DE SA BASE (2026-10-07). Icinga 2 derive l'environnement
# d'Icinga DB de la cle publique de son AC, et chaque identifiant d'hote, de service et
# d'historique de cet environnement. Recreee a chaque reconstruction, elle rendait
# l'historique restaure orphelin : on l'avait donc jete (decision du 2026-09-30, revue
# le 2026-10-07 — l'historique, c'est la disponibilite qu'on doit pouvoir prouver).
nom: icinga
chemins:
- "{{ (serveur_icinga_ca | default('/var/lib/icinga2/ca/ca.crt')) | dirname }}"
- /var/lib/icinga2/icingadb.env
serveur_web_dorsal:
nom: web_dorsal
chemins: ["{{ serveur_web_dorsal_racine | default('/srv/webapp') }}"]
# Jeux retenus pour CE noeud : intersection du catalogue et de ses groupes. Derive, donc
# un tenant qui deplace un service emporte sa sauvegarde avec lui, sans rien re-declarer.
client_backup_jobs: >-
{{ client_backup_catalogue | dict2items
| selectattr('key', 'in', group_names) | map(attribute='value') | list }}
# 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"
# L'Icinga auquel ce noeud a deja rapporte (empreinte de son AC) — voir tasks/verifier.yml.
client_backup_marque_icinga: "/etc/setops/client_backup-icinga-entendu"
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
# LE CONTROLE DE RESTAURATION (2026-09-28) — voir `templates/verifier-restauration.sh.j2`.
# Hebdomadaire : il relit une part du depot et restaure le dernier instantane pour de vrai.
client_backup_restauration_horaire: "Sun *-*-* 04:00:00"
client_backup_restauration_part: "10%"
# Huit jours : une semaine entre deux controles, plus une journee de marge. Au-dela sans
# nouvelle, Icinga passe le service au rouge (voir `serveur_icinga_fraicheur_restauration`).
client_backup_ttl_restauration: 691200
# Combien de temps un script attend un depot verrouille par un autre (`restic --retry-lock`)
# avant d'echouer. Le plus long des occupants est le controle de restauration ; il relit
# 10 % du depot et le restaure entier.
client_backup_attente_verrou: "30m"
# --- LA RESTAURATION DANS LA RECONSTRUCTION (2026-09-30) ----------------------------
#
# Jusqu'ici une reconstruction repartait d'un etat NEUF : les instantanes se restauraient
# pour PROUVER qu'ils s'ouvrent, jamais pour etre remis en service. Desormais chaque role
# proprietaire d'un etat inclut `tasks/restaurer.yml` au moment ou il le creerait neuf, et
# remet celui de l'incarnation precedente s'il existe. Voir `templates/restaurer.sh.j2`.
#
# `false` : ne jamais restaurer (chaque jeu est alors acte « desactive », et la
# sauvegarde ne s'en trouve pas bloquee).
client_backup_restauration_active: true
# Imposer un instantane (son `short_id`) au lieu du dernier anterieur a la naissance.
# Par hote, et le temps d'une passe : `-e client_backup_restauration_instantane=abcd1234`.
client_backup_restauration_instantane: ""
# Ou chaque jeu laisse la trace de ce qui lui est arrive (restaure, neuf, en_place...).
client_backup_restauration_marques: "/etc/setops/restauration"
# Ce qui etait en place avant d'etre ecrase. Hors des chemins sauvegardes : on ne veut
# pas deposer chez l'hebergeur l'etat qu'on vient justement de remplacer.
client_backup_restauration_mise_de_cote: "/var/backups/setops-avant-restauration"