vigie : sa base dans tous les modes ; trois restes du site corrigés

- serveur_icingaweb2 : base de console résolue et utilisée en SSO comme en db
  (config_backend db) ; le badge de migrations la réclame sans condition
- serveur_icingaweb2 : php8.4-fpm et nginx avant les paquets tiers ; apache2
  retiré s'il part seul (apt-get -s purge)
- serveur_nginx : ssl_protocols de Debian neutralisé (doublon à chaque reload)
- site_inventaire : les hyperviseurs ne reçoivent plus le cache du site ni ses
  amorçages, qu'ils n'atteignent pas
- exemple de voûte : vault_bd_icingaweb2 dans tous les modes ; CHANGELOG 64

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-10-03 21:04:51 -04:00
parent 2f275d8a7d
commit f11bb11ad7
8 changed files with 143 additions and 18 deletions

View file

@ -1,5 +1,61 @@
# CHANGELOG — Set-OPS
## 2026-10-03 (64) — La vigie a sa base partout ; trois restes du site corrigés
**Demande de l'exploitant** : traiter les points 3 et 5 du rapport d'anomalies.
**3. Icinga Web sans base chez les locataires.** Depuis leur reconstruction, la vigie de
Chezlepro journalisait 145 fois par heure, page ouverte, « Failed to load pending
migrations : Please check if a db instance exists at all » ; Technolibre de même dès qu'on la
regardait. Le site avait reçu sa base le 2026-09-15, mais seulement en mode `db` ; les
locataires, en SSO, étaient restés en `config_backend = "ini"`. Lu dans le code
d'Icinga Web : `ConfigMenu::createMigrationBadge` compte les migrations SANS condition de
permission — aucun réglage de rôle ne le fait taire. **Fait** : `serveur_icingaweb2` résout
et utilise sa base dans TOUS les modes (`config_backend = "db"`, ressource `icingaweb_db`,
schéma chargé une fois) ; les comptes en base et le compte d'amorçage restent propres au
mode `db`. Base `icingaweb2` ajoutée aux plans de Chezlepro, Technolibre, du lab et des
deux modèles qui portent une vigie (`observabilite`, `integral`) ; `vault_bd_icingaweb2`
aux gabarits de voûte et à l'exemple public. **Reste à l'exploitant** : la valeur réelle
dans trois voûtes (l'écriture a été refusée à l'assistant), puis le déploiement chez les
deux locataires (`serveur_postgresql`, `serveur_icingaweb2`, `serveur_ops_tenant` pour
que le runner reçoive la voûte). Les préférences déjà rangées en fichiers ne sont pas
reprises : ce ne sont que des réglages d'affichage.
**5a. Le cache du site donné aux hyperviseurs, qui ne l'atteignent pas.** Mesuré depuis
asgard : `10.37.33.21:3142` injoignable. Or `artefacts_amorcage` leur était transmis par
`communes` : un passage de `client_journal` aurait réécrit leur dépôt Grafana en `http://`
par ce mandataire, et apt l'aurait perdu. **Fait** : `site_inventaire.py` ne leur donne plus
ni `artefacts_amorcage`, ni `setops_depot_binaires`, ni `dns_amorcage` (son jumeau, P79) —
amorçages d'une VM née du gabarit, qu'aucun rôle des hyperviseurs ne lit. Simulé : un passage
de `client_journal` n'y change plus que la config Alloy.
**5b. apache2 sur `site-mon-01`.** Tiré par `libapache2-mod-php8.4`, dont plus rien ne
dépendait : au site, les paquets d'Icinga viennent du cache du contrôleur et s'installaient
AVANT `php8.4-fpm`, et apt prenait la première alternative PHP. Les locataires, en une seule
transaction, n'avaient pas ce reste. **Fait** : `php8.4-fpm` et nginx posés avant les
paquets tiers ; apache2 retiré seulement si `apt-get -s purge` confirme qu'il part seul.
Appliqué : 0 paquet apache, port 80 fermé, vigie en 302.
**5c. Grafana Live sans WebSocket.** Chaque page de l'observatoire journalisait
`GET /api/live/ws` en 400, au site comme chez les locataires : l'edge relayait sans
`Upgrade`. Le mécanisme existait (Collabora) ; Grafana ne le déclarait pas. **Fait** :
`websocket: true` sur `grafana` dans les six plans qui le portent (site, deux locataires,
lab, deux modèles). Appliqué au site : la poignée WebSocket atteint Grafana (401 sans
session, au lieu de 400).
**5d. `ssl_protocols` en double.** Le `nginx.conf` de Debian le déclare déjà ;
`99-setops.conf` le répétait au même niveau — `duplicate value "TLSv1.3"` à chaque
rechargement. **Fait** : la ligne de Debian est neutralisée, comme `server_tokens` avant
elle. Appliqué au site : `nginx -t` sans avertissement.
**Validation** : `ansible-lint` (0 violation), `make verifier` conforme sur Chezlepro (83/83)
et preuves conformes sur Technolibre (82 + 1 sautée, la voûte) ; `voute.py verifier` complet
pour les deux. Au site : simulé puis appliqué, 0 échec.
**Fuite à signaler** : en lisant la config d'Icinga Web de Chezlepro, l'assistant a affiché
en clair le mot de passe de liaison LDAP `cn=icingaweb2` (`bind_pw` n'était pas masqué). Il
n'est écrit nulle part ailleurs que dans la conversation ; une rotation est recommandée.
## 2026-10-03 (63) — Les locataires mesurés et corrigés : même bruit, et le courriel en ajoute
**Demande de l'exploitant** : mesurer chez les deux locataires les défauts corrigés au site

View file

@ -32,7 +32,8 @@ vault_postgresql_keycloak: ""
vault_bd_keycloak: ""
vault_bd_forgejo: ""
vault_bd_icingadb: ""
# La base des COMPTES de la console (mode `db`) — distincte de celle du moteur.
# La base de la console (vigie) dans tous les modes : comptes en mode `db`, préférences
# et migrations partout — distincte de celle du moteur.
vault_bd_icingaweb2: ""
# --- Forge (Forgejo) ---

View file

@ -16,6 +16,19 @@ serveur_icingaweb2_paquets:
- php8.4-mbstring
- nginx
# INSTALLES AVANT LES PAQUETS TIERS, pour qu'apt ne choisisse pas apache2 (voir tasks).
serveur_icingaweb2_web_prealables:
- php8.4-fpm
- nginx
# Ce qu'un ordre d'installation fautif a laisse, et que le role retire s'il part seul.
serveur_icingaweb2_apache_restes:
- apache2
- apache2-bin
- apache2-data
- apache2-utils
- libapache2-mod-php8.4
serveur_icingaweb2_php_fpm_service: "php8.4-fpm"
serveur_icingaweb2_php_fpm_socket: "/run/php/php8.4-fpm.sock"
serveur_icingaweb2_nginx_service: "nginx"

View file

@ -52,7 +52,13 @@
name: resoudre_base
vars:
resoudre_base_groupe: "icingaweb2"
when: serveur_icingaweb2_auth == 'db'
# DANS TOUS LES MODES (2026-10-03), plus seulement `db`. Le cadre de migration d'Icinga
# Web 2 veut une base quoi qu'il arrive : sans elle, chaque page qui affiche le menu de
# configuration journalise « Failed to load pending migrations : Please check if a db
# instance exists at all » — 145 fois par heure chez Chezlepro, vigie ouverte, depuis
# la reconstruction. Le site avait recu sa base le 2026-09-15 ; les locataires, en SSO,
# etaient restes sans. Le badge est calcule sans condition de permission
# (`ConfigMenu::createMigrationBadge`) : aucun reglage de role ne le fait taire.
- name: Adopter la base des comptes pour Icinga Web 2
ansible.builtin.set_fact:
@ -62,7 +68,6 @@
serveur_icingaweb2_db_utilisateur: "{{ resoudre_base_entree.proprietaire }}"
serveur_icingaweb2_db_motdepasse: "{{ resoudre_base_db_password }}"
no_log: true
when: serveur_icingaweb2_auth == 'db'
- name: Exiger un mot de passe pour le compte d'amorçage
ansible.builtin.assert:
@ -76,7 +81,7 @@
# LE SCHEMA NE SE CHARGE QU'UNE FOIS. Le rejouer sur une base deja peuplee echouerait sur
# les objets existants — on lit donc d'abord si la table des comptes est la.
- name: La base des comptes est-elle déjà en place ?
- name: La base de la console est-elle déjà en place ?
community.postgresql.postgresql_query:
login_host: "{{ serveur_icingaweb2_db_host }}"
login_user: "{{ serveur_icingaweb2_db_utilisateur }}"
@ -89,10 +94,9 @@
changed_when: false
no_log: true
when:
- serveur_icingaweb2_auth == 'db'
- not ansible_check_mode
- name: Charger le schéma des comptes
- name: Charger le schéma de la console (comptes, préférences, migrations)
community.postgresql.postgresql_script:
login_host: "{{ serveur_icingaweb2_db_host }}"
login_user: "{{ serveur_icingaweb2_db_utilisateur }}"
@ -103,7 +107,6 @@
path: "{{ serveur_icingaweb2_schema }}"
no_log: true
when:
- serveur_icingaweb2_auth == 'db'
- not ansible_check_mode
- not (serveur_icingaweb2_schema_etat.query_result[0].presente | default(false))
@ -192,6 +195,18 @@
# `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.
# PHP-FPM ET NGINX AVANT LES PAQUETS TIERS (2026-10-03). Les paquets d'Icinga exigent
# « un PHP pour le web », et apt choisit la PREMIERE alternative qu'aucun paquet present ne
# satisfait : `libapache2-mod-php`, qui tire apache2. Chez les locataires, tout s'installe
# en une transaction avec `php8.4-fpm`, et apt n'a pas a choisir. Au site, les paquets
# tiers viennent du cache du controleur, SEULS, avant le reste : `site-mon-01` portait un
# apache2 ecoutant sur le port 80 depuis le 2026-09-14, que rien n'utilisait.
- name: Installer PHP-FPM et nginx avant les paquets tiers (sinon apt choisit apache2)
ansible.builtin.apt:
name: "{{ serveur_icingaweb2_web_prealables }}"
state: present
update_cache: true
- name: Poser les paquets tiers depuis le cache du controleur
ansible.builtin.include_role:
name: paquets_tiers
@ -211,6 +226,27 @@
when: (serveur_icingaweb2_paquets
| difference((paquets_tiers_disponibles | default({})).keys() | list)) | length > 0
# LE RESTE D'UNE INSTALLATION DANS LE MAUVAIS ORDRE (voir plus haut). On ne le retire que
# si apt confirme qu'il part SEUL : `state: absent` emporterait aussi tout paquet qui en
# dependrait, et ce role n'a pas a decider de leur sort.
- name: Simuler le retrait d'apache2 (rien d'autre ne doit partir)
ansible.builtin.command:
argv: "{{ ['apt-get', '-s', 'purge'] + serveur_icingaweb2_apache_restes }}"
register: serveur_icingaweb2_apache_simulation
changed_when: false
check_mode: false
- name: Retirer apache2, que rien n'utilise
ansible.builtin.apt:
name: "{{ serveur_icingaweb2_apache_restes }}"
state: absent
purge: true
when:
- serveur_icingaweb2_apache_simulation.stdout_lines | select('match', '^(Purg|Remv) ') | list | length > 0
- (serveur_icingaweb2_apache_simulation.stdout_lines | select('match', '^(Purg|Remv) ')
| map('regex_replace', '^(?:Purg|Remv) (\\S+).*$', '\\1') | list)
is subset(serveur_icingaweb2_apache_restes)
- name: Ajouter www-data au groupe icingaweb2 (lecture de la config)
ansible.builtin.user:
name: www-data

