Set-OPS-Public/roles/serveur_prometheus/templates/prometheus.yml.j2
Daniel Allaire f3bb9caee3 chiffrement : metriques et journaux du site en TLS, et la frontiere certifiee par sa propre autorite
Metriques et journaux (CHANGELOG 142). Le site pose les quatre interrupteurs TLS
que chaque locataire pose dans ses group_vars : node_exporter et Loki servaient
en HTTP sur les neuf VM, le certificat etait la, inutilise. test_parite_tls_site.py
compare desormais le site aux locataires. Les hyperviseurs poussent leurs
journaux en HTTPS verifie, avec la racine du site comme simple fichier.
L'exportateur PostgreSQL sert en HTTPS partout, sur l'interrupteur de PostgreSQL
lui-meme, que Prometheus lit pour choisir son schema.

La frontiere (CHANGELOG 143). Une autorite subordonnee signee par la racine du
site, bornee (pathlen 0, noms sous le domaine et adresses du site, contrainte
critique), cle nee sur la boite (scripts/frontiere_pki.py). Syslog vers Loki en
TLS mutuel (seule la frontiere peut ecrire), exportateur en HTTPS, interface web
sur le certificat de services ; les clients d'API verifient la frontiere contre
la racine du site. La lecture du magasin passe par config.xml : l'API de
recherche renvoie les cles privees.

Deploye et mesure au site, chez les deux locataires et sur la frontiere ; 95 preuves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:25:28 -04:00

89 lines
4.3 KiB
Django/Jinja

# Gere par Set-OPS (role serveur_prometheus). Ne pas editer a la main.
global:
scrape_interval: {{ serveur_prometheus_intervalle }}
scrape_configs:
- job_name: prometheus
static_configs:
- targets: ['localhost:9090']
- job_name: node
{% if serveur_prometheus_metriques_tls | default(false) %}
scheme: https
tls_config:
ca_file: {{ serveur_prometheus_metriques_ca }}
{% endif %}
static_configs:
{# `intersect` rend un ENSEMBLE : son ordre d'iteration n'est pas stable d'un
processus Python a l'autre. Sans `sort`, ce fichier se rendait differemment a
chaque passage — memes quatorze cibles, ordre different — et Prometheus etait
redemarre pour rien. Trouve par le passage d'idempotence du 2026-08-09.
On trie sur les HOTES : ordre deterministe et lisible par un humain. #}
- targets: {{ (groups.get(serveur_prometheus_groupe_metriques, []) | intersect(groups.get('hotes_actifs', [])) | sort)
| map('extract', hostvars)
| selectattr('ansible_host', 'defined')
| map(attribute='ansible_host')
| map('regex_replace', '$', ':' ~ serveur_prometheus_port_node)
| list | to_json }}
{# --- LES CIBLES QUE LES ROLES DECLARENT (2026-09-14) ---------------------------
Chaque `roles/<role>/meta/metriques.yml` decrit son exportateur ; le nom du DOSSIER
est le nom du GROUPE, donc les cibles sont les hotes actifs de ce groupe.
UN ROLE DECLARE SANS HOTE NE PRODUIT AUCUN JOB. Prometheus n'a pas a porter une cible
qui n'existe pas, et son journal n'a pas a se remplir de refus previsibles.
ORDRE STABLE : `sort` sur les hotes, comme pour `node` juste au-dessus. Sans lui, le
fichier se rend differemment a chaque passage et Prometheus redemarre pour rien —
defaut trouve le 2026-08-09, qu'on ne va pas reintroduire ici. #}
{% for decl in serveur_prometheus_declarations.results | default([]) %}
{% set role = decl.item.path | dirname | dirname | basename %}
{% set meta = decl.content | b64decode | from_yaml %}
{% set expo = meta.exportateur | default(none) %}
{% set cibles = (groups.get(role, []) | intersect(groups.get('hotes_actifs', [])) | sort)
| map('extract', hostvars) | selectattr('ansible_host', 'defined')
| map(attribute='ansible_host')
| map('regex_replace', '$', ':' ~ (expo.port | default(0))) | list %}
{# TLS SI LA DECLARATION LE DIT ET QUE CHAQUE CIBLE L'A (2026-10-12) : `tls_si` nomme
l'interrupteur que le role exportateur lit lui aussi. Une seule cible sans lui, et le
job reste en HTTP — mieux qu'un job HTTPS qui en perdrait une sans rien dire. #}
{% set tls = namespace(oui=(expo.tls_si | default('') | length > 0) if expo else false) %}
{% for h in (groups.get(role, []) | intersect(groups.get('hotes_actifs', [])) | sort) %}
{% if tls.oui and not (hostvars[h][expo.tls_si] | default(false) | bool) %}{% set tls.oui = false %}{% endif %}
{% endfor %}
{% if expo and cibles %}
- job_name: {{ expo.job }}
{% if tls.oui %}
scheme: https
tls_config:
ca_file: {{ serveur_prometheus_metriques_ca }}
{% endif %}
static_configs:
- targets: {{ cibles | to_json }}
{% endif %}
{% endfor %}
{# Les cibles ECRITES AU PLAN completent la derivation, elles ne la remplacent pas :
un equipement ou un service tiers n'a aucun role Set-OPS pour le declarer. #}
{% for job in serveur_prometheus_cibles_supplementaires %}
- job_name: {{ job.job }}
{# UNE CIBLE HORS FLOTTE PEUT SERVIR EN TLS (2026-10-12) : la frontiere, certifiee par son
autorite subordonnee, verifiee contre la meme racine que le reste. #}
{% if job.tls | default(false) %}
scheme: https
tls_config:
ca_file: {{ serveur_prometheus_metriques_ca }}
{% endif %}
static_configs:
{% if job.hotes | default({}) %}
{# UNE CIBLE NOMMEE PAR BLOC : l'etiquette `hote` s'attache a un `static_config`, pas a
une adresse. Les series disent alors « asgard », pas « 192.168.11.41:9100 ». #}
{% for cible in job.cibles %}
- targets: {{ [cible] | to_json }}
labels:
hote: {{ job.hotes[cible] | default(cible) | to_json }}
{% endfor %}
{% else %}
- targets: {{ job.cibles | to_json }}
{% endif %}
{% endfor %}