#!/usr/bin/env python3 """L'exportateur PostgreSQL sert en TLS, et Prometheus le lit en HTTPS verifie. POURQUOI (2026-10-12). Le 9187 etait le seul port de metriques en clair, chez les locataires comme au site — dette inscrite au registre le 2026-09-14. Un seul interrupteur decide des deux bouts : `serveur_postgresql_tls_actif`, que la declaration de l'exportateur nomme (`tls_si`) et que Prometheus lit dans les hostvars de chaque cible. """ from __future__ import annotations import base64 import sys from pathlib import Path import jinja2 import yaml RACINE = Path(__file__).resolve().parents[2] PG = RACINE / "roles/serveur_postgresql" def rendre_prometheus(tls_par_hote: dict[str, bool]) -> dict: meta = PG / "meta/metriques.yml" # LES FILTRES D'ANSIBLE LUI-MEME (intersect, extract, regex_replace, b64decode...) : # un rendu avec des imitations prouverait les imitations. from ansible.plugins.filter.core import FilterModule as Coeur from ansible.plugins.filter.mathstuff import FilterModule as Maths env = jinja2.Environment(extensions=["jinja2.ext.do"]) env.filters.update(Coeur().filters()) env.filters.update(Maths().filters()) hotes = sorted(tls_par_hote) hostvars = {h: {"ansible_host": f"10.0.0.{i + 1}", "serveur_postgresql_tls_actif": v} for i, (h, v) in enumerate(sorted(tls_par_hote.items()))} rendu = env.from_string((RACINE / "roles/serveur_prometheus/templates/prometheus.yml.j2") .read_text(encoding="utf-8")).render( serveur_prometheus_intervalle="15s", serveur_prometheus_metriques_tls=True, serveur_prometheus_metriques_ca="/etc/step/certs/root_ca.crt", serveur_prometheus_groupe_metriques="client_metrique", serveur_prometheus_port_node=9100, serveur_prometheus_cibles_supplementaires=[], groups={"client_metrique": hotes, "hotes_actifs": hotes, "serveur_postgresql": hotes}, hostvars=hostvars, serveur_prometheus_declarations={"results": [{ "item": {"path": str(meta)}, "content": base64.b64encode(meta.read_bytes()).decode()}]}, ) return {j["job_name"]: j for j in yaml.safe_load(rendu)["scrape_configs"]} def main() -> int: echecs: list[str] = [] expo = (yaml.safe_load((PG / "meta/metriques.yml").read_text(encoding="utf-8")) or {}).get("exportateur") or {} if expo.get("tls_si") != "serveur_postgresql_tls_actif": echecs.append(f"meta/metriques.yml : tls_si = {expo.get('tls_si')!r}, attendu serveur_postgresql_tls_actif") flux = yaml.safe_load((PG / "meta/flux.yml").read_text(encoding="utf-8"))["flux"] f9187 = next((f for f in flux if str(f.get("port")) == "9187"), {}) if f9187.get("chiffrement") != "tls": echecs.append(f"le registre dit 9187 `{f9187.get('chiffrement')}`, attendu `tls`") taches = (PG / "tasks/main.yml").read_text(encoding="utf-8") if "--web.config.file=" not in taches or "serveur_postgresql_exportateur_web_config" not in taches: echecs.append("l'exportateur ne recoit pas `--web.config.file`") if "setops-pg-exportateur-cert-sync" not in taches: echecs.append("l'exportateur n'est pas abonne au renouvellement du certificat") jobs = rendre_prometheus({"data-sql-01": True, "data-sql-02": True}) pg = jobs.get("postgresql", {}) if pg.get("scheme") != "https" or (pg.get("tls_config") or {}).get("ca_file") != "/etc/step/certs/root_ca.crt": echecs.append(f"toutes cibles TLS : job postgresql = {pg}, attendu https + ca_file") jobs = rendre_prometheus({"data-sql-01": True, "data-sql-02": False}) if jobs.get("postgresql", {}).get("scheme") == "https": echecs.append("une cible sans TLS : le job ne doit pas passer en https (il la perdrait)") for e in echecs: print(f"ECHEC : {e}") if not echecs: print("OK : l'exportateur PostgreSQL sert en TLS ; Prometheus le lit en HTTPS verifie sur le meme interrupteur.") return 1 if echecs else 0 if __name__ == "__main__": raise SystemExit(main())