From a1997774be7daa45679a2194e3737e2afbd9b9f9 Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Mon, 24 Aug 2026 17:51:29 -0400 Subject: [PATCH] frontiere : appliquee, et la contrainte de nommage d'OPNsense consignee MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Devis clos : 0 a creer, 0 a retirer, 65 inchange + 15 routes. Flotte verifiee apres coup -- DNS interne, Internet et apt sans erreur sur les cinq hotes. OPNsense refuse un alias de 32 caracteres ou plus. La contrainte n'etait ecrite nulle part et se manifestait a l'APPLICATION, pas au devis. nom_alias abrege desormais (SERVEUR_ -> SRV_, comme le pare-feu est-ouest) et REFUSE bruyamment si le nom deborde encore : un devis qui promet un objet que la cible rejettera n'est pas un devis. Le role devient serveur_cache_site. « RIEN N'EST APPLIQUE » signifiait « pas encore recharge », pas « rien ecrit » : les objets etaient dans la config, le pare-feu en marche les ignorait. J'avais lu le message a l'envers. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 37 +++++++++++++++++++ docs/catalogue-services.md | 2 +- roles/serveur_artefacts/meta/flux.yml | 2 +- .../README.md | 2 +- .../defaults/main.yml | 15 +++++++- .../meta/authentification.yml | 0 .../meta/empreinte.yml | 0 .../meta/flux.yml | 0 .../tasks/main.yml | 2 +- scripts/devis_opnsense.py | 35 +++++++++++++++++- 10 files changed, 87 insertions(+), 8 deletions(-) rename roles/{serveur_artefacts_site => serveur_cache_site}/README.md (98%) rename roles/{serveur_artefacts_site => serveur_cache_site}/defaults/main.yml (53%) rename roles/{serveur_artefacts_site => serveur_cache_site}/meta/authentification.yml (100%) rename roles/{serveur_artefacts_site => serveur_cache_site}/meta/empreinte.yml (100%) rename roles/{serveur_artefacts_site => serveur_cache_site}/meta/flux.yml (100%) rename roles/{serveur_artefacts_site => serveur_cache_site}/tasks/main.yml (92%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f7ff58..be52938 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,42 @@ # CHANGELOG — Set-OPS +## 2026-08-24 — La frontière appliquée, et une contrainte qui n'était écrite nulle part + +`a creer : 0 | a retirer : 0 | inchange : 65 + 15 routes` — le devis est clos. La flotte de +patient 0 vérifiée après coup : DNS interne, Internet et `apt` sans erreur sur les cinq. + +### OPNsense refuse un nom d'alias de 32 caractères ou plus + +Le schéma `SETOPS__` ne laisse que 17 caractères au rôle : + +``` +SETOPS_PATI29_SERVEUR_ARTEFACTS_SITE 36 refusé +SETOPS_PATI29_SRV_ARTEFACTS_SITE 32 refusé encore, d'un caractère +SETOPS_PATI29_SRV_CACHE_SITE 28 passe +``` + +La contrainte n'était **écrite nulle part**, et elle s'est manifestée à l'**application**, +pas au devis. Un devis qui promet un objet que la cible rejettera n'est pas un devis : +`nom_alias` abrège désormais (`SERVEUR_` → `SRV_`, comme le pare-feu est-ouest) et **refuse +bruyamment** si le nom déborde encore. Le rôle est renommé `serveur_cache_site`. + +### « RIEN N'EST APPLIQUÉ » voulait dire autre chose que ce que j'ai lu + +Le premier essai a signalé cinq échecs et affiché *« RIEN N'EST APPLIQUÉ, la config reste +en attente »*. J'ai d'abord compris que le boîtier n'avait pas été touché. Le devis +suivant disait autre chose : 62 objets inchangés au lieu de 49. + +Les objets **étaient bien écrits** dans la configuration ; c'est le **rechargement** qui +n'avait pas eu lieu — « en attente » au sens d'OPNsense. Une différence qui compte : entre +les deux, la configuration contenait des objets que le pare-feu en marche ignorait encore. + +### Et la garde du boîtier injoignable a servi + +Une tentative a échoué sur une poignée TLS expirée — le premier paquet après une pause, +mesuré tout au long de la journée. Le code a **refusé de lire un boîtier injoignable comme +un boîtier vide**, au lieu de proposer de tout recréer. C'est exactement ce pour quoi cette +garde avait été écrite. + ## 2026-08-24 — Le chaînage des caches, et la règle qui appartient au SITE Le runner de SITE est le seul à toucher la configuration système du matériel. Sa diff --git a/docs/catalogue-services.md b/docs/catalogue-services.md index 53ff8c1..a73dc85 100644 --- a/docs/catalogue-services.md +++ b/docs/catalogue-services.md @@ -53,7 +53,7 @@ Ce que la reconstruction couvre, par capacité : | Source d'artefacts | `serveur_artefacts`, `client_artefacts` | cache apt de l'ecosysteme (apt-cacher-ng) : les paquets viennent de chez soi, pas de six serveurs etrangers — **mode hors ligne** pour prouver ce que le cache detient vraiment | | Runner de site | `serveur_ops_site` | le pouvoir de **materialiser** : creer et detruire des VM sur la fabric. Detient la voute du SITE, chiffree, et n'entre JAMAIS chez un tenant — reserve a l'ecosysteme de l'hebergeur | | Resolution | `serveur_resolveur`, `client_resolveur` | UN resolveur recursif par tenant (Unbound), qui recurse depuis la racine et delegue la zone souveraine a PowerDNS. Remplace les N demons locaux d'avant le 2026-08-24 | -| Cache du site | `serveur_artefacts_site` | designe LE cache que les ecosystemes voisins prennent comme amont : Debian telecharge une fois pour toute la fabric, et le cache ne voit que des requetes agregees. Reserve a l'ecosysteme de l'hebergeur | +| Cache du site | `serveur_cache_site` | designe LE cache que les ecosystemes voisins prennent comme amont : Debian telecharge une fois pour toute la fabric, et le cache ne voit que des requetes agregees. Reserve a l'ecosysteme de l'hebergeur | | Agents de flotte | `client_metrique`, `client_journal`, `client_smtp` | collecte et relais sur toute la flotte | *Rôles retirés (2026-07-04, supersédés ou hors conception)* : `serveur_sendmail` diff --git a/roles/serveur_artefacts/meta/flux.yml b/roles/serveur_artefacts/meta/flux.yml index ee30a2e..7c342cb 100644 --- a/roles/serveur_artefacts/meta/flux.yml +++ b/roles/serveur_artefacts/meta/flux.yml @@ -19,7 +19,7 @@ flux: # LE CHAINAGE, cote consommateur. Un seul port, et seulement vers un CACHE — jamais # d'une VM vers le cache d'un autre tenant. # - # L'INGRESS CORRESPONDANT N'EST PAS ICI : il appartient a `serveur_artefacts_site`, le + # L'INGRESS CORRESPONDANT N'EST PAS ICI : il appartient a `serveur_cache_site`, le # cache designe du site. Sans cette separation, chaque cache aurait accepte chaque # autre — un maillage complet, visible au devis du 2026-08-24. - sens: egress diff --git a/roles/serveur_artefacts_site/README.md b/roles/serveur_cache_site/README.md similarity index 98% rename from roles/serveur_artefacts_site/README.md rename to roles/serveur_cache_site/README.md index 8f56054..a96aa2f 100644 --- a/roles/serveur_artefacts_site/README.md +++ b/roles/serveur_cache_site/README.md @@ -1,4 +1,4 @@ -# serveur_artefacts_site +# serveur_cache_site **Le cache d'artefacts du SITE** : celui que les écosystèmes voisins prennent comme amont, pour que Debian ne soit téléchargé qu'une fois pour toute la fabric. diff --git a/roles/serveur_artefacts_site/defaults/main.yml b/roles/serveur_cache_site/defaults/main.yml similarity index 53% rename from roles/serveur_artefacts_site/defaults/main.yml rename to roles/serveur_cache_site/defaults/main.yml index 172c0a8..0fa72c5 100644 --- a/roles/serveur_artefacts_site/defaults/main.yml +++ b/roles/serveur_cache_site/defaults/main.yml @@ -1,5 +1,16 @@ --- -# LE CACHE D'ARTEFACTS DU SITE — celui que les autres écosystèmes prennent comme amont. +# LE CACHE D'ARTEFACTS DU SITE — nomme `cache_site` et non `artefacts_site`. +# +# POURQUOI CE NOM COURT (2026-08-24). OPNsense refuse un nom d'alias de 32 caracteres ou +# plus, et le schema `SETOPS__` ne laisse que 17 caracteres au role : +# +# SETOPS_PATI29_SERVEUR_ARTEFACTS_SITE 36 refuse a l'application +# SETOPS_PATI29_SRV_ARTEFACTS_SITE 32 refuse encore, d'un caractere +# SETOPS_PATI29_SRV_CACHE_SITE 28 passe +# +# La contrainte n'etait ecrite nulle part et s'est manifestee a l'APPLICATION, pas au +# devis. Elle l'est desormais, et le devis REFUSE plutot que de promettre un objet que la +# cible rejettera. — celui que les autres écosystèmes prennent comme amont. # # Même patron que `serveur_ops_site` : le rôle n'installe rien de plus, il **attribue une # responsabilité** et la rend lisible dans le plan. `serveur_artefacts` fait le travail ; @@ -14,4 +25,4 @@ # tenant ordinaire qui le déclarerait s'offrirait à servir ses voisins sans y être invité. # # Le cache du site va DIRECTEMENT chez Debian : il est le bout de la chaîne. -serveur_artefacts_site_amont_attendu: "" +serveur_cache_site_amont_attendu: "" diff --git a/roles/serveur_artefacts_site/meta/authentification.yml b/roles/serveur_cache_site/meta/authentification.yml similarity index 100% rename from roles/serveur_artefacts_site/meta/authentification.yml rename to roles/serveur_cache_site/meta/authentification.yml diff --git a/roles/serveur_artefacts_site/meta/empreinte.yml b/roles/serveur_cache_site/meta/empreinte.yml similarity index 100% rename from roles/serveur_artefacts_site/meta/empreinte.yml rename to roles/serveur_cache_site/meta/empreinte.yml diff --git a/roles/serveur_artefacts_site/meta/flux.yml b/roles/serveur_cache_site/meta/flux.yml similarity index 100% rename from roles/serveur_artefacts_site/meta/flux.yml rename to roles/serveur_cache_site/meta/flux.yml diff --git a/roles/serveur_artefacts_site/tasks/main.yml b/roles/serveur_cache_site/tasks/main.yml similarity index 92% rename from roles/serveur_artefacts_site/tasks/main.yml rename to roles/serveur_cache_site/tasks/main.yml index 4a8651f..93a108a 100644 --- a/roles/serveur_artefacts_site/tasks/main.yml +++ b/roles/serveur_cache_site/tasks/main.yml @@ -7,7 +7,7 @@ - name: Exiger que le cache du site aille directement à l'amont ansible.builtin.assert: that: - - (serveur_artefacts_amont | default('')) == serveur_artefacts_site_amont_attendu + - (serveur_artefacts_amont | default('')) == serveur_cache_site_amont_attendu fail_msg: >- Ce cache est déclaré cache du SITE : il doit aller directement chez Debian. `serveur_artefacts_amont` doit rester vide — sinon les caches se chaînent en boucle. diff --git a/scripts/devis_opnsense.py b/scripts/devis_opnsense.py index 7d278c8..de8af83 100644 --- a/scripts/devis_opnsense.py +++ b/scripts/devis_opnsense.py @@ -261,9 +261,40 @@ def cibles_par_role(role: str, inventaire: Path) -> list[str]: return sorted({ips[h] for h in _hotes_du_groupe(data, role) if h in ips}) +# OPNsense refuse un nom d'alias de 32 caracteres ou plus. La contrainte n'etait nulle +# part, et elle s'est manifestee a l'APPLICATION — pas au devis : +# +# SETOPS_PATI29_SERVEUR_ARTEFACTS_SITE (36) +# « The name must start with a letter [...] and be less than 32 characters » +# +# L'outil a bien refuse d'appliquer quoi que ce soit, mais le devis, lui, annoncait un +# objet impossible. Un devis qui promet ce que la cible refusera n'est pas un devis. +LONGUEUR_MAX_ALIAS = 31 + +# Meme abreviation que le pare-feu est-ouest (`devis_proxmox_fw` : serveur_ -> srv-), pour +# qu'un seul schema de lecture serve aux deux couches. +ABREGES = (("SERVEUR_", "SRV_"), ("CLIENT_", "CLI_")) + + def nom_alias(role: str, etiquette: str) -> str: - """Alias d'hotes, PREFIXE par le tenant : deux tenants ont chacun leur nginx.""" - return f"SETOPS_{etiquette}_{role.upper()}" + """Alias d'hotes, PREFIXE par le tenant : deux tenants ont chacun leur nginx. + + Abrege SEULEMENT si le nom complet depasse — un nom court reste lisible tel quel, et + la flotte existante n'est pas renommee pour la commodite d'un seul role. + """ + nom = f"SETOPS_{etiquette}_{role.upper()}" + if len(nom) <= LONGUEUR_MAX_ALIAS: + return nom + court = role.upper() + for long, bref in ABREGES: + court = court.replace(long, bref) + nom = f"SETOPS_{etiquette}_{court}" + if len(nom) > LONGUEUR_MAX_ALIAS: + # Mieux vaut un devis qui refuse qu'un devis qui promet l'impossible. + raise SystemExit( + f"Nom d'alias trop long pour OPNsense ({len(nom)} > {LONGUEUR_MAX_ALIAS}) : {nom}.\n" + f"Raccourcir le nom du role `{role}`.") + return nom def _instance_active() -> tuple[str, dict] | tuple[None, None]: