Set-OPS-Public/scripts/tests/test_exportateur_pg_tls.py

84 lines
4 KiB
Python
Raw Normal View History

#!/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())