View file

@ -1,8 +1,8 @@
; Géré par Set-OPS (rôle serveur_icingaweb2). Ne pas éditer à la main.
[global]
show_stacktraces = "0"
{% if serveur_icingaweb2_auth == 'db' %}
; LA CONSOLE RANGE SON PROPRE ETAT DANS SA PROPRE BASE (2026-09-15).
; LA CONSOLE RANGE SON PROPRE ETAT DANS SA PROPRE BASE (2026-09-15 au site, 2026-10-03
; partout : les locataires en SSO etaient restes en `ini`, avec la premiere erreur ci-dessous).
;
; `config_backend = "ini"` laissait les preferences en fichiers, et c'etait tenable — mais
; le cadre de MIGRATION d'Icinga Web 2, lui, veut une instance de base quoi qu'il arrive.
@ -19,9 +19,6 @@ show_stacktraces = "0"
; d'un navigateur a l'autre, ce que des fichiers locaux ne font pas.
config_backend = "db"
config_resource = "icingaweb_db"
{% else %}
config_backend = "ini"
{% endif %}
[logging]
log = "syslog"

View file

@ -18,11 +18,10 @@ charset = "UTF8"
ssl_mode = "{{ serveur_icingaweb2_db_sslmode }}"
ssl_ca = "{{ serveur_icingaweb2_db_sslrootcert }}"
{% endif %}
{% if serveur_icingaweb2_auth == 'db' %}
; LA BASE DES COMPTES DE LA CONSOLE — distincte d'`icingadb`, qui est celle du MOTEUR.
; Meler les deux ferait disparaitre les comptes au premier passage des migrations du
; moteur, sans que personne n'ait touche aux comptes.
; LA BASE DE LA CONSOLE — distincte d'`icingadb`, qui est celle du MOTEUR. Dans tous les
; modes : comptes en mode `db`, preferences et migrations partout. Meler les deux bases
; ferait disparaitre les comptes au premier passage des migrations du moteur.
[icingaweb_db]
type = "db"
db = "pgsql"
@ -36,7 +35,6 @@ charset = "UTF8"
ssl_mode = "{{ serveur_icingaweb2_db_sslmode }}"
ssl_ca = "{{ serveur_icingaweb2_db_sslrootcert }}"
{% endif %}
{% endif %}
{% if serveur_icingaweb2_auth not in ('db',) %}
[icingaweb_ldap]

