flux : seulement_si non_vide ; forge 3000 et PowerDNS 5300 conditionnels
La forge du site sert son propre TLS (443) : le flux 3000 derriere un edge ne vaut que si serveur_forgejo_tls est faux. L'AXFR 5300 ne vaut que la ou l'instance publique existe (dns_public_site non vide) : pas au site. Absente partout = vide ; une expression Jinja garde le statu quo. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
0318412a9e
commit
25fd4fc6f1
3 changed files with 33 additions and 10 deletions
|
|
@ -19,11 +19,17 @@ flux:
|
|||
# dépend ». Le vocabulaire des flux n'a pas de mot pour l'indécision, et c'est tant
|
||||
# mieux : un flux se lit pour savoir ce qui circule et comment. Une valeur ambiguë
|
||||
# rendrait l'audit impossible sans aller lire le déploiement.
|
||||
#
|
||||
# ET LA SITUATION SE LIT DANS LE REGLAGE (2026-09-30) : la premiere seulement si la forge ne
|
||||
# sert pas son propre TLS. Au site (`serveur_forgejo_tls: true`), elle ecoute 443 et rien
|
||||
# sur 3000 ; la regle restait posee parce que le site a un edge — et la sonde
|
||||
# `connectivite` la montrait coupee chaque minute.
|
||||
- sens: ingress
|
||||
port: 3000
|
||||
protocole: tcp
|
||||
pair: edge
|
||||
chiffrement: clair
|
||||
seulement_si: {variable: serveur_forgejo_tls, egal: false}
|
||||
raison: >-
|
||||
Interface web + Git HTTP derrière un edge : le nginx termine le TLS et parle en
|
||||
clair à la forge. C'est le cas de tout tenant.
|
||||
|
|
|
|||
|
|
@ -47,15 +47,22 @@ flux:
|
|||
# Sur l'instance `pdns@public` seulement, qui ne sert que les zones `primaire-cache`.
|
||||
# UDP pour le SOA que le secondaire compare avant de tirer, TCP pour le transfert. La
|
||||
# regle limite qui peut FRAPPER ; TSIG decide qui peut LIRE.
|
||||
#
|
||||
# SEULEMENT LA OU LE SITE A UN SERVEUR PUBLIC A SERVIR (2026-09-30) — la ou l'instance
|
||||
# `pdns@public` existe (`zones-publiques.yml` ne se joue que sous la meme condition). Le
|
||||
# PowerDNS du SITE n'en a pas : il ecoute sur la boucle locale, et la regle restait posee,
|
||||
# coupee — la sonde `connectivite` le montrait chaque minute.
|
||||
- sens: ingress
|
||||
port: 5300
|
||||
protocole: tcp
|
||||
pair: dns_public_site
|
||||
seulement_si: {variable: dns_public_site, non_vide: true}
|
||||
chiffrement: clair
|
||||
raison: "AXFR signe TSIG par le serveur DNS public du site, vers l'instance publique uniquement."
|
||||
- sens: ingress
|
||||
port: 5300
|
||||
protocole: udp
|
||||
pair: dns_public_site
|
||||
seulement_si: {variable: dns_public_site, non_vide: true}
|
||||
chiffrement: clair
|
||||
raison: "Interrogation du SOA par le secondaire du site avant chaque transfert."
|
||||
|
|
|
|||
|
|
@ -107,15 +107,17 @@ def charger_flux() -> dict[str, list[dict]]:
|
|||
# simplement sans objet ») ; le registre ne savait pas le dire.
|
||||
#
|
||||
# seulement_si: {variable: <nom>, egal: <valeur>}
|
||||
# seulement_si: {variable: <nom>, non_vide: true} (absente partout = vide)
|
||||
#
|
||||
# La valeur est lue la ou ce role la lit : variables de l'hote (inventaire du site), puis
|
||||
# `group_vars` de l'ecosysteme, puis defauts du role. INDETERMINABLE (une expression Jinja)
|
||||
# -> la regle est emise, comme avant, et on le dit : ouvrir sans savoir est le statu quo,
|
||||
# fermer sans savoir pourrait couper une console.
|
||||
def _valeur_variable(variable: str, groupe: str, dossier: Path | None,
|
||||
hostvars: dict | None) -> tuple[bool, object]:
|
||||
hostvars: dict | None) -> tuple[str, object]:
|
||||
"""("connu", valeur) | ("absent", None) | ("indetermine", None) — une expression Jinja."""
|
||||
if hostvars and variable in hostvars:
|
||||
return True, hostvars[variable]
|
||||
return "connu", hostvars[variable]
|
||||
if dossier is not None:
|
||||
gv = dossier / "inventories" / "principal" / "group_vars"
|
||||
candidats = [gv / f"{groupe}.yml", *sorted((gv / groupe).glob("*.yml"))]
|
||||
|
|
@ -128,16 +130,16 @@ def _valeur_variable(variable: str, groupe: str, dossier: Path | None,
|
|||
except yaml.YAMLError:
|
||||
continue
|
||||
if isinstance(data, dict) and variable in data:
|
||||
return True, data[variable]
|
||||
return "connu", data[variable]
|
||||
defauts = ROLES / groupe / "defaults" / "main.yml"
|
||||
if defauts.is_file():
|
||||
data = yaml.safe_load(defauts.read_text(encoding="utf-8")) or {}
|
||||
if variable in data:
|
||||
v = data[variable]
|
||||
if isinstance(v, str) and "{{" in v:
|
||||
return False, None
|
||||
return True, v
|
||||
return False, None
|
||||
return "indetermine", None
|
||||
return "connu", v
|
||||
return "absent", None
|
||||
|
||||
|
||||
def flux_applicable(fl: dict, groupe: str, dossier: Path | None,
|
||||
|
|
@ -146,8 +148,15 @@ def flux_applicable(fl: dict, groupe: str, dossier: Path | None,
|
|||
cond = fl.get("seulement_si")
|
||||
if not cond:
|
||||
return True
|
||||
connu, valeur = _valeur_variable(str(cond.get("variable")), groupe, dossier, hostvars)
|
||||
if not connu:
|
||||
etat, valeur = _valeur_variable(str(cond.get("variable")), groupe, dossier, hostvars)
|
||||
if "non_vide" in cond:
|
||||
# Absente partout, elle est vide : c'est ce que le role lit (`| default('')`).
|
||||
if etat == "indetermine":
|
||||
print(f"note : {groupe} — `{cond.get('variable')}` indeterminable ici : la regle "
|
||||
f"du port {fl.get('port')} est emise (statu quo).", file=sys.stderr)
|
||||
return True
|
||||
return bool(valeur) == bool(cond.get("non_vide"))
|
||||
if etat != "connu":
|
||||
print(f"note : {groupe} — `{cond.get('variable')}` indeterminable ici : la regle "
|
||||
f"du port {fl.get('port')} est emise (statu quo).", file=sys.stderr)
|
||||
return True
|
||||
|
|
@ -181,8 +190,9 @@ def valider(flux_par_role: dict[str, list[dict]]) -> tuple[int, int]:
|
|||
erreurs.append(f"{ref} : chiffrement invalide {fl.get('chiffrement')!r}")
|
||||
cond = fl.get("seulement_si")
|
||||
if cond is not None and (not isinstance(cond, dict) or not cond.get("variable")
|
||||
or "egal" not in cond):
|
||||
erreurs.append(f"{ref} : seulement_si attend {{variable: <nom>, egal: <valeur>}}")
|
||||
or ("egal" not in cond) == ("non_vide" not in cond)):
|
||||
erreurs.append(f"{ref} : seulement_si attend {{variable: <nom>, egal: <valeur>}} "
|
||||
f"ou {{variable: <nom>, non_vide: true}}")
|
||||
for p in _pairs(fl):
|
||||
if p not in MOTS_PAIR and p not in roles_existants and not str(p).startswith(("serveur_", "client_")):
|
||||
erreurs.append(f"{ref} : pair inconnu {p!r}")
|
||||
|
|
|
|||
Loading…
Reference in a new issue