2026-06-24 20:17:46 -04:00
|
|
|
---
|
2026-08-09 14:05:25 -04:00
|
|
|
# Recuperee UNE FOIS. Sans garde, chaque deploiement recontactait le serveur du
|
|
|
|
|
# fournisseur : cinq cles x quatorze hotes = soixante-dix allers-retours externes pour
|
|
|
|
|
# des cles deja installees, et autant d'occasions qu'un tiers lent fasse tomber le
|
|
|
|
|
# deploiement. Arbitrage rendu le 2026-08-09 : une plateforme souveraine ne depend pas
|
|
|
|
|
# de six serveurs etrangers pour redeployer ce qu'elle possede deja.
|
|
|
|
|
#
|
|
|
|
|
# CONSEQUENCE ASSUMEE : une rotation de cle amont n'est plus recuperee toute seule. Elle
|
|
|
|
|
# ne passe pas inapercue pour autant — `apt` refuse alors le depot, bruyamment. Pour
|
|
|
|
|
# forcer le rafraichissement : supprimer le fichier et rejouer le role.
|
|
|
|
|
- name: Cette ressource est-elle deja recuperee ? (Telecharger le trousseau de cles I)
|
|
|
|
|
ansible.builtin.stat:
|
|
|
|
|
path: "/tmp/icinga-archive-keyring.deb"
|
|
|
|
|
register: telecharger_le_trousseau_de_cles_icinga_present
|
|
|
|
|
|
2026-06-24 20:17:46 -04:00
|
|
|
- name: Telecharger le trousseau de cles Icinga
|
2026-08-09 14:05:25 -04:00
|
|
|
when: not telecharger_le_trousseau_de_cles_icinga_present.stat.exists
|
2026-06-24 20:17:46 -04:00
|
|
|
ansible.builtin.get_url:
|
|
|
|
|
url: "{{ serveur_icinga_keyring_url }}"
|
|
|
|
|
dest: "/tmp/icinga-archive-keyring.deb"
|
|
|
|
|
mode: "0644"
|
|
|
|
|
|
|
|
|
|
- name: Installer le trousseau de cles Icinga
|
|
|
|
|
ansible.builtin.apt:
|
|
|
|
|
deb: "/tmp/icinga-archive-keyring.deb"
|
|
|
|
|
state: present
|
|
|
|
|
|
|
|
|
|
- name: Ajouter le depot apt Icinga
|
|
|
|
|
ansible.builtin.apt_repository:
|
|
|
|
|
repo: "{{ serveur_icinga_depot_source }}"
|
|
|
|
|
filename: icinga
|
|
|
|
|
state: present
|
|
|
|
|
|
|
|
|
|
- name: Installer Icinga 2, Icinga DB et Redis dedie
|
|
|
|
|
ansible.builtin.apt:
|
|
|
|
|
name: "{{ serveur_icinga_paquets }}"
|
|
|
|
|
state: present
|
|
|
|
|
update_cache: true
|
|
|
|
|
|
2026-07-03 15:57:06 -04:00
|
|
|
- name: Resoudre la base de donnees depuis le registre (role partage)
|
|
|
|
|
ansible.builtin.include_role:
|
|
|
|
|
name: resoudre_base
|
|
|
|
|
vars:
|
|
|
|
|
resoudre_base_groupe: "{{ serveur_icinga_groupe }}"
|
2026-06-24 20:17:46 -04:00
|
|
|
|
2026-07-03 15:57:06 -04:00
|
|
|
- name: Adopter les facts de base pour Icinga
|
2026-06-24 20:17:46 -04:00
|
|
|
ansible.builtin.set_fact:
|
2026-07-03 15:57:06 -04:00
|
|
|
serveur_icinga_entree: "{{ resoudre_base_entree }}"
|
|
|
|
|
serveur_icinga_db_password: "{{ resoudre_base_db_password }}"
|
|
|
|
|
serveur_icinga_db_host: "{{ resoudre_base_db_host }}"
|
2026-06-24 20:17:46 -04:00
|
|
|
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
|
|
|
# --- Identite TLS de l'API (5665) ---
|
|
|
|
|
# ON NE TOUCHE PAS A `NodeName`, et c'est une lecon payee cher (2026-08-12).
|
|
|
|
|
# `icinga2 api setup` REECRIT lui-meme NodeName d'apres le nom COURT de la machine, puis
|
|
|
|
|
# nomme ses certificats d'apres lui : CN=mon-01, SAN=DNS:mon-01. Toute tentative de
|
|
|
|
|
# l'aligner sur le FQDN est effacee au passage suivant — on l'a essaye avant et apres
|
|
|
|
|
# `api setup`, sans succes.
|
|
|
|
|
#
|
|
|
|
|
# Plutot que de lutter contre sa convention, on l'ADOPTE : le consommateur appelle l'API
|
|
|
|
|
# par le nom que le certificat porte (`serveur_backup` compose son URL sur le nom court).
|
|
|
|
|
# Le plancher `/etc/hosts` deploye par `hosts_statiques` le resout sur chaque machine.
|
2026-06-24 20:17:46 -04:00
|
|
|
- name: Configurer l'API Icinga 2
|
|
|
|
|
ansible.builtin.command:
|
|
|
|
|
cmd: icinga2 api setup
|
|
|
|
|
creates: /etc/icinga2/features-enabled/api.conf
|
|
|
|
|
notify: Redemarrer icinga2
|
|
|
|
|
|
|
|
|
|
- name: Activer la fonctionnalite icingadb dans Icinga 2
|
|
|
|
|
ansible.builtin.command:
|
|
|
|
|
cmd: icinga2 feature enable icingadb
|
|
|
|
|
creates: /etc/icinga2/features-enabled/icingadb.conf
|
|
|
|
|
notify: Redemarrer icinga2
|
|
|
|
|
|
|
|
|
|
- name: Activer et demarrer le Redis Icinga DB
|
2026-07-01 21:01:53 -04:00
|
|
|
when: not ansible_check_mode
|
2026-06-24 20:17:46 -04:00
|
|
|
ansible.builtin.systemd:
|
|
|
|
|
name: "{{ serveur_icinga_service_redis }}"
|
|
|
|
|
enabled: true
|
|
|
|
|
state: started
|
|
|
|
|
|
|
|
|
|
- name: Importer le schema Icinga DB dans PostgreSQL (une fois)
|
|
|
|
|
ansible.builtin.shell:
|
|
|
|
|
cmd: >-
|
|
|
|
|
PGPASSWORD='{{ serveur_icinga_db_password }}'
|
|
|
|
|
psql -h {{ serveur_icinga_db_host }} -U {{ serveur_icinga_entree.proprietaire }}
|
|
|
|
|
-d {{ serveur_icinga_entree.base }} -f {{ serveur_icinga_schema }}
|
|
|
|
|
&& touch /etc/icingadb/.schema-imported
|
|
|
|
|
creates: /etc/icingadb/.schema-imported
|
|
|
|
|
no_log: true
|
|
|
|
|
|
|
|
|
|
- name: Deployer la configuration Icinga DB
|
|
|
|
|
ansible.builtin.template:
|
|
|
|
|
src: config.yml.j2
|
|
|
|
|
dest: "{{ serveur_icinga_config }}"
|
|
|
|
|
owner: root
|
|
|
|
|
group: icingadb
|
|
|
|
|
mode: "0640"
|
|
|
|
|
no_log: true
|
|
|
|
|
notify: Redemarrer icingadb
|
|
|
|
|
|
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
|
|
|
# `icinga2 api setup` active la fonctionnalite EN MEME TEMPS qu'il pose les certificats,
|
|
|
|
|
# et sa garde `creates:` la saute des que le fichier existe. Consequence mesuree le
|
|
|
|
|
# 2026-08-12 : apres un nettoyage des certificats, l'API restait DESACTIVEE — icinga2
|
|
|
|
|
# demarrait, se declarait `active`, et n'ecoutait sur rien. Exiger l'activation
|
|
|
|
|
# separement rend l'etat independant de l'ordre des nettoyages.
|
|
|
|
|
- name: API — exiger que la fonctionnalite soit activee
|
|
|
|
|
ansible.builtin.command:
|
|
|
|
|
cmd: icinga2 feature enable api
|
|
|
|
|
creates: /etc/icinga2/features-enabled/api.conf
|
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
|
|
|
notify: Redemarrer icinga2
|
|
|
|
|
|
|
|
|
|
- name: API — durcir l'ApiListener (aucune config ni commande acceptee)
|
|
|
|
|
ansible.builtin.template:
|
|
|
|
|
src: api.conf.j2
|
|
|
|
|
dest: /etc/icinga2/features-available/api.conf
|
|
|
|
|
owner: root
|
|
|
|
|
group: nagios
|
|
|
|
|
mode: "0640"
|
|
|
|
|
notify: Redemarrer icinga2
|
|
|
|
|
|
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
|
|
|
# --- Supervision des sauvegardes ---
|
|
|
|
|
# La liste des detenteurs d'etat appartient a `client_backup`. On la LIT chez lui plutot
|
|
|
|
|
# que de la recopier : deux listes finissent toujours par diverger, et la divergence se
|
|
|
|
|
# lirait « tout va bien » des deux cotes.
|
|
|
|
|
- name: Lire la liste des detenteurs d'etat chez client_backup
|
|
|
|
|
ansible.builtin.include_vars:
|
|
|
|
|
file: "{{ role_path }}/../client_backup/vars/main.yml"
|
|
|
|
|
name: _catalogue_sauvegarde
|
|
|
|
|
|
|
|
|
|
- name: Adopter la liste des detenteurs d'etat
|
|
|
|
|
ansible.builtin.set_fact:
|
|
|
|
|
client_backup_groupes_etat: "{{ _catalogue_sauvegarde.client_backup_groupes_etat }}"
|
|
|
|
|
|
|
|
|
|
- name: Exiger le secret d'API du depot (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.
|
|
|
|
|
|
|
|
|
|
- name: Deployer le compte d'API du depot de sauvegarde
|
|
|
|
|
ansible.builtin.template:
|
|
|
|
|
src: setops-api-users.conf.j2
|
|
|
|
|
dest: "{{ serveur_icinga_api_conf }}"
|
|
|
|
|
owner: root
|
|
|
|
|
group: nagios
|
|
|
|
|
mode: "0640"
|
|
|
|
|
no_log: true
|
|
|
|
|
notify: Redemarrer icinga2
|
|
|
|
|
|
|
|
|
|
- name: Deployer les objets de supervision des sauvegardes
|
|
|
|
|
ansible.builtin.template:
|
|
|
|
|
src: setops-sauvegardes.conf.j2
|
|
|
|
|
dest: "{{ serveur_icinga_setops_conf }}"
|
|
|
|
|
owner: root
|
|
|
|
|
group: nagios
|
|
|
|
|
mode: "0640"
|
|
|
|
|
notify: Redemarrer icinga2
|
|
|
|
|
|
|
|
|
|
- name: Valider la configuration Icinga 2 avant de la rendre vivante
|
|
|
|
|
ansible.builtin.command:
|
|
|
|
|
cmd: icinga2 daemon -C
|
|
|
|
|
changed_when: false
|
|
|
|
|
|
2026-06-24 20:17:46 -04:00
|
|
|
- name: Activer et demarrer Icinga 2 et Icinga DB
|
2026-07-01 21:01:53 -04:00
|
|
|
when: not ansible_check_mode
|
2026-06-24 20:17:46 -04:00
|
|
|
ansible.builtin.systemd:
|
|
|
|
|
name: "{{ item }}"
|
|
|
|
|
enabled: true
|
|
|
|
|
state: started
|
|
|
|
|
loop:
|
|
|
|
|
- "{{ serveur_icinga_service_icinga2 }}"
|
|
|
|
|
- "{{ serveur_icinga_service_icingadb }}"
|
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
|
|
|
|
|
|
|
|
# --- Publier l'AC vers le depot de sauvegarde ---
|
|
|
|
|
# Le depot rapporte l'etat des instantanes a cette API et doit VERIFIER le pair. Il lui
|
|
|
|
|
# faut donc cette AC — mais lui est un SERVICE et nous une APPLICATION : il se deploie
|
|
|
|
|
# AVANT nous, et exiger son attente inverserait le graphe des couches (refuse par P08 le
|
|
|
|
|
# 2026-08-12). C'est donc a NOUS de la lui porter, une fois que `icinga2 api setup` l'a
|
|
|
|
|
# creee. Sens correct : l'application rejoint le service, jamais l'inverse.
|
|
|
|
|
- name: Publier l'AC d'Icinga vers le depot de sauvegarde
|
|
|
|
|
when: groups['serveur_backup'] | default([]) | length > 0
|
|
|
|
|
ansible.builtin.slurp:
|
|
|
|
|
src: "{{ serveur_icinga_ca }}"
|
|
|
|
|
register: serveur_icinga_ca_contenu
|
|
|
|
|
|
|
|
|
|
- name: Deposer l'AC sur le depot de sauvegarde
|
|
|
|
|
when: groups['serveur_backup'] | default([]) | length > 0
|
|
|
|
|
ansible.builtin.copy:
|
|
|
|
|
content: "{{ serveur_icinga_ca_contenu.content | b64decode }}"
|
|
|
|
|
dest: "{{ serveur_icinga_ca_destination_depot }}"
|
|
|
|
|
owner: root
|
|
|
|
|
group: root
|
|
|
|
|
mode: "0644"
|
|
|
|
|
delegate_to: "{{ groups['serveur_backup'] | first }}"
|