Machinerie d'exposition : SAN auto-dérivés + vhost WebSocket-aware
Élimine 3 gotchas du spike Nextcloud/Collabora : - instancier dérive sans_exposition par edge (FQDN exposés du plan) → le cert edge (client_pki_sans) se met à jour tout seul, plus de liste manuelle en group_vars ; - champ 'websocket' sur une application → le vhost d'exposition ajoute la map $http_upgrade + en-têtes Upgrade/Connection + timeout long (Collabora, éditeurs live) ; - (plancher /etc/hosts : le mécanisme d'alias existait déjà, publier_expositions=true). Prouvé sur le lab : cert edge couvre les 6 expositions, bureau 200 en WebSocket. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
da4976aa98
commit
0d2bc2c2e9
3 changed files with 26 additions and 1 deletions
|
|
@ -6,6 +6,10 @@
|
||||||
# amont = http://<IP interne de l'hote de l'application>:<port>. Une exposition
|
# amont = http://<IP interne de l'hote de l'application>:<port>. Une exposition
|
||||||
# non resolvable (application sans hote actif, ou sans port) est listee mais
|
# non resolvable (application sans hote actif, ou sans port) est listee mais
|
||||||
# NON publiee (binding declare, pas encore cablable).
|
# NON publiee (binding declare, pas encore cablable).
|
||||||
|
{% if serveur_nginx_expositions | default([]) | selectattr('websocket', 'defined') | selectattr('websocket') | list | length > 0 %}
|
||||||
|
# Une exposition au moins est WebSocket-aware (ex. Collabora) : map d'upgrade.
|
||||||
|
map $http_upgrade $connection_upgrade { default upgrade; "" close; }
|
||||||
|
{% endif %}
|
||||||
{% for expo in serveur_nginx_expositions | default([]) %}
|
{% for expo in serveur_nginx_expositions | default([]) %}
|
||||||
{% set ns = namespace(amont='') %}
|
{% set ns = namespace(amont='') %}
|
||||||
{% if expo.hote in hostvars and hostvars[expo.hote].ansible_host is defined and expo.port %}
|
{% if expo.hote in hostvars and hostvars[expo.hote].ansible_host is defined and expo.port %}
|
||||||
|
|
@ -37,6 +41,13 @@ server {
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
{% if expo.websocket | default(false) %}
|
||||||
|
# Collabora & co : editeur en direct via WebSocket (upgrade + longue tenue).
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection $connection_upgrade;
|
||||||
|
proxy_read_timeout 3600s;
|
||||||
|
{% endif %}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,12 @@ import yaml
|
||||||
|
|
||||||
from inventory_rules import (
|
from inventory_rules import (
|
||||||
charger_applications,
|
charger_applications,
|
||||||
|
charger_domaines,
|
||||||
charger_nomenclature,
|
charger_nomenclature,
|
||||||
charger_serveurs,
|
charger_serveurs,
|
||||||
deriver_nomenclature,
|
deriver_nomenclature,
|
||||||
deriver_ressources,
|
deriver_ressources,
|
||||||
|
expositions_des_applications,
|
||||||
fonction_seq,
|
fonction_seq,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -55,6 +57,7 @@ GENERE = INVENTAIRE.with_name("hosts.genere.yml")
|
||||||
FICHIER_SERVEURS = INSTANCE / "plan/serveurs.yml"
|
FICHIER_SERVEURS = INSTANCE / "plan/serveurs.yml"
|
||||||
FICHIER_APPLICATIONS = INSTANCE / "plan/applications.yml"
|
FICHIER_APPLICATIONS = INSTANCE / "plan/applications.yml"
|
||||||
FICHIER_NOMENCLATURE = INSTANCE / "plan/nomenclature.yml"
|
FICHIER_NOMENCLATURE = INSTANCE / "plan/nomenclature.yml"
|
||||||
|
FICHIER_DOMAINES = INSTANCE / "plan/domaines.yml"
|
||||||
|
|
||||||
GROUPES_SOCLE = ["serveur_debian", "serveur_durci"]
|
GROUPES_SOCLE = ["serveur_debian", "serveur_durci"]
|
||||||
PLACEMENT = [("noeud", "proxmox_noeud"), ("stockage", "proxmox_stockage"),
|
PLACEMENT = [("noeud", "proxmox_noeud"), ("stockage", "proxmox_stockage"),
|
||||||
|
|
@ -151,6 +154,9 @@ def generer() -> dict:
|
||||||
apps = charger_applications(FICHIER_APPLICATIONS).get("applications", {})
|
apps = charger_applications(FICHIER_APPLICATIONS).get("applications", {})
|
||||||
nomenclature = charger_nomenclature(FICHIER_NOMENCLATURE)
|
nomenclature = charger_nomenclature(FICHIER_NOMENCLATURE)
|
||||||
injections = resoudre_liens(apps, serveurs, nomenclature)
|
injections = resoudre_liens(apps, serveurs, nomenclature)
|
||||||
|
# Expositions du plan (champ 'expose') -> pour deriver les SANs de chaque edge.
|
||||||
|
domaines = charger_domaines(FICHIER_DOMAINES)
|
||||||
|
expositions = expositions_des_applications({"applications": apps}, domaines)
|
||||||
|
|
||||||
services_par_hote: dict = {}
|
services_par_hote: dict = {}
|
||||||
for app in apps.values():
|
for app in apps.values():
|
||||||
|
|
@ -184,9 +190,14 @@ def generer() -> dict:
|
||||||
# Variables derivees des liens (bindings) declares par les applications.
|
# Variables derivees des liens (bindings) declares par les applications.
|
||||||
for var, val in injections.get(nom, {}).items():
|
for var, val in injections.get(nom, {}).items():
|
||||||
hostvars[var] = val
|
hostvars[var] = val
|
||||||
|
groupes = set(GROUPES_SOCLE) | services_par_hote.get(nom, set()) | set(srv.get("integrations") or [])
|
||||||
|
# SANs d'exposition : les FQDN que cet edge sert (derives du plan) -> cert edge
|
||||||
|
# (client_pki_sans), au lieu d'une liste manuelle. Vide si l'hote n'est pas un edge.
|
||||||
|
sans = sorted({e["fqdn"] for e in expositions if e.get("edge") in groupes})
|
||||||
|
if sans:
|
||||||
|
hostvars["sans_exposition"] = sans
|
||||||
etat = "hotes_actifs" if srv.get("etat") == "actif" else "hotes_planifies"
|
etat = "hotes_actifs" if srv.get("etat") == "actif" else "hotes_planifies"
|
||||||
children[etat]["hosts"][nom] = hostvars
|
children[etat]["hosts"][nom] = hostvars
|
||||||
groupes = set(GROUPES_SOCLE) | services_par_hote.get(nom, set()) | set(srv.get("integrations") or [])
|
|
||||||
for groupe in sorted(groupes):
|
for groupe in sorted(groupes):
|
||||||
children.setdefault(groupe, {"hosts": {}})["hosts"][nom] = None
|
children.setdefault(groupe, {"hosts": {}})["hosts"][nom] = None
|
||||||
return {"all": {"children": children}}
|
return {"all": {"children": children}}
|
||||||
|
|
|
||||||
|
|
@ -200,6 +200,9 @@ def expositions_des_applications(applications: dict, domaines: dict, edge: str |
|
||||||
"fqdn": fqdn, "application": app_id, "groupe": app.get("groupe"),
|
"fqdn": fqdn, "application": app_id, "groupe": app.get("groupe"),
|
||||||
"hote": app.get("hote"), "port": app.get("port"),
|
"hote": app.get("hote"), "port": app.get("port"),
|
||||||
"edge": edge_dom, "domaine": dom,
|
"edge": edge_dom, "domaine": dom,
|
||||||
|
# Exposition WebSocket-aware (ex. Collabora) : le vhost ajoute
|
||||||
|
# alors l'upgrade Upgrade/Connection. Declare par l'app (websocket: true).
|
||||||
|
"websocket": bool(app.get("websocket")),
|
||||||
})
|
})
|
||||||
return resultat
|
return resultat
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue