frontiere : appliquee, et la contrainte de nommage d'OPNsense consignee
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 <noreply@anthropic.com>
This commit is contained in:
parent
fa7e656844
commit
a1997774be
10 changed files with 87 additions and 8 deletions
37
CHANGELOG.md
37
CHANGELOG.md
|
|
@ -1,5 +1,42 @@
|
||||||
# CHANGELOG — Set-OPS
|
# 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_<tenant>_<ROLE>` 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
|
## 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
|
Le runner de SITE est le seul à toucher la configuration système du matériel. Sa
|
||||||
|
|
|
||||||
|
|
@ -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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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`
|
*Rôles retirés (2026-07-04, supersédés ou hors conception)* : `serveur_sendmail`
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ flux:
|
||||||
# LE CHAINAGE, cote consommateur. Un seul port, et seulement vers un CACHE — jamais
|
# LE CHAINAGE, cote consommateur. Un seul port, et seulement vers un CACHE — jamais
|
||||||
# d'une VM vers le cache d'un autre tenant.
|
# 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
|
# cache designe du site. Sans cette separation, chaque cache aurait accepte chaque
|
||||||
# autre — un maillage complet, visible au devis du 2026-08-24.
|
# autre — un maillage complet, visible au devis du 2026-08-24.
|
||||||
- sens: egress
|
- sens: egress
|
||||||
|
|
|
||||||
|
|
@ -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,
|
**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.
|
pour que Debian ne soit téléchargé qu'une fois pour toute la fabric.
|
||||||
|
|
@ -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_<tenant>_<ROLE>` 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
|
# 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 ;
|
# 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é.
|
# 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.
|
# 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: ""
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
- name: Exiger que le cache du site aille directement à l'amont
|
- name: Exiger que le cache du site aille directement à l'amont
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
- (serveur_artefacts_amont | default('')) == serveur_artefacts_site_amont_attendu
|
- (serveur_artefacts_amont | default('')) == serveur_cache_site_amont_attendu
|
||||||
fail_msg: >-
|
fail_msg: >-
|
||||||
Ce cache est déclaré cache du SITE : il doit aller directement chez Debian.
|
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.
|
`serveur_artefacts_amont` doit rester vide — sinon les caches se chaînent en boucle.
|
||||||
|
|
@ -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})
|
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:
|
def nom_alias(role: str, etiquette: str) -> str:
|
||||||
"""Alias d'hotes, PREFIXE par le tenant : deux tenants ont chacun leur nginx."""
|
"""Alias d'hotes, PREFIXE par le tenant : deux tenants ont chacun leur nginx.
|
||||||
return f"SETOPS_{etiquette}_{role.upper()}"
|
|
||||||
|
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]:
|
def _instance_active() -> tuple[str, dict] | tuple[None, None]:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue