2026-06-24 20:17:46 -04:00
|
|
|
---
|
|
|
|
|
- name: Installer PostgreSQL
|
|
|
|
|
ansible.builtin.apt:
|
|
|
|
|
name: "{{ serveur_postgresql_packages }}"
|
|
|
|
|
state: present
|
|
|
|
|
update_cache: true
|
|
|
|
|
cache_valid_time: 3600
|
|
|
|
|
|
|
|
|
|
- name: Lister les clusters PostgreSQL installes
|
|
|
|
|
ansible.builtin.find:
|
|
|
|
|
paths: /etc/postgresql
|
|
|
|
|
file_type: directory
|
|
|
|
|
recurse: false
|
|
|
|
|
register: serveur_postgresql_clusters
|
|
|
|
|
|
|
|
|
|
- name: Determiner la version du cluster
|
|
|
|
|
ansible.builtin.set_fact:
|
|
|
|
|
serveur_postgresql_version_effective: >-
|
|
|
|
|
{{ serveur_postgresql_version
|
2026-07-04 20:46:53 -04:00
|
|
|
or (serveur_postgresql_clusters.files | map(attribute='path') | map('basename')
|
|
|
|
|
| select('match', '^[0-9]+$') | sort | last | default('')) }}
|
2026-06-24 20:17:46 -04:00
|
|
|
|
|
|
|
|
- name: Refuser si aucun cluster PostgreSQL n'est present
|
|
|
|
|
ansible.builtin.assert:
|
|
|
|
|
that:
|
|
|
|
|
- serveur_postgresql_version_effective | length > 0
|
|
|
|
|
fail_msg: "Aucun cluster PostgreSQL trouve sous /etc/postgresql."
|
|
|
|
|
|
|
|
|
|
- name: Definir le repertoire de configuration du cluster
|
|
|
|
|
ansible.builtin.set_fact:
|
|
|
|
|
serveur_postgresql_conf_dir: "/etc/postgresql/{{ serveur_postgresql_version_effective }}/main"
|
|
|
|
|
|
|
|
|
|
- name: Assurer le repertoire conf.d
|
|
|
|
|
ansible.builtin.file:
|
|
|
|
|
path: "{{ serveur_postgresql_conf_dir }}/conf.d"
|
|
|
|
|
state: directory
|
|
|
|
|
owner: postgres
|
|
|
|
|
group: postgres
|
|
|
|
|
mode: "0755"
|
|
|
|
|
|
2026-07-04 18:31:30 -04:00
|
|
|
# --- TLS : synchroniser le cert step_ca AVANT d'activer ssl (ordre critique) ---
|
|
|
|
|
- name: TLS — assurer le repertoire des certificats (postgres)
|
|
|
|
|
ansible.builtin.file:
|
|
|
|
|
path: "{{ serveur_postgresql_tls_dir }}"
|
|
|
|
|
state: directory
|
|
|
|
|
owner: postgres
|
|
|
|
|
group: postgres
|
|
|
|
|
mode: "0700"
|
|
|
|
|
when: serveur_postgresql_tls_actif | bool
|
|
|
|
|
|
|
|
|
|
- name: TLS — deployer le script de synchronisation du certificat
|
|
|
|
|
ansible.builtin.template:
|
|
|
|
|
src: setops-postgresql-cert-sync.sh.j2
|
|
|
|
|
dest: /usr/local/sbin/setops-postgresql-cert-sync
|
|
|
|
|
owner: root
|
|
|
|
|
group: root
|
|
|
|
|
mode: "0755"
|
|
|
|
|
when: serveur_postgresql_tls_actif | bool
|
|
|
|
|
|
|
|
|
|
- name: TLS — deployer les unites de synchronisation (service + path)
|
|
|
|
|
ansible.builtin.template:
|
|
|
|
|
src: "{{ item.s }}"
|
|
|
|
|
dest: "{{ item.d }}"
|
|
|
|
|
owner: root
|
|
|
|
|
group: root
|
|
|
|
|
mode: "0644"
|
|
|
|
|
loop:
|
|
|
|
|
- { s: "setops-postgresql-cert-sync.service.j2", d: "/etc/systemd/system/setops-postgresql-cert-sync.service" }
|
|
|
|
|
- { s: "setops-postgresql-cert-sync.path.j2", d: "/etc/systemd/system/setops-postgresql-cert-sync.path" }
|
|
|
|
|
loop_control:
|
|
|
|
|
label: "{{ item.d | basename }}"
|
|
|
|
|
when: serveur_postgresql_tls_actif | bool
|
|
|
|
|
|
|
|
|
|
- name: TLS — activer la surveillance du certificat (path)
|
|
|
|
|
ansible.builtin.systemd:
|
|
|
|
|
name: setops-postgresql-cert-sync.path
|
|
|
|
|
enabled: true
|
|
|
|
|
state: started
|
|
|
|
|
daemon_reload: true
|
|
|
|
|
when:
|
|
|
|
|
- serveur_postgresql_tls_actif | bool
|
|
|
|
|
- not ansible_check_mode
|
|
|
|
|
|
|
|
|
|
- name: TLS — synchroniser le certificat maintenant
|
|
|
|
|
ansible.builtin.command: /usr/local/sbin/setops-postgresql-cert-sync
|
|
|
|
|
changed_when: false
|
|
|
|
|
when:
|
|
|
|
|
- serveur_postgresql_tls_actif | bool
|
|
|
|
|
- not ansible_check_mode
|
|
|
|
|
|
|
|
|
|
- name: TLS — verifier la presence du certificat synchronise
|
|
|
|
|
ansible.builtin.stat:
|
|
|
|
|
path: "{{ serveur_postgresql_tls_dir }}/{{ item }}"
|
|
|
|
|
register: serveur_postgresql_tls_stat
|
|
|
|
|
loop:
|
|
|
|
|
- server.crt
|
|
|
|
|
- server.key
|
|
|
|
|
when:
|
|
|
|
|
- serveur_postgresql_tls_actif | bool
|
|
|
|
|
- not ansible_check_mode
|
|
|
|
|
|
|
|
|
|
- name: TLS — garde-fou, refuser d'activer ssl sans certificat en place
|
|
|
|
|
ansible.builtin.assert:
|
|
|
|
|
that:
|
|
|
|
|
- item.stat.exists
|
|
|
|
|
fail_msg: "Certificat PG absent ({{ item.item }}) — ssl non active pour ne rien casser."
|
|
|
|
|
loop: "{{ serveur_postgresql_tls_stat.results | default([]) }}"
|
|
|
|
|
loop_control:
|
|
|
|
|
label: "{{ item.item }}"
|
|
|
|
|
when:
|
|
|
|
|
- serveur_postgresql_tls_actif | bool
|
|
|
|
|
- not ansible_check_mode
|
|
|
|
|
|
2026-06-24 20:17:46 -04:00
|
|
|
- name: Deployer la configuration Set-OPS
|
|
|
|
|
ansible.builtin.template:
|
|
|
|
|
src: 99-setops.conf.j2
|
|
|
|
|
dest: "{{ serveur_postgresql_conf_dir }}/conf.d/99-setops.conf"
|
|
|
|
|
owner: postgres
|
|
|
|
|
group: postgres
|
|
|
|
|
mode: "0640"
|
|
|
|
|
notify: Redemarrer PostgreSQL
|
|
|
|
|
|
|
|
|
|
- name: Deployer pg_hba.conf
|
|
|
|
|
ansible.builtin.template:
|
|
|
|
|
src: pg_hba.conf.j2
|
|
|
|
|
dest: "{{ serveur_postgresql_conf_dir }}/pg_hba.conf"
|
|
|
|
|
owner: postgres
|
|
|
|
|
group: postgres
|
|
|
|
|
mode: "0640"
|
|
|
|
|
notify: Recharger PostgreSQL
|
|
|
|
|
|
|
|
|
|
- name: Activer et demarrer PostgreSQL
|
2026-07-01 21:01:53 -04:00
|
|
|
when: not ansible_check_mode
|
2026-06-24 20:17:46 -04:00
|
|
|
ansible.builtin.systemd:
|
|
|
|
|
name: "{{ serveur_postgresql_service_name }}"
|
|
|
|
|
enabled: true
|
|
|
|
|
state: started
|
|
|
|
|
|
|
|
|
|
- name: Appliquer la configuration en attente avant le provisioning
|
|
|
|
|
ansible.builtin.meta: flush_handlers
|
|
|
|
|
|
|
|
|
|
- name: Creer les comptes applicatifs PostgreSQL
|
|
|
|
|
community.postgresql.postgresql_user:
|
|
|
|
|
name: "{{ item.nom }}"
|
|
|
|
|
password: "{{ item.mot_de_passe }}"
|
|
|
|
|
role_attr_flags: "{{ item.attributs | default('LOGIN') }}"
|
|
|
|
|
state: present
|
|
|
|
|
become: true
|
|
|
|
|
become_user: postgres
|
|
|
|
|
loop: "{{ serveur_postgresql_comptes }}"
|
|
|
|
|
loop_control:
|
|
|
|
|
label: "{{ item.nom }}"
|
|
|
|
|
no_log: true
|
|
|
|
|
when: serveur_postgresql_comptes | length > 0
|
|
|
|
|
|
|
|
|
|
- name: Creer les bases applicatives PostgreSQL
|
|
|
|
|
community.postgresql.postgresql_db:
|
|
|
|
|
name: "{{ item.nom }}"
|
|
|
|
|
owner: "{{ item.proprietaire | default(item.nom) }}"
|
|
|
|
|
encoding: "{{ item.encodage | default('UTF8') }}"
|
|
|
|
|
state: present
|
|
|
|
|
become: true
|
|
|
|
|
become_user: postgres
|
|
|
|
|
loop: "{{ serveur_postgresql_bases }}"
|
|
|
|
|
loop_control:
|
|
|
|
|
label: "{{ item.nom }}"
|
|
|
|
|
when: serveur_postgresql_bases | length > 0
|
|
|
|
|
|
|
|
|
|
# --- Provisioning depuis le registre partage instance/plan/bases-donnees.yml ---
|
|
|
|
|
|
|
|
|
|
- name: Charger le registre des bases applicatives
|
|
|
|
|
ansible.builtin.include_vars:
|
|
|
|
|
file: "{{ setops_plan_dir }}/bases-donnees.yml"
|
|
|
|
|
failed_when: false
|
|
|
|
|
|
|
|
|
|
- name: Determiner les serveurs BD nommes heberges par ce groupe
|
|
|
|
|
ansible.builtin.set_fact:
|
|
|
|
|
serveur_postgresql_noms_bd: >-
|
|
|
|
|
{{ (serveurs_bd | default({})) | dict2items
|
|
|
|
|
| selectattr('value.groupe', 'defined')
|
|
|
|
|
| selectattr('value.groupe', 'equalto', serveur_postgresql_groupe)
|
|
|
|
|
| map(attribute='key') | list }}
|
|
|
|
|
|
|
|
|
|
- name: Selectionner les bases hebergees par ce serveur
|
|
|
|
|
ansible.builtin.set_fact:
|
|
|
|
|
serveur_postgresql_registre: >-
|
|
|
|
|
{{ (bases_donnees | default({})) | dict2items
|
|
|
|
|
| selectattr('value.serveur', 'defined')
|
|
|
|
|
| selectattr('value.serveur', 'in', serveur_postgresql_noms_bd)
|
|
|
|
|
| list }}
|
|
|
|
|
|
|
|
|
|
- name: Creer les comptes proprietaires (registre)
|
|
|
|
|
community.postgresql.postgresql_user:
|
|
|
|
|
name: "{{ item.value.proprietaire }}"
|
|
|
|
|
password: "{{ lookup('vars', item.value.secret) }}"
|
|
|
|
|
role_attr_flags: "LOGIN"
|
|
|
|
|
state: present
|
|
|
|
|
become: true
|
|
|
|
|
become_user: postgres
|
|
|
|
|
loop: "{{ serveur_postgresql_registre }}"
|
|
|
|
|
loop_control:
|
|
|
|
|
label: "{{ item.value.proprietaire }}"
|
|
|
|
|
no_log: true
|
|
|
|
|
when: serveur_postgresql_registre | length > 0
|
|
|
|
|
|
|
|
|
|
- name: Creer les bases applicatives (registre)
|
|
|
|
|
community.postgresql.postgresql_db:
|
|
|
|
|
name: "{{ item.value.base }}"
|
|
|
|
|
owner: "{{ item.value.proprietaire }}"
|
|
|
|
|
encoding: UTF8
|
|
|
|
|
state: present
|
|
|
|
|
become: true
|
|
|
|
|
become_user: postgres
|
|
|
|
|
loop: "{{ serveur_postgresql_registre }}"
|
|
|
|
|
loop_control:
|
|
|
|
|
label: "{{ item.value.base }}"
|
|
|
|
|
when: serveur_postgresql_registre | length > 0
|
sondes : les cinq qui manquaient le plus (autorite, base, annuaire, zones, edge)
CORRECTION DE COMPTE D ABORD. J avais annonce cinq groupes sans sonde. La
mesure en donne VINGT-SEPT : j avais compte ceux que j avais en tete, pas
ceux que le depot contient. Il en reste vingt-deux.
LES CINQ, PAR ORDRE DE DEGAT SILENCIEUX.
autorite (step_ca) — la plus urgente : nos certificats vivent 24 h, une AC
muette ne casse rien aujourd hui et casse TOUT demain, d un coup, sur les
21 machines. Elle surveille aussi l expiration de la RACINE, que personne
ne regarde parce qu elle vit des annees (relevee a 3642 jours).
base (postgresql) — une VRAIE requete, pas pg_isready : celui-ci dit que
le port repond, pas que la base sert. Plus le compte des connexions : a
saturation, chaque application tombe sans que la base ait l air morte.
annuaire (openldap) — elle COMPTE les entrees. Un annuaire vide repond
success a tout : le mensonge des sauvegardes vides, vert et sans contenu.
zones (powerdns) — un autoritatif sans zone repond NXDOMAIN a tout, ce qui
se lit comme « ce nom n existe pas ».
edge (nginx) — elle valide la configuration SUR DISQUE : nginx sert la
derniere valide, et une configuration cassee ne se voit qu au prochain
demarrage, souvent des mois plus tard.
Les cinq eprouvees vertes sur le sain puis rouges PAR PARAMETRE, sans
toucher a un service.
Etat : 20 sondes sur 19 roles, 23 services distincts, 70 instances, 67 au
vert. Les trois autres sont connues : deux sauvegardes sans donnee a
emporter, et Loki en delai de stabilisation apres redeploiement.
make prouver : CONFORME, 64 OK, 0 echec, 0 saute.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-10 03:47:03 -04:00
|
|
|
|
restauration : la reconstruction remet l'etat de l'incarnation precedente
Chaque role proprietaire (AC, bases, annuaire, Nextcloud, rspamd/DKIM,
courriel, forge, web) remet son etat au moment ou il le creerait neuf,
depuis le dernier instantane anterieur a la naissance de la machine.
La sauvegarde refuse de deposer tant qu'un etat d'avant attend.
Outil de noeud setops-restaurer ; cibles sauvegarder-maintenant,
restauration-etat, restauration-renoncer ; test_restauration.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 02:39:22 -04:00
|
|
|
# --- LES BASES D'UNE INCARNATION PRECEDENTE (2026-09-30) ----------------------------
|
|
|
|
|
#
|
|
|
|
|
# ICI, et pas plus tard : les bases viennent d'etre creees, leurs consommateurs (Keycloak,
|
|
|
|
|
# Forgejo...) ne les ont pas encore touchees. Chaque base VIDE recoit sa section du
|
|
|
|
|
# `pg_dumpall` d'avant, en une transaction ; une base qui a deja des tables n'est jamais
|
|
|
|
|
# ecrasee d'office. Les roles et leurs mots de passe restent ceux de la voute : la section
|
|
|
|
|
# d'une base ne les porte pas.
|
|
|
|
|
- name: Restauration — quelles bases sont vierges ?
|
|
|
|
|
ansible.builtin.command:
|
|
|
|
|
argv:
|
|
|
|
|
- psql
|
|
|
|
|
- -tAX
|
|
|
|
|
- -d
|
|
|
|
|
- "{{ item }}"
|
|
|
|
|
- -c
|
|
|
|
|
- >-
|
|
|
|
|
select count(*) from information_schema.tables
|
|
|
|
|
where table_schema not in ('pg_catalog','information_schema')
|
|
|
|
|
become: true
|
|
|
|
|
become_user: postgres
|
|
|
|
|
loop: "{{ serveur_postgresql_bases_restaurables }}"
|
|
|
|
|
register: serveur_postgresql_tables_initiales
|
|
|
|
|
changed_when: false
|
|
|
|
|
check_mode: false
|
|
|
|
|
failed_when: false
|
|
|
|
|
|
|
|
|
|
- name: Restauration — retenir les bases vierges
|
|
|
|
|
ansible.builtin.set_fact:
|
|
|
|
|
serveur_postgresql_bases_vierges: >-
|
|
|
|
|
{{ serveur_postgresql_tables_initiales.results
|
|
|
|
|
| selectattr('rc', 'equalto', 0)
|
|
|
|
|
| selectattr('stdout', 'equalto', '0')
|
|
|
|
|
| map(attribute='item') | list }}
|
|
|
|
|
|
|
|
|
|
- name: Restauration — les bases d'une incarnation precedente ?
|
|
|
|
|
ansible.builtin.include_role:
|
|
|
|
|
name: client_backup
|
|
|
|
|
tasks_from: restaurer.yml
|
|
|
|
|
vars:
|
|
|
|
|
client_backup_restaurer_jeu: serveur_postgresql
|
|
|
|
|
client_backup_restaurer_vierge: "{{ serveur_postgresql_bases_vierges | length > 0 }}"
|
|
|
|
|
|
|
|
|
|
- name: Restauration — rejouer chaque base vierge
|
|
|
|
|
changed_when: true
|
|
|
|
|
ansible.builtin.command:
|
|
|
|
|
argv:
|
|
|
|
|
- /usr/local/sbin/setops-restaurer
|
|
|
|
|
- base
|
|
|
|
|
- --instantane
|
|
|
|
|
- "{{ client_backup_restauration.instantane }}"
|
|
|
|
|
- "{{ item }}"
|
|
|
|
|
loop: "{{ serveur_postgresql_bases_vierges }}"
|
|
|
|
|
when: client_backup_restauration.etat == 'a_restaurer'
|
|
|
|
|
|
|
|
|
|
- name: Restauration — acter les bases remises
|
|
|
|
|
ansible.builtin.include_role:
|
|
|
|
|
name: client_backup
|
|
|
|
|
tasks_from: acter.yml
|
|
|
|
|
vars:
|
|
|
|
|
client_backup_acter_etat: restaure
|
|
|
|
|
when: client_backup_restauration.etat == 'a_restaurer'
|
|
|
|
|
|
sondes : les cinq qui manquaient le plus (autorite, base, annuaire, zones, edge)
CORRECTION DE COMPTE D ABORD. J avais annonce cinq groupes sans sonde. La
mesure en donne VINGT-SEPT : j avais compte ceux que j avais en tete, pas
ceux que le depot contient. Il en reste vingt-deux.
LES CINQ, PAR ORDRE DE DEGAT SILENCIEUX.
autorite (step_ca) — la plus urgente : nos certificats vivent 24 h, une AC
muette ne casse rien aujourd hui et casse TOUT demain, d un coup, sur les
21 machines. Elle surveille aussi l expiration de la RACINE, que personne
ne regarde parce qu elle vit des annees (relevee a 3642 jours).
base (postgresql) — une VRAIE requete, pas pg_isready : celui-ci dit que
le port repond, pas que la base sert. Plus le compte des connexions : a
saturation, chaque application tombe sans que la base ait l air morte.
annuaire (openldap) — elle COMPTE les entrees. Un annuaire vide repond
success a tout : le mensonge des sauvegardes vides, vert et sans contenu.
zones (powerdns) — un autoritatif sans zone repond NXDOMAIN a tout, ce qui
se lit comme « ce nom n existe pas ».
edge (nginx) — elle valide la configuration SUR DISQUE : nginx sert la
derniere valide, et une configuration cassee ne se voit qu au prochain
demarrage, souvent des mois plus tard.
Les cinq eprouvees vertes sur le sain puis rouges PAR PARAMETRE, sans
toucher a un service.
Etat : 20 sondes sur 19 roles, 23 services distincts, 70 instances, 67 au
vert. Les trois autres sont connues : deux sauvegardes sans donnee a
emporter, et Loki en delai de stabilisation apres redeploiement.
make prouver : CONFORME, 64 OK, 0 echec, 0 saute.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-10 03:47:03 -04:00
|
|
|
# --- Sonde de supervision (docs/supervision-conception.md) --------------------------
|
|
|
|
|
# Le role qui possede la verite depose sa propre sonde ; le porteur (`client_sante`) la
|
|
|
|
|
# fait tourner et pousse le verdict, sans savoir ce qu'elle mesure.
|
|
|
|
|
- name: Assurer le repertoire des sondes de supervision
|
|
|
|
|
ansible.builtin.file:
|
|
|
|
|
path: /usr/local/lib/setops/sondes
|
|
|
|
|
state: directory
|
|
|
|
|
owner: root
|
|
|
|
|
group: root
|
|
|
|
|
mode: "0755"
|
|
|
|
|
|
|
|
|
|
- name: Deposer la sonde « base »
|
|
|
|
|
ansible.builtin.template:
|
|
|
|
|
src: sonde-base.sh.j2
|
|
|
|
|
dest: /usr/local/lib/setops/sondes/base.sh
|
|
|
|
|
owner: root
|
|
|
|
|
group: root
|
|
|
|
|
mode: "0750"
|
2026-09-14 11:35:14 -04:00
|
|
|
|
|
|
|
|
# --- L'EXPORTATEUR DE METRIQUES -------------------------------------------------------
|
|
|
|
|
#
|
|
|
|
|
# CE QU'IL AJOUTE A LA SONDE : la sonde repond « est-ce casse ? » et ne garde aucune
|
|
|
|
|
# memoire. L'exportateur publie des SERIES — ce qui derive lentement n'a que le graphe
|
|
|
|
|
# pour se faire voir. Voir `meta/metriques.yml` pour ce que le moteur en derive.
|
|
|
|
|
- name: Creer le compte de metriques (lecture seule, pg_monitor)
|
|
|
|
|
community.postgresql.postgresql_user:
|
|
|
|
|
name: "{{ serveur_postgresql_exportateur_utilisateur }}"
|
|
|
|
|
password: "{{ serveur_postgresql_exportateur_mot_de_passe }}"
|
|
|
|
|
role_attr_flags: "LOGIN"
|
|
|
|
|
state: present
|
|
|
|
|
become: true
|
|
|
|
|
become_user: postgres
|
|
|
|
|
no_log: true
|
|
|
|
|
when: serveur_postgresql_exportateur_actif | bool
|
|
|
|
|
|
|
|
|
|
# `pg_monitor` DONNE LES VUES DE STATISTIQUES, ET RIEN D'AUTRE. Pas une ligne de donnee
|
|
|
|
|
# applicative. C'est la difference entre superviser une base et l'ouvrir.
|
|
|
|
|
- name: Donner a ce compte le role de supervision, et lui seul
|
|
|
|
|
community.postgresql.postgresql_membership:
|
|
|
|
|
group: pg_monitor
|
|
|
|
|
target_role: "{{ serveur_postgresql_exportateur_utilisateur }}"
|
|
|
|
|
state: present
|
|
|
|
|
become: true
|
|
|
|
|
become_user: postgres
|
|
|
|
|
when: serveur_postgresql_exportateur_actif | bool
|
|
|
|
|
|
|
|
|
|
- name: Installer l'exportateur de metriques
|
|
|
|
|
ansible.builtin.apt:
|
|
|
|
|
name: "{{ serveur_postgresql_exportateur_paquet }}"
|
|
|
|
|
state: present
|
|
|
|
|
when: serveur_postgresql_exportateur_actif | bool
|
|
|
|
|
|
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
|
|
|
# --- TLS DE L'EXPORTATEUR (2026-10-12) -----------------------------------------------
|
|
|
|
|
# Le 9187 etait le seul port de metriques en clair, chez les locataires comme au site :
|
|
|
|
|
# une dette inscrite au registre le 2026-09-14. Meme pont que `client_metrique` : le
|
|
|
|
|
# certificat de la machine, copie la ou `prometheus` le lit, recopie a chaque renouvellement.
|
|
|
|
|
- name: Exportateur TLS — repertoire des certificats (prometheus)
|
|
|
|
|
ansible.builtin.file:
|
|
|
|
|
path: "{{ serveur_postgresql_exportateur_tls_dir }}"
|
|
|
|
|
state: directory
|
|
|
|
|
owner: prometheus
|
|
|
|
|
group: prometheus
|
|
|
|
|
mode: "0700"
|
|
|
|
|
when:
|
|
|
|
|
- serveur_postgresql_exportateur_actif | bool
|
|
|
|
|
- serveur_postgresql_tls_actif | bool
|
|
|
|
|
|
|
|
|
|
- name: Exportateur TLS — script de synchronisation du certificat
|
|
|
|
|
ansible.builtin.template:
|
|
|
|
|
src: setops-pg-exportateur-cert-sync.sh.j2
|
|
|
|
|
dest: /usr/local/sbin/setops-pg-exportateur-cert-sync
|
|
|
|
|
owner: root
|
|
|
|
|
group: root
|
|
|
|
|
mode: "0755"
|
|
|
|
|
when:
|
|
|
|
|
- serveur_postgresql_exportateur_actif | bool
|
|
|
|
|
- serveur_postgresql_tls_actif | bool
|
|
|
|
|
|
|
|
|
|
- name: Exportateur TLS — unites de synchronisation (service + path)
|
|
|
|
|
ansible.builtin.template:
|
|
|
|
|
src: "{{ item.s }}"
|
|
|
|
|
dest: "{{ item.d }}"
|
|
|
|
|
owner: root
|
|
|
|
|
group: root
|
|
|
|
|
mode: "0644"
|
|
|
|
|
loop:
|
|
|
|
|
- { s: "setops-pg-exportateur-cert-sync.service.j2", d: "/etc/systemd/system/setops-pg-exportateur-cert-sync.service" }
|
|
|
|
|
- { s: "setops-pg-exportateur-cert-sync.path.j2", d: "/etc/systemd/system/setops-pg-exportateur-cert-sync.path" }
|
|
|
|
|
loop_control:
|
|
|
|
|
label: "{{ item.d | basename }}"
|
|
|
|
|
when:
|
|
|
|
|
- serveur_postgresql_exportateur_actif | bool
|
|
|
|
|
- serveur_postgresql_tls_actif | bool
|
|
|
|
|
|
|
|
|
|
- name: Exportateur TLS — activer la surveillance du certificat (path)
|
|
|
|
|
ansible.builtin.systemd:
|
|
|
|
|
name: setops-pg-exportateur-cert-sync.path
|
|
|
|
|
enabled: true
|
|
|
|
|
state: started
|
|
|
|
|
daemon_reload: true
|
|
|
|
|
when:
|
|
|
|
|
- serveur_postgresql_exportateur_actif | bool
|
|
|
|
|
- serveur_postgresql_tls_actif | bool
|
|
|
|
|
- not ansible_check_mode
|
|
|
|
|
|
|
|
|
|
- name: Exportateur TLS — synchroniser le certificat maintenant
|
|
|
|
|
ansible.builtin.command: /usr/local/sbin/setops-pg-exportateur-cert-sync
|
|
|
|
|
changed_when: false
|
|
|
|
|
when:
|
|
|
|
|
- serveur_postgresql_exportateur_actif | bool
|
|
|
|
|
- serveur_postgresql_tls_actif | bool
|
|
|
|
|
- not ansible_check_mode
|
|
|
|
|
|
|
|
|
|
- name: Exportateur TLS — garde-fou, ne pas activer sans certificat en place
|
|
|
|
|
ansible.builtin.stat:
|
|
|
|
|
path: "{{ serveur_postgresql_exportateur_tls_dir }}/exportateur.key"
|
|
|
|
|
register: serveur_postgresql_exportateur_cle
|
|
|
|
|
when:
|
|
|
|
|
- serveur_postgresql_exportateur_actif | bool
|
|
|
|
|
- serveur_postgresql_tls_actif | bool
|
|
|
|
|
- not ansible_check_mode
|
|
|
|
|
|
|
|
|
|
- name: Exportateur TLS — exiger le certificat synchronise
|
|
|
|
|
ansible.builtin.assert:
|
|
|
|
|
that:
|
|
|
|
|
- serveur_postgresql_exportateur_cle.stat.exists
|
|
|
|
|
fail_msg: >-
|
|
|
|
|
Certificat de l'exportateur absent ({{ serveur_postgresql_exportateur_tls_dir }}) :
|
|
|
|
|
la machine porte-t-elle client_pki ? TLS non active, plutot qu'un exportateur muet.
|
|
|
|
|
when:
|
|
|
|
|
- serveur_postgresql_exportateur_actif | bool
|
|
|
|
|
- serveur_postgresql_tls_actif | bool
|
|
|
|
|
- not ansible_check_mode
|
|
|
|
|
|
|
|
|
|
- name: Exportateur TLS — deployer la web-config
|
|
|
|
|
ansible.builtin.template:
|
|
|
|
|
src: web-config-exportateur.yml.j2
|
|
|
|
|
dest: "{{ serveur_postgresql_exportateur_web_config }}"
|
|
|
|
|
owner: root
|
|
|
|
|
group: prometheus
|
|
|
|
|
mode: "0640"
|
|
|
|
|
notify: Redemarrer l exportateur de metriques
|
|
|
|
|
when:
|
|
|
|
|
- serveur_postgresql_exportateur_actif | bool
|
|
|
|
|
- serveur_postgresql_tls_actif | bool
|
|
|
|
|
|
2026-09-14 11:35:14 -04:00
|
|
|
# LA CHAINE DE CONNEXION NE PASSE PAS PAR LA LIGNE DE COMMANDE. Un `DATA_SOURCE_NAME` en
|
|
|
|
|
# argument serait lisible dans `ps` par tout le monde sur la machine ; dans un fichier a
|
|
|
|
|
# 0600, il ne l'est que par root et le service.
|
|
|
|
|
- name: Poser la chaine de connexion de l'exportateur
|
|
|
|
|
ansible.builtin.copy:
|
|
|
|
|
content: |
|
|
|
|
|
# Gere par Set-OPS (role serveur_postgresql). Ne pas editer a la main.
|
|
|
|
|
DATA_SOURCE_NAME="postgresql://{{ serveur_postgresql_exportateur_utilisateur
|
|
|
|
|
}}:{{ serveur_postgresql_exportateur_mot_de_passe
|
|
|
|
|
}}@127.0.0.1:5432/postgres?sslmode=disable"
|
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
|
|
|
ARGS="--web.listen-address=:{{ serveur_postgresql_exportateur_port }}{{
|
|
|
|
|
(' --web.config.file=' ~ serveur_postgresql_exportateur_web_config)
|
|
|
|
|
if serveur_postgresql_tls_actif | bool else '' }}"
|
2026-09-14 11:35:14 -04:00
|
|
|
dest: /etc/default/prometheus-postgres-exporter
|
|
|
|
|
owner: root
|
|
|
|
|
group: root
|
|
|
|
|
mode: "0600"
|
|
|
|
|
no_log: true
|
|
|
|
|
notify: Redemarrer l exportateur de metriques
|
|
|
|
|
when: serveur_postgresql_exportateur_actif | bool
|
|
|
|
|
|
|
|
|
|
- name: Activer et demarrer l'exportateur de metriques
|
|
|
|
|
ansible.builtin.systemd:
|
|
|
|
|
name: "{{ serveur_postgresql_exportateur_service }}"
|
|
|
|
|
enabled: true
|
|
|
|
|
state: started
|
|
|
|
|
when: serveur_postgresql_exportateur_actif | bool
|