dns : le site resout chez lui, et le socle cesse de le defaire
Mise au point de l'exploitant : la frontiere n'a pas de service DNS actif et gere. Un
Unbound y tourne — il repondait, ce qui m'a induit en erreur — mais un processus n'est pas
un service. La delegation de zone que j'y avais posee est retiree.
Et le site a son propre DNS : `dns_amorcage` pointait encore sur la frontiere, valeur d'un
moment ou site-dns-01 n'existait pas. Elle vaut desormais 10.0.3.51.
Deux defauts revelés au passage :
- devis_opnsense lisait encore underlay.machines(), vide depuis le deplacement du plan.
Il proposait de RETIRER 36 objets — tous les alias et regles du site. Aucune preuve ne
couvre ce devis : c'est le plan avant application qui l'a attrape.
- le socle defaisait la bascule de client_resolveur a chaque passage. Sa garde ne
protegeait que l'hote du resolveur (127.0.0.1). Invisible chez un tenant, ou
dns_amorcage vaut l'adresse du resolveur : la coincidence masquait le defaut. Ca ne
s'est vu qu'en retirant la regle de pare-feu — les cinq machines ont perdu la
resolution d'un coup.
L'amorcage ne s'applique plus que si rien de sense n'est en place. Verifie : socle
`changed=0`, les cinq machines restent sur 10.0.3.51.
42 preuves vertes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
b60946f19f
commit
ea67b75a15
4 changed files with 106 additions and 5 deletions
38
CHANGELOG.md
38
CHANGELOG.md
|
|
@ -1,5 +1,43 @@
|
|||
# CHANGELOG — Set-OPS
|
||||
|
||||
## 2026-08-25 — Le site résout chez lui, et le socle cesse de le défaire
|
||||
|
||||
**Mise au point de l'exploitant** : la frontière OPNsense n'a pas de service DNS actif et
|
||||
géré. Un Unbound y *tourne* — il répondait, ce qui m'a induit en erreur — mais un
|
||||
processus n'est pas un service. La délégation de zone que j'y avais posée est retirée :
|
||||
on ne règle pas ce que rien ne déclare ni ne prouve.
|
||||
|
||||
**Et surtout, le site a son propre DNS.** `dns_amorcage` pointait encore sur la frontière
|
||||
(`10.0.3.1`), valeur d'un moment où `site-dns-01` n'existait pas. Elle a survécu à sa
|
||||
raison d'être. Il vaut désormais `10.0.3.51`.
|
||||
|
||||
*Reste un cas étroit, nommé plutôt que résolu : la machine qui PORTE le DNS ne peut pas
|
||||
résoudre chez elle avant de l'avoir installé. Un site bâti depuis zéro doit créer et
|
||||
déployer `site-dns-01` en premier.*
|
||||
|
||||
### Le devis de la frontière ne voyait plus le site
|
||||
|
||||
En déplaçant le plan hors de `underlay.yml`, j'ai vidé `machines()` sans rebrancher
|
||||
`devis_opnsense`. Il proposait de **retirer 36 objets** — tous les alias et toutes les
|
||||
règles du site. Aucune preuve ne couvre le devis de la frontière du site, donc
|
||||
`make prouver` restait vert : **c'est le plan avant application qui l'a attrapé**, et rien
|
||||
d'autre ne l'aurait fait.
|
||||
|
||||
### Le socle défaisait la bascule du résolveur
|
||||
|
||||
Sa garde ne protégeait que l'hôte du résolveur (`127.0.0.1`). Partout ailleurs il
|
||||
réécrivait `/etc/resolv.conf` avec `dns_amorcage`, annulant `client_resolveur` à chaque
|
||||
passage.
|
||||
|
||||
Invisible chez un tenant : `dns_amorcage` y vaut l'adresse du résolveur du tenant, donc
|
||||
réécrire remettait la même valeur. **La coïncidence masquait le défaut.** Sur le site les
|
||||
deux ont divergé quelques heures — et ça ne s'est vu qu'en retirant la règle de pare-feu
|
||||
vers la frontière : les cinq machines ont perdu la résolution d'un coup.
|
||||
|
||||
L'amorçage ne s'applique plus que si rien de sensé n'est en place : ni la loopback, ni un
|
||||
résolveur déclaré de l'écosystème. Vérifié — le socle repasse `changed=0` et les cinq
|
||||
machines restent sur `10.0.3.51`.
|
||||
|
||||
## 2026-08-25 — Le génome vit sur la forge du SITE
|
||||
|
||||
Six dépôts, 606 commits, chaque tête confrontée entre le poste et la forge : **identique**.
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
| P21 | Federation : aucun index en collision | AFF-102 | ✅ OK | Federation coherente : 3 instance(s) federee(s), aucun index en collision. |
|
||||
| P22 | Plan de recette a jour (genere du wiki) | AFF-002 | ✅ OK | Plan de recette à jour (22 sections). |
|
||||
| P23 | Underlay sans collision avec la plage tenant | AFF-103 | ✅ OK | Underlay conforme : 8 reseau(x), aucune collision avec la plage tenant. |
|
||||
| P24 | Frontiere nord/sud : acces d'administration declare | AFF-104 | ✅ OK | CONFORME : frontiere nord/sud, 65 regles, 15 routes, admin=10.0.0.0/24,10.17.0.0/24,10.29.19.41/32,192.168.254.2/32,192.168.255.2/32. |
|
||||
| P24 | Frontiere nord/sud : acces d'administration declare | AFF-104 | ✅ OK | CONFORME : frontiere nord/sud, 86 regles, 15 routes, admin=10.0.0.0/24,10.17.0.0/24,10.29.19.41/32,192.168.254.2/32,192.168.255.2/32. |
|
||||
| P25 | Pare-feu Proxmox : est-ouest intra-tenant derive | AFF-107 | ✅ OK | CONFORME : pare-feu Proxmox, 3 tenant(s), 50 groupe(s), 79 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 5 hote(s) x 5 integration(s) universelle(s) : aucune lacune, aucune recopie (0 exemption(s) derivee(s) du service rendu). |
|
||||
| P27 | Propriete des intrants : hebergeur et tenant separes | AFF-109 | ✅ OK | 8 cle(s) de cluster chez l'hebergeur, aucune recopiee dans les group_vars du tenant. |
|
||||
|
|
|
|||
|
|
@ -46,10 +46,36 @@
|
|||
{% for serveur in dns_amorcage.split(',') if serveur | trim %}
|
||||
nameserver {{ serveur | trim }}
|
||||
{% endfor %}
|
||||
# L'AMORÇAGE NE DOIT PAS DÉFAIRE CE QUI EST DÉJÀ EN PLACE (2026-08-25).
|
||||
#
|
||||
# La garde ne protégeait que l'hôte du résolveur lui-même (`127.0.0.1`). Partout
|
||||
# ailleurs, le socle réécrivait `/etc/resolv.conf` avec `dns_amorcage` — donc
|
||||
# **défaisait la bascule** de `client_resolveur` à chaque passage.
|
||||
#
|
||||
# Ça ne s'était jamais vu chez un tenant : `dns_amorcage` y vaut l'adresse du
|
||||
# résolveur du tenant, si bien que réécrire remettait la même valeur. La
|
||||
# coïncidence masquait le défaut.
|
||||
#
|
||||
# Sur le site, les deux ont divergé quelques heures — l'amorçage pointait encore sur
|
||||
# la frontière alors que `client_resolveur` avait basculé les machines sur
|
||||
# `site-dns-01`. Chaque déploiement du socle les ramenait en arrière, en silence, et
|
||||
# ça ne s'est vu qu'en retirant la règle de pare-feu vers la frontière : les cinq
|
||||
# machines ont alors perdu la résolution d'un coup.
|
||||
#
|
||||
# On ne pose donc l'amorçage que si rien de sensé n'est déjà là : ni la loopback, ni
|
||||
# un résolveur déclaré de cet écosystème.
|
||||
vars:
|
||||
serveur_debian_resolv_texte: >-
|
||||
{{ serveur_debian_resolv_actuel.content | default('') | b64decode | string }}
|
||||
serveur_debian_resolveurs_declares: >-
|
||||
{{ (groups['serveur_resolveur'] | default([]))
|
||||
| map('extract', hostvars, 'ansible_host') | select | list }}
|
||||
when:
|
||||
- dns_amorcage is defined
|
||||
- dns_amorcage | string | length > 0
|
||||
- "'127.0.0.1' not in (serveur_debian_resolv_actuel.content | default('') | b64decode | string)"
|
||||
- "'127.0.0.1' not in serveur_debian_resolv_texte"
|
||||
- serveur_debian_resolveurs_declares
|
||||
| select('in', serveur_debian_resolv_texte) | list | length == 0
|
||||
|
||||
roles:
|
||||
- common_packages
|
||||
|
|
|
|||
|
|
@ -310,6 +310,35 @@ def _instance_active() -> tuple[str, dict] | tuple[None, None]:
|
|||
return None, None
|
||||
|
||||
|
||||
def _plan_site() -> Path | None:
|
||||
"""Le `plan/` du SITE, a cote de son `underlay.yml`."""
|
||||
c = underlay_mod.chemin()
|
||||
return (c.resolve().parent / "plan") if c else None
|
||||
|
||||
|
||||
def _lire_plan_site(nom: str) -> dict:
|
||||
d = _plan_site()
|
||||
f = (d / nom) if d else None
|
||||
if not f or not f.is_file():
|
||||
return {}
|
||||
return yaml.safe_load(f.read_text(encoding="utf-8")) or {}
|
||||
|
||||
|
||||
def _machines_du_plan_site() -> list[dict]:
|
||||
srv = (_lire_plan_site("serveurs.yml") or {}).get("serveurs") or {}
|
||||
return [{**s, "nom": nom} for nom, s in srv.items()
|
||||
if str(s.get("etat", "actif")) == "actif"]
|
||||
|
||||
|
||||
def _services_du_plan_site() -> dict[str, list[str]]:
|
||||
apps = (_lire_plan_site("applications.yml") or {}).get("applications") or {}
|
||||
out: dict[str, list[str]] = {}
|
||||
for app in apps.values():
|
||||
if app.get("hote") and app.get("groupe"):
|
||||
out.setdefault(str(app["hote"]), []).append(str(app["groupe"]))
|
||||
return out
|
||||
|
||||
|
||||
def construire(tenants: list[tuple[str, str, dict]]) -> dict:
|
||||
"""Le devis, en structure — rendue ensuite en texte ou en JSON."""
|
||||
flux_par_role = charger_flux()
|
||||
|
|
@ -558,7 +587,15 @@ def construire(tenants: list[tuple[str, str, dict]]) -> dict:
|
|||
# Une regle posee sur la mauvaise interface ne correspond jamais — c'est la panne la
|
||||
# plus silencieuse de cette couche.
|
||||
_u = underlay_mod.charger()
|
||||
_machines_site = underlay_mod.machines(_u)
|
||||
# LES MACHINES DU SITE VIENNENT DE SON PLAN, plus de l'underlay (2026-08-25).
|
||||
#
|
||||
# En deplacant le plan hors de `underlay.yml`, j'ai vide `machines()` sans reprendre
|
||||
# ici : le devis a cesse de voir le site et a propose de RETIRER ses 36 objets — tous
|
||||
# ses alias et toutes ses regles. Aucune preuve ne couvre le devis de la frontiere du
|
||||
# site, donc `make prouver` restait vert. C'est le plan avant application qui l'a
|
||||
# attrape, et rien d'autre ne l'aurait fait.
|
||||
_machines_site = _machines_du_plan_site()
|
||||
_services_site = _services_du_plan_site()
|
||||
if _machines_site:
|
||||
_res_site = {r.get("nom"): r for r in underlay_mod.reseaux(_u)}
|
||||
_sous_site = sorted({str(_res_site[m["reseau"]]["sous_reseau"])
|
||||
|
|
@ -629,12 +666,12 @@ def construire(tenants: list[tuple[str, str, dict]]) -> dict:
|
|||
"passerelle : le site n'a pas de resolveur a lui.",
|
||||
})
|
||||
|
||||
_roles_site = sorted({s for m in _machines_site for s in (m.get("services") or [])}
|
||||
_roles_site = sorted({g for gs in _services_site.values() for g in gs}
|
||||
| {"serveur_debian"})
|
||||
for _role in _roles_site:
|
||||
_ips = sorted({str(m["ip"]) for m in _machines_site
|
||||
if _role == "serveur_debian"
|
||||
or _role in (m.get("services") or [])})
|
||||
or _role in _services_site.get(m["nom"], [])})
|
||||
_a = nom_alias(_role, "SITE")
|
||||
alias[_a] = {
|
||||
"type": "host",
|
||||
|
|
|
|||
Loading…
Reference in a new issue