CE QU IL FERME. openipmi.service echouait a chaque demarrage sur les
quatorze machines depuis le 2026-09-02, et systemctl --failed rendait ZERO
partout : non parce qu elles allaient bien, mais parce qu aucune n avait
redemarre depuis. Il a fallu qu un humain redemarre une machine pour que
le defaut existe aux yeux de quelqu un. Un controle qui ne peut echouer
qu au demarrage ne mesure rien tant que rien ne demarre.
PASSIF, ET A DUREE DE VIE. Un controle actif ne voit pas la machine MUETTE.
Ici c est le noeud qui parle, et le ttl de son envoi fait la fraicheur :
sans nouvelle, Icinga perime le service tout seul. Le silence alerte autant
que l echec. Le minuteur declenche AU DEMARRAGE autant que toutes les 15
min : les echecs de cette famille naissent au boot.
CRITIQUE DES LA PREMIERE UNITE, jamais un seuil — une unite en echec est
soit un vrai probleme soit du bruit a retirer, et un seuil ferait vivre le
bruit. Les tolerances se nomment une par une, vide par defaut.
CONTROLE NEGATIF. Unite factice sur obs-01, etat relu dans IcingaDB :
CRITICAL, et le verdict NOMME l unite. Les treize autres OK. Apres
nettoyage : 14/14 OK.
UN CONFLIT EVITE. setops-sauvegardes.conf definissait les object Host ; un
second fichier de controle aurait redefini les memes, et Icinga refuse un
objet en double — la configuration entiere aurait ete rejetee, donc AUCUNE
supervision, en voulant en ajouter. Les hotes vivent maintenant dans
setops-hotes.conf, definis une fois.
TROIS OBSTACLES. ${#tableau[@]} contient {# que Jinja lit comme un debut de
commentaire (remede : comment_start_string en tete du gabarit). Ma premiere
sonde a traduit un 403 « Missing permission: objects/query/service » en
« 0 service » — encore un echec qui ecrasait permission ; l etat se lit
dans IcingaDB. Et un echec apt transitoire sur mon-01, local et disparu au
second essai : mesure avant conclusion.
NON FAIT : le SITE n a pas recu client_sante.
make prouver : CONFORME, 63 OK, 0 echec, 0 saute.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
271 lines
11 KiB
YAML
271 lines
11 KiB
YAML
---
|
|
# 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
|
|
|
|
- name: Telecharger le trousseau de cles Icinga
|
|
when: not telecharger_le_trousseau_de_cles_icinga_present.stat.exists
|
|
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
|
|
|
|
# LE CACHE DU CONTROLEUR D'ABORD, LE DEPOT DISTANT POUR LE RESTE (2026-09-03).
|
|
#
|
|
# Les depots tiers sont en HTTPS, et `client_artefacts` pose
|
|
# `Acquire::https::Proxy "DIRECT"` — ils CONTOURNENT donc le cache du site et sortent sur
|
|
# Internet a chaque construction de VM. `make cacher-paquets` les tire une fois, versions
|
|
# epinglees et empreintes verifiees ; ce role les depose depuis ce cache.
|
|
- name: Poser les paquets tiers depuis le cache du controleur
|
|
ansible.builtin.include_role:
|
|
name: paquets_tiers
|
|
vars:
|
|
paquets_tiers_noms: "{{ serveur_icinga_paquets }}"
|
|
|
|
- name: Installer Icinga 2, Icinga DB et Redis dedie
|
|
ansible.builtin.apt:
|
|
# CE QUE LE CACHE N'A PAS FOURNI, ET RIEN D'AUTRE. Les dependances Debian passent
|
|
# par le cache du site en HTTP ; seuls les paquets tiers non caches exigent Internet.
|
|
# Reinstaller ce que le cache vient de poser ferait un `update_cache` inutile — et
|
|
# hors ligne, il echouerait APRES un travail deja fait.
|
|
name: "{{ serveur_icinga_paquets | difference((paquets_tiers_disponibles | default({})).keys() | list) }}"
|
|
state: present
|
|
update_cache: true
|
|
|
|
when: (serveur_icinga_paquets
|
|
| difference((paquets_tiers_disponibles | default({})).keys() | list)) | length > 0
|
|
- 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 }}"
|
|
|
|
- name: Adopter les facts de base pour Icinga
|
|
ansible.builtin.set_fact:
|
|
serveur_icinga_entree: "{{ resoudre_base_entree }}"
|
|
serveur_icinga_db_password: "{{ resoudre_base_db_password }}"
|
|
serveur_icinga_db_host: "{{ resoudre_base_db_host }}"
|
|
no_log: true
|
|
|
|
# --- Identite TLS de l'API (5665) ---
|
|
# ON NE TOUCHE PAS A `NodeName` — et il n'y a rien a corriger ici.
|
|
#
|
|
# `icinga2 api setup` ECRIT lui-meme NodeName d'apres `hostname -f`, puis nomme ses
|
|
# certificats d'apres lui. Tant que `/etc/hosts` mettait le nom COURT en premier,
|
|
# `hostname -f` rendait « mon-01 » et le certificat devenait invérifiable en appelant par
|
|
# le nom complet. On a longuement tente d'aligner NodeName a la main, avant et apres
|
|
# `api setup` : efface a chaque fois.
|
|
#
|
|
# LA CAUSE ETAIT AILLEURS. Depuis que `hosts_statiques` place le FQDN en premier
|
|
# (2026-08-13), `hostname -f` rend le nom complet et Icinga s'emet spontanement un
|
|
# certificat CN et SAN = FQDN. Rien a forcer : il suffisait que la machine sache
|
|
# comment elle s'appelle.
|
|
- 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
|
|
when: not ansible_check_mode
|
|
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
|
|
|
|
# `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
|
|
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 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 }}"
|
|
|
|
# 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
|
|
fail_msg: >-
|
|
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:
|
|
src: setops-api-users.conf.j2
|
|
dest: "{{ serveur_icinga_api_conf }}"
|
|
owner: root
|
|
group: nagios
|
|
mode: "0640"
|
|
no_log: true
|
|
notify: Redemarrer icinga2
|
|
|
|
# LES HOTES D'ABORD : les fichiers de service s'y attachent, et Icinga refuse un service
|
|
# dont l'hote n'existe pas. L'ordre dans `conf.d` n'est pas garanti par le nom, mais
|
|
# Icinga charge tout le repertoire avant de resoudre — l'ordre de deploiement suffit.
|
|
- name: Deployer les hotes supervises (definis une seule fois)
|
|
ansible.builtin.template:
|
|
src: setops-hotes.conf.j2
|
|
dest: "{{ serveur_icinga_hotes_conf }}"
|
|
owner: root
|
|
group: nagios
|
|
mode: "0640"
|
|
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: Deployer les objets de supervision de la sante des noeuds
|
|
ansible.builtin.template:
|
|
src: setops-sante.conf.j2
|
|
dest: "{{ serveur_icinga_sante_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
|
|
|
|
- name: Activer et demarrer Icinga 2 et Icinga DB
|
|
when: not ansible_check_mode
|
|
ansible.builtin.systemd:
|
|
name: "{{ item }}"
|
|
enabled: true
|
|
state: started
|
|
loop:
|
|
- "{{ serveur_icinga_service_icinga2 }}"
|
|
- "{{ serveur_icinga_service_icingadb }}"
|
|
|
|
# --- 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 }}"
|
|
|
|
# --- A QUI PARLER -------------------------------------------------------------
|
|
- name: Déclarer le destinataire des alertes
|
|
ansible.builtin.template:
|
|
src: setops-users.conf.j2
|
|
dest: /etc/icinga2/conf.d/setops-users.conf
|
|
owner: root
|
|
group: nagios
|
|
mode: "0640"
|
|
when: serveur_icinga_destinataire | length > 0
|
|
notify: Redemarrer icinga2
|
|
|
|
# DIRE CE QU'ON NE FAIT PAS. Sans destinataire, Icinga notifie `root@localhost` — une
|
|
# adresse que personne ne lit. Le silence alerterait alors dans le vide.
|
|
- name: Dire que personne ne recevra les alertes
|
|
ansible.builtin.debug:
|
|
msg: >-
|
|
`serveur_icinga_destinataire` n'est pas renseigne : les notifications iront a
|
|
`root@localhost`, que personne ne lit. La supervision VERRA les defauts sans
|
|
pouvoir les dire. Declarer l'adresse au plan.
|
|
when: serveur_icinga_destinataire | length == 0
|