2026-07-03 22:49:26 -04:00
|
|
|
---
|
|
|
|
|
- name: Exiger la clé publique de sauvegarde
|
|
|
|
|
ansible.builtin.assert:
|
|
|
|
|
that:
|
|
|
|
|
- serveur_backup_pubkey | length > 0
|
|
|
|
|
fail_msg: "serveur_backup_pubkey requis (clé publique de la paire de sauvegarde)."
|
|
|
|
|
|
|
|
|
|
- name: Créer l'utilisateur de sauvegarde
|
|
|
|
|
ansible.builtin.user:
|
|
|
|
|
name: "{{ serveur_backup_utilisateur }}"
|
|
|
|
|
home: "{{ serveur_backup_racine }}"
|
|
|
|
|
shell: /bin/bash
|
|
|
|
|
create_home: true
|
|
|
|
|
system: true
|
|
|
|
|
|
|
|
|
|
- name: Sécuriser la racine des dépôts
|
|
|
|
|
ansible.builtin.file:
|
|
|
|
|
path: "{{ serveur_backup_racine }}"
|
|
|
|
|
state: directory
|
|
|
|
|
owner: "{{ serveur_backup_utilisateur }}"
|
|
|
|
|
group: "{{ serveur_backup_utilisateur }}"
|
|
|
|
|
mode: "0700"
|
|
|
|
|
|
|
|
|
|
- name: Autoriser la clé SSH de sauvegarde
|
|
|
|
|
ansible.posix.authorized_key:
|
|
|
|
|
user: "{{ serveur_backup_utilisateur }}"
|
|
|
|
|
key: "{{ serveur_backup_pubkey }}"
|
|
|
|
|
state: present
|
supervision : c'est le DEPOT qui dit ou en sont les sauvegardes
Icinga ne surveillait rien : aucun objet Host ni Service de Set-OPS, seulement
la config Debian d'origine sur localhost.
Superviser setops-sauvegarde.service aurait reproduit le defaut du jour meme :
l'unite etait VERTE sur onze noeuds pendant qu'elle n'emportait rien. Le noeud
sait qu'il a LANCE sa sauvegarde, pas qu'elle est ARRIVEE. backup-01 evalue donc
ses depots et pousse un resultat passif par noeud vers l'API Icinga.
Trois criteres, parce qu'un seul suffit a mentir : l'instantane existe, il est
recent (26 h / 50 h), il contient au moins un fichier.
Le sens du flux est delibere : le depot parle a la supervision, jamais l'inverse
— compromettre mon-01 ne donne aucun acces aux sauvegardes.
Le ttl de 6 h fait la fraicheur : si le rapporteur se tait, Icinga perime les
services tout seul. C'est le silence qui a laisse le defaut vivre un mois.
Deux erreurs corrigees par la mesure :
- --data-urlencode refuse en Bad Request (l'API veut du JSON) ; le flux, le TLS
et l'auth marchaient, seule la charge etait perdue.
- le seuil « vide » en octets signalait a tort idm-01 (2363 o) : un export LDIF
d'un annuaire a un compte pese cela. « Vide » se mesure en FICHIERS. Et le
verdict est un AVERTISSEMENT : la machine ne distingue pas « les donnees ont
disparu » de « il n'y en a pas encore ».
Reserve assumee : curl -k — l'API presente le cert de sa propre AC, pas step-ca.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 21:39:05 -04:00
|
|
|
|
|
|
|
|
# --- Vérification des dépôts et rapport passif vers Icinga ---
|
|
|
|
|
# La liste des détenteurs d'état appartient à `client_backup`. On la LIT chez lui : deux
|
|
|
|
|
# listes finissent toujours par diverger, et la divergence se lirait « tout va bien ».
|
|
|
|
|
- name: Lire la liste des détenteurs d'état chez client_backup
|
|
|
|
|
ansible.builtin.include_vars:
|
|
|
|
|
file: "{{ role_path }}/../client_backup/vars/main.yml"
|
|
|
|
|
name: _catalogue_sauvegarde
|
|
|
|
|
|
|
|
|
|
- name: Adopter la liste des détenteurs d'état
|
|
|
|
|
ansible.builtin.set_fact:
|
|
|
|
|
client_backup_groupes_etat: "{{ _catalogue_sauvegarde.client_backup_groupes_etat }}"
|
|
|
|
|
|
|
|
|
|
- name: Exiger de quoi rapporter à Icinga (Vault + hôte de supervision)
|
|
|
|
|
ansible.builtin.assert:
|
|
|
|
|
that:
|
|
|
|
|
- serveur_backup_icinga_motdepasse | length > 0
|
|
|
|
|
- serveur_backup_icinga_hote | length > 0
|
|
|
|
|
- serveur_backup_noeuds_attendus | length > 0
|
|
|
|
|
fail_msg: >-
|
|
|
|
|
vault_icinga_api_depot requis, un hôte du groupe `serveur_icinga` doit exister, et
|
|
|
|
|
au moins un nœud doit détenir de l'état. Sans cela, les sauvegardes ne seraient
|
|
|
|
|
surveillées par personne — c'est exactement le silence qui a laissé le défaut du
|
|
|
|
|
2026-07-03 vivre un mois.
|
|
|
|
|
|
|
|
|
|
- name: Installer curl et le mot de passe restic pour la vérification
|
|
|
|
|
ansible.builtin.apt:
|
|
|
|
|
name: [curl, restic]
|
|
|
|
|
state: present
|
|
|
|
|
|
|
|
|
|
- name: Créer le répertoire des secrets Set-OPS
|
|
|
|
|
ansible.builtin.file:
|
|
|
|
|
path: /etc/setops
|
|
|
|
|
state: directory
|
|
|
|
|
owner: root
|
|
|
|
|
group: root
|
|
|
|
|
mode: "0700"
|
|
|
|
|
|
|
|
|
|
- name: Déposer le mot de passe restic (lecture des dépôts)
|
|
|
|
|
ansible.builtin.copy:
|
|
|
|
|
content: "{{ vault_restic_password }}\n"
|
|
|
|
|
dest: /etc/setops/restic.pass
|
|
|
|
|
owner: root
|
|
|
|
|
group: root
|
|
|
|
|
mode: "0600"
|
|
|
|
|
no_log: true
|
|
|
|
|
|
|
|
|
|
- name: Déposer le mot de passe d'API Icinga
|
|
|
|
|
ansible.builtin.copy:
|
|
|
|
|
content: "{{ serveur_backup_icinga_motdepasse }}"
|
|
|
|
|
dest: /etc/setops/icinga-api.pass
|
|
|
|
|
owner: root
|
|
|
|
|
group: root
|
|
|
|
|
mode: "0600"
|
|
|
|
|
no_log: true
|
|
|
|
|
|
reconstruction from-zero : sept defauts, tous invisibles sur une flotte debout
Chezlepro reconstruit depuis zero en 10.17.x.x. Sept defauts sont tombes, et
AUCUN n'etait detectable autrement — c'est tout l'argument de la reconstruction
comme preuve.
1. aucune route vers le tenant sur le poste (posee a la main, jamais persistee)
2. backup-01 exigeait l'AC d'Icinga — dependance non declaree
3. ma correction inversait les couches : REFUSEE PAR P08 avant d'entrer
4. base Forgejo a moitie initialisee (sequelle de l'arret du n°2)
5. /etc/hosts : nom court avant le FQDN -> hostname -f faux sur les 14 machines
6. API Icinga jamais activee (garde `creates:` d'api setup)
7. restic refuse tout le lot si un chemin declare manque
LE CINQUIEME DEPASSAIT ICINGA. hostname -f rend le PREMIER nom : toute la flotte
se croyait appelee « mon-01 ». Visible sur le certificat d'Icinga, mais le meme
piege attendait Postfix (myhostname), les journaux, les certificats. FQDN d'abord.
CE QU'ON NE CORRIGE PAS : icinga2 api setup REECRIT NodeName d'apres le nom court
et nomme ses certificats d'apres lui. Aligne avant, il est ecrase ; aligne apres,
les certificats portent le mauvais nom. On adopte sa convention : le depot appelle
l'API par le nom court, celui que le certificat porte.
serveur_icinga_node_name derive desormais de l'INVENTAIRE, pas d'ansible_fqdn —
un fait qui depend du resolveur et rendait « mon-01 » alors que le FQDN etait bon.
Le commentaire ecrit pour avertir qu'une sequence accolade-diese casse les
gabarits Jinja contenait cette sequence, et cassait le gabarit. Neuf hotes en
echec pour un avertissement mal redige.
Sept devis CONFORME, prouver 36/36, make test 0. Neuf sauvegardes reussies.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 03:36:56 -04:00
|
|
|
# L'AC d'Icinga sert a VERIFIER le pair en rapportant (il refuse de servir un certificat
|
|
|
|
|
# qu'il n'a pas emis). Mais le depot est un SERVICE et la supervision une APPLICATION :
|
|
|
|
|
# la couche `services` precede `apps`, donc Icinga n'existe pas encore au premier
|
|
|
|
|
# deploiement. Exiger son AC ici inversait le graphe — P08 l'a refuse.
|
|
|
|
|
#
|
|
|
|
|
# On la prend donc SI ELLE EXISTE, et c'est `serveur_icinga` qui la POUSSE quand il monte
|
|
|
|
|
# (roles/serveur_icinga/tasks/main.yml). Le rapporteur echoue bruyamment tant qu'elle
|
|
|
|
|
# manque — et il n'y a de toute facon personne a qui rapporter.
|
|
|
|
|
- name: L'AC d'Icinga est-elle deja disponible ?
|
|
|
|
|
ansible.builtin.stat:
|
|
|
|
|
path: "{{ serveur_backup_icinga_ca_source }}"
|
|
|
|
|
delegate_to: "{{ serveur_backup_icinga_hote }}"
|
|
|
|
|
register: serveur_backup_ca_presente
|
|
|
|
|
|
icinga : le pair est verifie — mais pas avec un certificat step-ca
Objectif : supprimer le `curl -k` du rapporteur. Fait, et la maniere a ete
imposee par la mesure.
SERVIR UN CERTIFICAT STEP-CA SUR L'API EST IMPOSSIBLE. Icinga le dit lui-meme :
« Our certificate will expire soon, but we own the CA. Renewing. » Il renouvelle
tout certificat expirant sous 30 jours ; les notres vivent 24 h ; possedant une
AC, il re-emet avec la sienne et ecrase le notre a chaque demarrage. Collision
entre deux politiques de PKI, et la notre n'est pas negociable.
Deux decouvertes en chemin, toutes deux par le garde-fou `icinga2 daemon -C`
ajoute au role, qui a ARRETE le deploiement avant de redemarrer la supervision :
- cert_path/key_path/ca_path sont DEPRECIES depuis 2.8 ; les poser reveille un
chemin de code herite qui exige en plus un objet Endpoint ;
- l'identite de l'API est le CN du certificat. NodeName valait « mon-01 », donc
le SAN aussi, alors qu'on appelle par le FQDN : aucune verification n'aurait
pu reussir. NodeName est desormais aligne sur le FQDN.
A LA PLACE : Icinga garde son AC (domaine de confiance FERME, legitime) et
backup-01 verifie le pair contre CETTE AC, recuperee depuis mon-01 au
deploiement. Le pair est authentifie ; seule la racine differe.
Controle negatif — une verification qu'on ne teste pas est un ornement : avec la
mauvaise AC (step-ca), curl refuse (« unable to get local issuer certificate ») ;
avec la bonne, les neuf rapports passent.
Sous-AC step-ca ecartee : elle poserait sur l'hote de supervision une cle capable
d'EMETTRE pour n'importe quel nom, alors qu'on a choisi le sens du flux pour que
compromettre mon-01 ne donne rien.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 08:33:50 -04:00
|
|
|
- name: Récupérer l'AC d'Icinga depuis l'hôte de supervision
|
reconstruction from-zero : sept defauts, tous invisibles sur une flotte debout
Chezlepro reconstruit depuis zero en 10.17.x.x. Sept defauts sont tombes, et
AUCUN n'etait detectable autrement — c'est tout l'argument de la reconstruction
comme preuve.
1. aucune route vers le tenant sur le poste (posee a la main, jamais persistee)
2. backup-01 exigeait l'AC d'Icinga — dependance non declaree
3. ma correction inversait les couches : REFUSEE PAR P08 avant d'entrer
4. base Forgejo a moitie initialisee (sequelle de l'arret du n°2)
5. /etc/hosts : nom court avant le FQDN -> hostname -f faux sur les 14 machines
6. API Icinga jamais activee (garde `creates:` d'api setup)
7. restic refuse tout le lot si un chemin declare manque
LE CINQUIEME DEPASSAIT ICINGA. hostname -f rend le PREMIER nom : toute la flotte
se croyait appelee « mon-01 ». Visible sur le certificat d'Icinga, mais le meme
piege attendait Postfix (myhostname), les journaux, les certificats. FQDN d'abord.
CE QU'ON NE CORRIGE PAS : icinga2 api setup REECRIT NodeName d'apres le nom court
et nomme ses certificats d'apres lui. Aligne avant, il est ecrase ; aligne apres,
les certificats portent le mauvais nom. On adopte sa convention : le depot appelle
l'API par le nom court, celui que le certificat porte.
serveur_icinga_node_name derive desormais de l'INVENTAIRE, pas d'ansible_fqdn —
un fait qui depend du resolveur et rendait « mon-01 » alors que le FQDN etait bon.
Le commentaire ecrit pour avertir qu'une sequence accolade-diese casse les
gabarits Jinja contenait cette sequence, et cassait le gabarit. Neuf hotes en
echec pour un avertissement mal redige.
Sept devis CONFORME, prouver 36/36, make test 0. Neuf sauvegardes reussies.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 03:36:56 -04:00
|
|
|
when: serveur_backup_ca_presente.stat.exists
|
icinga : le pair est verifie — mais pas avec un certificat step-ca
Objectif : supprimer le `curl -k` du rapporteur. Fait, et la maniere a ete
imposee par la mesure.
SERVIR UN CERTIFICAT STEP-CA SUR L'API EST IMPOSSIBLE. Icinga le dit lui-meme :
« Our certificate will expire soon, but we own the CA. Renewing. » Il renouvelle
tout certificat expirant sous 30 jours ; les notres vivent 24 h ; possedant une
AC, il re-emet avec la sienne et ecrase le notre a chaque demarrage. Collision
entre deux politiques de PKI, et la notre n'est pas negociable.
Deux decouvertes en chemin, toutes deux par le garde-fou `icinga2 daemon -C`
ajoute au role, qui a ARRETE le deploiement avant de redemarrer la supervision :
- cert_path/key_path/ca_path sont DEPRECIES depuis 2.8 ; les poser reveille un
chemin de code herite qui exige en plus un objet Endpoint ;
- l'identite de l'API est le CN du certificat. NodeName valait « mon-01 », donc
le SAN aussi, alors qu'on appelle par le FQDN : aucune verification n'aurait
pu reussir. NodeName est desormais aligne sur le FQDN.
A LA PLACE : Icinga garde son AC (domaine de confiance FERME, legitime) et
backup-01 verifie le pair contre CETTE AC, recuperee depuis mon-01 au
deploiement. Le pair est authentifie ; seule la racine differe.
Controle negatif — une verification qu'on ne teste pas est un ornement : avec la
mauvaise AC (step-ca), curl refuse (« unable to get local issuer certificate ») ;
avec la bonne, les neuf rapports passent.
Sous-AC step-ca ecartee : elle poserait sur l'hote de supervision une cle capable
d'EMETTRE pour n'importe quel nom, alors qu'on a choisi le sens du flux pour que
compromettre mon-01 ne donne rien.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 08:33:50 -04:00
|
|
|
ansible.builtin.slurp:
|
|
|
|
|
src: "{{ serveur_backup_icinga_ca_source }}"
|
|
|
|
|
delegate_to: "{{ serveur_backup_icinga_hote }}"
|
|
|
|
|
register: serveur_backup_ca_icinga
|
|
|
|
|
|
|
|
|
|
- name: Déposer l'AC d'Icinga pour la vérification du pair
|
reconstruction from-zero : sept defauts, tous invisibles sur une flotte debout
Chezlepro reconstruit depuis zero en 10.17.x.x. Sept defauts sont tombes, et
AUCUN n'etait detectable autrement — c'est tout l'argument de la reconstruction
comme preuve.
1. aucune route vers le tenant sur le poste (posee a la main, jamais persistee)
2. backup-01 exigeait l'AC d'Icinga — dependance non declaree
3. ma correction inversait les couches : REFUSEE PAR P08 avant d'entrer
4. base Forgejo a moitie initialisee (sequelle de l'arret du n°2)
5. /etc/hosts : nom court avant le FQDN -> hostname -f faux sur les 14 machines
6. API Icinga jamais activee (garde `creates:` d'api setup)
7. restic refuse tout le lot si un chemin declare manque
LE CINQUIEME DEPASSAIT ICINGA. hostname -f rend le PREMIER nom : toute la flotte
se croyait appelee « mon-01 ». Visible sur le certificat d'Icinga, mais le meme
piege attendait Postfix (myhostname), les journaux, les certificats. FQDN d'abord.
CE QU'ON NE CORRIGE PAS : icinga2 api setup REECRIT NodeName d'apres le nom court
et nomme ses certificats d'apres lui. Aligne avant, il est ecrase ; aligne apres,
les certificats portent le mauvais nom. On adopte sa convention : le depot appelle
l'API par le nom court, celui que le certificat porte.
serveur_icinga_node_name derive desormais de l'INVENTAIRE, pas d'ansible_fqdn —
un fait qui depend du resolveur et rendait « mon-01 » alors que le FQDN etait bon.
Le commentaire ecrit pour avertir qu'une sequence accolade-diese casse les
gabarits Jinja contenait cette sequence, et cassait le gabarit. Neuf hotes en
echec pour un avertissement mal redige.
Sept devis CONFORME, prouver 36/36, make test 0. Neuf sauvegardes reussies.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 03:36:56 -04:00
|
|
|
when: serveur_backup_ca_presente.stat.exists
|
icinga : le pair est verifie — mais pas avec un certificat step-ca
Objectif : supprimer le `curl -k` du rapporteur. Fait, et la maniere a ete
imposee par la mesure.
SERVIR UN CERTIFICAT STEP-CA SUR L'API EST IMPOSSIBLE. Icinga le dit lui-meme :
« Our certificate will expire soon, but we own the CA. Renewing. » Il renouvelle
tout certificat expirant sous 30 jours ; les notres vivent 24 h ; possedant une
AC, il re-emet avec la sienne et ecrase le notre a chaque demarrage. Collision
entre deux politiques de PKI, et la notre n'est pas negociable.
Deux decouvertes en chemin, toutes deux par le garde-fou `icinga2 daemon -C`
ajoute au role, qui a ARRETE le deploiement avant de redemarrer la supervision :
- cert_path/key_path/ca_path sont DEPRECIES depuis 2.8 ; les poser reveille un
chemin de code herite qui exige en plus un objet Endpoint ;
- l'identite de l'API est le CN du certificat. NodeName valait « mon-01 », donc
le SAN aussi, alors qu'on appelle par le FQDN : aucune verification n'aurait
pu reussir. NodeName est desormais aligne sur le FQDN.
A LA PLACE : Icinga garde son AC (domaine de confiance FERME, legitime) et
backup-01 verifie le pair contre CETTE AC, recuperee depuis mon-01 au
deploiement. Le pair est authentifie ; seule la racine differe.
Controle negatif — une verification qu'on ne teste pas est un ornement : avec la
mauvaise AC (step-ca), curl refuse (« unable to get local issuer certificate ») ;
avec la bonne, les neuf rapports passent.
Sous-AC step-ca ecartee : elle poserait sur l'hote de supervision une cle capable
d'EMETTRE pour n'importe quel nom, alors qu'on a choisi le sens du flux pour que
compromettre mon-01 ne donne rien.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 08:33:50 -04:00
|
|
|
ansible.builtin.copy:
|
|
|
|
|
content: "{{ serveur_backup_ca_icinga.content | b64decode }}"
|
|
|
|
|
dest: "{{ serveur_backup_ca_verification }}"
|
|
|
|
|
owner: root
|
|
|
|
|
group: root
|
|
|
|
|
mode: "0644"
|
|
|
|
|
|
supervision : c'est le DEPOT qui dit ou en sont les sauvegardes
Icinga ne surveillait rien : aucun objet Host ni Service de Set-OPS, seulement
la config Debian d'origine sur localhost.
Superviser setops-sauvegarde.service aurait reproduit le defaut du jour meme :
l'unite etait VERTE sur onze noeuds pendant qu'elle n'emportait rien. Le noeud
sait qu'il a LANCE sa sauvegarde, pas qu'elle est ARRIVEE. backup-01 evalue donc
ses depots et pousse un resultat passif par noeud vers l'API Icinga.
Trois criteres, parce qu'un seul suffit a mentir : l'instantane existe, il est
recent (26 h / 50 h), il contient au moins un fichier.
Le sens du flux est delibere : le depot parle a la supervision, jamais l'inverse
— compromettre mon-01 ne donne aucun acces aux sauvegardes.
Le ttl de 6 h fait la fraicheur : si le rapporteur se tait, Icinga perime les
services tout seul. C'est le silence qui a laisse le defaut vivre un mois.
Deux erreurs corrigees par la mesure :
- --data-urlencode refuse en Bad Request (l'API veut du JSON) ; le flux, le TLS
et l'auth marchaient, seule la charge etait perdue.
- le seuil « vide » en octets signalait a tort idm-01 (2363 o) : un export LDIF
d'un annuaire a un compte pese cela. « Vide » se mesure en FICHIERS. Et le
verdict est un AVERTISSEMENT : la machine ne distingue pas « les donnees ont
disparu » de « il n'y en a pas encore ».
Reserve assumee : curl -k — l'API presente le cert de sa propre AC, pas step-ca.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 21:39:05 -04:00
|
|
|
- name: Déployer le script de vérification
|
|
|
|
|
ansible.builtin.template:
|
|
|
|
|
src: verifier-sauvegardes.sh.j2
|
|
|
|
|
dest: /usr/local/sbin/setops-verifier-sauvegardes.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.service.j2, d: setops-verification.service }
|
|
|
|
|
- { s: setops-verification.timer.j2, d: setops-verification.timer }
|
|
|
|
|
|
|
|
|
|
- name: Activer le timer de vérification
|
|
|
|
|
when: not ansible_check_mode
|
|
|
|
|
ansible.builtin.systemd:
|
|
|
|
|
name: setops-verification.timer
|
|
|
|
|
enabled: true
|
|
|
|
|
state: started
|
|
|
|
|
daemon_reload: true
|