View file

@ -16,6 +16,18 @@
replace: '\1# server_tokens gere par Set-OPS (conf.d/99-setops.conf)'
notify: Valider et recharger nginx
# MEME CAS POUR `ssl_protocols` (2026-10-03), mais sans echec : nginx ACCEPTE la directive
# deux fois au meme niveau, ajoute les valeurs, et avertit a chaque rechargement —
# `duplicate value "TLSv1.3" in /etc/nginx/conf.d/99-setops.conf:3`, releve dans le journal
# de `site-edge-01`. Un avertissement repete est un avertissement qu'on cesse de lire ; et
# la politique TLS doit avoir UNE source, la notre.
- name: Neutraliser le ssl_protocols par defaut de Debian
ansible.builtin.replace:
path: /etc/nginx/nginx.conf
regexp: '^(\s*)ssl_protocols\s+[^;]+;.*$'
replace: '\1# ssl_protocols gere par Set-OPS (conf.d/99-setops.conf)'
notify: Valider et recharger nginx
- name: Deployer le durcissement de base nginx
ansible.builtin.template:
src: 99-setops.conf.j2

View file

@ -962,7 +962,19 @@ def inventaire() -> dict:
# sert pour l'identite de noeud du cluster.
**({"client_journal_loki_url":
f"http://{_ip_loki}:3100/loki/api/v1/push"} if _ip_loki else {}),
**communes,
# NI LE CACHE DU SITE NI SES AMORCAGES (2026-10-03). `communes` le donne a toute machine du
# site ; un hyperviseur ne l'atteint pas — mesure depuis asgard :
# `10.37.33.21:3142` injoignable, par la frontiere comme tout le reste. Avec
# `artefacts_amorcage`, `client_journal` aurait reecrit sa source Grafana en
# `http://` par ce mandataire, et apt aurait perdu le depot. Sans lui, la
# source reste `https://apt.grafana.com`, celle qui marche. Le depot de
# binaires passe par le meme cache : meme raison.
#
# `dns_amorcage` part avec eux, son jumeau (P79) : les deux amorcent la PREMIERE
# mise en route d'une VM nee du gabarit, et un hyperviseur n'en nait pas. Aucun
# role qu'il recoit ne le lit.
**{k: v for k, v in communes.items()
if k not in ("artefacts_amorcage", "setops_depot_binaires", "dns_amorcage")},
}
groupes.setdefault("hyperviseurs", []).append(_nom)
# ON TIRE, ON NE POUSSE PAS — ET C'EST LA ROUTE GELEE QUI LE DECIDE (2026-09-10).