Application de ce que l entree precedente avait prepare : 49 regles creees,
5 retirees. Cibles Prometheus 2/8 -> 8/8, hotes dans Loki 1/7 -> 7/7.
Puis correction de ce que l application a revele. Cinq defauts, tous de la
meme famille : un reglage qui ne suit pas l interrupteur dont il depend.
- la sonde de Loki interrogeait en HTTPS un Loki servant en clair ;
- la sonde de la forge visait http:// sur un port 443 chiffre, puis
validait un certificat emis pour le FQDN en interrogeant 127.0.0.1 ;
- la sonde du runner comparait six depots a UNE branche quand le plan en
declare une par depot. Elle contredisait la declaration qu elle etait
censee verifier : elle n accusait pas la machine, elle s accusait
elle-meme.
Le tenant restait juste dans les cinq cas. C est le site, qui deploie ces
roles autrement, qui les a tous reveles d un coup.
La sonde des journaux, elle, avait deux defauts a la naissance. Elle criait
une perte deja reparee, faute de dire sur QUELLE fenetre - un ecart sans sa
fenetre n est pas une mesure, c est un nombre. Et elle alertait sur la
cicatrice plutot que sur la plaie : le compteur d Alloy est cumulatif, donc
six machines restaient orange pour toujours. Ce qui alerte est desormais la
CROISSANCE. En echange, un cas muet leve maintenant : zero envoi et zero
rejet n est pas la sante, c est un agent qui ne fait rien.
Enfin, le runner ne pouvait plus cloner le genome. Defaut PREEXISTANT,
revele par le redeploiement et non cause par lui - le diff des regles
generees le montre. generer_nftables(site=True) lisait le plan du TENANT :
derive se resolvait donc a 3000, le port que Forgejo ecoute derriere un
edge, alors que le plan du site dit 443 et explique pourquoi. La forge
ouvrait un port que personne n ecoute et laissait 443 ferme a toute la
flotte.
Mesure finale : Icinga 51/51, Prometheus 8/8, Loki 7/7, prouver 64 OK,
lint 0 defaut sur 82 fichiers. Controles negatifs sur des COPIES des
sondes : les quatre rendent CRITICAL.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
203 lines
11 KiB
YAML
203 lines
11 KiB
YAML
---
|
|
# LE POSTE D'EXPLOITATION — la machine depuis laquelle l'écosystème se reconstruit.
|
|
#
|
|
# Jusqu'ici, Set-OPS ne s'exécutait que depuis le poste de son mainteneur. L'écosystème
|
|
# détenait donc la recette (le génome, sur sa forge) sans que personne, chez lui, ne
|
|
# sache la lire à voix haute. `serveur_ops` est la cuisine : ansible, le génome cloné
|
|
# depuis SA PROPRE forge, et de quoi rejouer le moteur.
|
|
#
|
|
# CE QU'IL N'EMPORTE PAS, ET NE DOIT PAS EMPORTER : le mot de passe de la voûte. Il est
|
|
# saisi à l'exécution. Une machine qui détient à la fois le plan, l'accès SSH à la flotte
|
|
# et la clé des secrets n'a plus aucune profondeur : sa compromission est celle de tout
|
|
# l'écosystème. Voir README.md du rôle.
|
|
|
|
serveur_ops_utilisateur: "setops"
|
|
serveur_ops_racine: "/opt/setops"
|
|
serveur_ops_venv: "{{ serveur_ops_racine }}/venv"
|
|
|
|
# Paquets du poste. `make` parce que l'interface opérateur de Set-OPS est un Makefile ;
|
|
# `git` parce que le génome vit dans des dépôts ; `rsync` pour les transferts de fichiers
|
|
# d'Ansible sur les gros arbres.
|
|
serveur_ops_paquets:
|
|
- git
|
|
- make
|
|
- python3-venv
|
|
- python3-pip
|
|
- openssh-client
|
|
- rsync
|
|
- ca-certificates
|
|
|
|
# Ansible est ÉPINGLÉ sur la même famille que le poste du mainteneur (core 2.18) : un
|
|
# écosystème qui se reconstruit avec une version différente de celle qui l'a construit ne
|
|
# reproduit pas la même chose, et l'écart ne se voit qu'au premier échec.
|
|
serveur_ops_ansible: "ansible-core>=2.18,<2.19"
|
|
|
|
# --- D'OÙ VIENT LE GÉNOME ----------------------------------------------------
|
|
#
|
|
# DE SA PROPRE FORGE, pas de l'amont. C'est tout l'objet des miroirs : la forge de
|
|
# l'écosystème détient une copie vivante du génome, resynchronisée toutes les huit
|
|
# heures. Le poste d'exploitation lit CETTE copie — l'écosystème se reconstruit donc
|
|
# depuis lui-même, et non depuis son parent.
|
|
#
|
|
# Dérivé du groupe `serveur_forgejo` de l'inventaire. Surchargeable pour une forge
|
|
# externe au plan.
|
|
serveur_ops_forge_hote: >-
|
|
{{ (groups['serveur_forgejo'] | default([]) | first | default('')) }}
|
|
serveur_ops_forge_url: >-
|
|
{{ serveur_ops_forge_amont if (serveur_ops_forge_externe | bool)
|
|
else 'https://forge.' ~ domaine_interne }}
|
|
serveur_ops_forge_organisation: "genome"
|
|
|
|
# --- FILIATION, MUTUALISATION, ÉMANCIPATION ----------------------------------
|
|
#
|
|
# Un écosystème naît FILS : il n'héberge pas encore tout ce dont il a besoin, et emprunte
|
|
# à son hôte. Il peut rester ainsi — la MUTUALISATION est un état durable et choisi, pas
|
|
# une infirmité — ou devenir AUTOPORTANT le jour où il le décide.
|
|
#
|
|
# La forge est le premier service concerné. `false` : le génome est lu sur la forge de
|
|
# CET écosystème (état émancipé). `true` : il est lu chez l'hôte (état natal, ou
|
|
# mutualisation assumée) — et l'écosystème n'a alors pas besoin d'héberger de forge.
|
|
#
|
|
# CE N'EST PAS UN RABAIS. Un modèle sans forge n'est pas un modèle amputé : c'est un
|
|
# écosystème au premier âge, dont le runner lit le génome chez son parent. L'émancipation
|
|
# consiste à lever ce drapeau, déployer sa propre forge, et PROUVER que le lien est coupé.
|
|
#
|
|
# Cette variable était citée par `docs/dependances-groupes.yml` et par le README sans
|
|
# exister nulle part (constaté le 2026-08-24) : une exemption qui ne pouvait jamais
|
|
# s'appliquer, donc un écosystème sans forge que le registre refusait quand même.
|
|
serveur_ops_forge_externe: false
|
|
serveur_ops_forge_amont: ""
|
|
|
|
# --- LA CONFIANCE ENVERS UNE FORGE D'UN AUTRE TENANT -------------------------
|
|
#
|
|
# Lire le genome chez un voisin suppose de faire confiance a SON autorite — or chaque
|
|
# ecosysteme a la sienne, et `client_pki` n'etablit la confiance que dans celle du tenant.
|
|
#
|
|
# ON NE POSE PAS CETTE RACINE DANS LE MAGASIN SYSTEME. L'y mettre ferait confiance a cette
|
|
# AC pour TOUT ce que la machine contacte, alors qu'on ne lui demande qu'une chose : servir
|
|
# un depot git. `git config http.<url>.sslCAInfo` limite la confiance a CETTE url — une
|
|
# porte, pas un trousseau.
|
|
#
|
|
# Chemin d'un certificat racine PUBLIC, sur le controleur. Vide = rien a poser (forge du
|
|
# meme ecosysteme, ou AC publiquement reconnue).
|
|
serveur_ops_forge_amont_ac: ""
|
|
serveur_ops_forge_amont_ac_depot: "{{ serveur_ops_racine }}/.ac-amont.crt"
|
|
|
|
# Les dépôts du génome, et où ils atterrissent. Les noms sont ceux que la forge porte
|
|
# (minuscules) ; les destinations reprennent la disposition du poste du mainteneur, où
|
|
# le moteur et les instances sont des dossiers frères — c'est cette fraternité que
|
|
# `scripts/instances.py` découvre.
|
|
#
|
|
# AUCUN DÉFAUT NE NOMME UN ÉCOSYSTÈME PARTICULIER (2026-08-24).
|
|
#
|
|
# La première version listait ici `ops-patient0`. Tout écosystème qui aurait déployé un
|
|
# poste sans déclarer ses propres dépôts aurait donc cloné le génome de **patient 0** —
|
|
# silencieusement, et en croyant piloter le sien. C'est la faute que ce dépôt combat sous
|
|
# tous ses déguisements : un défaut plausible qui rend un résultat faux sans rien dire.
|
|
#
|
|
# Le moteur est le seul défaut légitime : il est le même pour tous. Le dépôt d'instance,
|
|
# lui, est propre à l'écosystème et DOIT être déclaré — la tâche d'exigence refuse de
|
|
# poursuivre sans lui.
|
|
serveur_ops_depots:
|
|
- { depot: "set-ops-public", dest: "Set-OPS-public", role: "moteur" }
|
|
|
|
# --- LES DEUX SYMLINKS (D-80) ------------------------------------------------
|
|
#
|
|
# `instance` dit QUEL tenant on pilote.
|
|
# `underlay.yml` dit SUR QUELLE FABRIC il repose.
|
|
#
|
|
# Les deux sont independants, et le second manquait. Un poste qui ne connaît que son
|
|
# plan sait CONFIGURER des machines existantes ; il ne sait pas en CREER. Placer une VM
|
|
# demande de savoir sur quel nœud, quel stockage, quel pont — c'est-à-dire l'underlay.
|
|
# Sans lui, le poste est un exploitant, pas un géniteur.
|
|
#
|
|
# CE QUE LE POSTE N'AURA PAS POUR AUTANT : `underlay.vault.yml`, qui porte les secrets du
|
|
# monde physique. Il est hors dépôt (gitignore), donc absent du génome. Le poste lit la
|
|
# CARTE de la fabric, pas ses clés.
|
|
|
|
# Le dossier de l'instance pilotée, cible du lien `instance`. Sans valeur, le rôle
|
|
# refuse : un poste qui ne sait pas quel tenant il pilote n'a rien à piloter.
|
|
serveur_ops_instance: ""
|
|
# Vide = le poste ne pose pas de lien `underlay.yml` (il configure, il n'engendre pas).
|
|
serveur_ops_underlay: ""
|
|
|
|
# --- CLÉ SSH DU POSTE --------------------------------------------------------
|
|
#
|
|
# Le poste a besoin d'atteindre toute la flotte en SSH. Il se fabrique donc sa PROPRE
|
|
# paire, distincte de celle du mainteneur : deux exploitants, deux clés, deux révocations
|
|
# possibles. La clé publique produite doit être autorisée sur la flotte — le rôle
|
|
# l'affiche et l'écrit dans un fichier prévu pour être repris au plan (voir README).
|
|
serveur_ops_cle_type: "ed25519"
|
|
serveur_ops_cle_generer: true
|
|
|
|
# --- CACHE DES COLLECTIONS (côté CONTRÔLEUR) ---------------------------------
|
|
#
|
|
# Le poste ne va PAS chercher ses collections sur galaxy.ansible.com : la frontière ne
|
|
# laisse pas passer ce flux, et ouvrir une règle vers un serveur étranger pour qu'un
|
|
# écosystème sache se reconstruire serait la mauvaise réponse. Le contrôleur télécharge
|
|
# une fois dans son cache, puis pousse par SSH — même idiome que `serveur_forgejo`
|
|
# devant son propre tiers.
|
|
#
|
|
# Effet de bord recherché : le poste devient déployable **hors ligne**.
|
|
# LE CACHE SUIT LE CONTENU DE `requirements.yml`, PAS SON EXISTENCE (2026-08-24).
|
|
#
|
|
# La premiere version gardait le telechargement par `creates: <cache>/requirements.yml` :
|
|
# une collection AJOUTEE au fichier n'aurait jamais ete recuperee, puisque le temoin
|
|
# existait deja. Le runner serait reste en retard sur le moteur, en silence -- exactement
|
|
# le defaut qui a fait qu'`ansible.posix` manquait sans que rien ne le dise.
|
|
#
|
|
# L'empreinte du fichier entre dans le chemin : changer une ligne de `requirements.yml`
|
|
# cree un cache neuf, donc un telechargement. Les anciens caches restent (ils ne genent
|
|
# pas) et documentent ce que les versions precedentes exigeaient.
|
|
serveur_ops_cache_collections: >-
|
|
{{ (setops_cache_artefacts | default(lookup('env', 'HOME') + '/.cache/setops')) }}/collections-{{
|
|
lookup('file', serveur_ops_requirements_controleur) | hash('sha1') | truncate(12, true, '') }}
|
|
serveur_ops_requirements_controleur: "{{ playbook_dir }}/../../requirements.yml"
|
|
|
|
# --- LA ROUE HORS-LIGNE : PYTHON PAR LE MEME CHEMIN QUE LES COLLECTIONS -------
|
|
#
|
|
# LA DERNIERE DEPENDANCE QUI SORTAIT PAR ELLE-MEME (2026-08-28). Les collections
|
|
# Ansible suivent depuis longtemps l'idiome de ce depot — « le CONTROLEUR telecharge une
|
|
# fois, puis pousse par SSH ; aucun flux nouveau depuis la cible, et l'artefact devient
|
|
# deployable hors ligne, ce qui est le sens meme d'une lignee autonome ». `pip`, lui,
|
|
# sortait encore vers PyPI, en HTTPS et par nom de domaine.
|
|
#
|
|
# Un tenant n'a NI DNS SORTANT NI 443 vers l'Internet, et c'est voulu. Mesure sur ops-01,
|
|
# premiere machine de la reconstruction de Chezlepro :
|
|
#
|
|
# ERROR: Could not find a version that satisfies the requirement ansible-core<2.19
|
|
# Echec temporaire dans la resolution du nom
|
|
#
|
|
# POURQUOI PAS LE PAQUET DEBIAN : trixie propose `ansible-core 2.19.4`, hors de la plage
|
|
# epinglee. Y aller demanderait de deplacer l'epinglage vers une version majeure aux
|
|
# changements de gabarits connus, trois jours apres l'avoir pose pour cause de
|
|
# portabilite. Ce n'est pas le moment, et ce n'est pas ce probleme-ci.
|
|
#
|
|
# L'EMPREINTE DES SOURCES ENTRE DANS LE CHEMIN, comme pour les collections : changer une
|
|
# ligne de `requirements-python.txt` ou l'epinglage d'ansible-core cree un cache neuf,
|
|
# donc un telechargement. Les anciens restent et documentent ce qu'exigeaient les
|
|
# versions precedentes.
|
|
serveur_ops_roues_source: "{{ playbook_dir }}/../../requirements-python.txt"
|
|
serveur_ops_cache_roues: >-
|
|
{{ (setops_cache_artefacts | default(lookup('env', 'HOME') + '/.cache/setops')) }}/roues-{{
|
|
(lookup('file', serveur_ops_roues_source) ~ serveur_ops_ansible) | hash('sha1') | truncate(12, true, '') }}
|
|
serveur_ops_roues_depot: "{{ serveur_ops_racine }}/.roues-hors-ligne"
|
|
|
|
# --- Sonde de supervision -----------------------------------------------------------
|
|
serveur_ops_sonde_racine: /opt/setops
|
|
serveur_ops_sonde_venv: /opt/setops/venv
|
|
# LA BRANCHE ATTENDUE SE DECLARE PAR DEPOT, PAS UNE FOIS POUR TOUS (2026-09-10).
|
|
#
|
|
# `serveur_ops_depots` porte deja un `branche:` par entree — le plan du site declare
|
|
# `master` pour `set-ops-modeles` et `ops-technolibre`. La sonde comparait pourtant les six
|
|
# depots a une valeur unique, et rapportait donc une divergence CRITIQUE sur deux depots
|
|
# posos EXACTEMENT la ou le plan les veut.
|
|
#
|
|
# Une sonde qui contredit la declaration qu'elle est censee verifier n'accuse pas la
|
|
# machine : elle s'accuse elle-meme. On derive la table de ce que le plan dit, et
|
|
# `serveur_ops_sonde_branche` ne sert plus que de defaut aux entrees qui se taisent.
|
|
serveur_ops_sonde_branche: main
|
|
serveur_ops_sonde_branches: >-
|
|
{{ dict(serveur_ops_depots | map(attribute='dest')
|
|
| zip(serveur_ops_depots
|
|
| map(attribute='branche', default=serveur_ops_sonde_branche))) }}
|