From 77dcc72aa6ac44ac3cea653bc6a329f9140ef5fc Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Tue, 7 Jul 2026 03:19:51 -0400 Subject: [PATCH] Recette d'acceptation : make valider (phase 4, v1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vérifie que les services FONCTIONNENT, pas juste qu'ils sont déployés — complète les *-verifier (statiques, sur le plan) par une recette fonctionnelle vivante. playbooks/valider.yml (lecture seule) : - Prometheus : toutes les cibles de supervision UP (API /targets) - Edge : les vhosts HTTPS exposés répondent (server_name réels filtrés sur domaine_interne, statut sain 200/301/302/401/403) Éprouvé sur la flotte : 7 cibles UP, 8 vhosts OK (sous pare-feu actif). À venir : courriel bout-en-bout (envoi->IMAP), restauration de sauvegarde. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 6 ++++ Makefile | 4 +++ playbooks/valider.yml | 70 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 playbooks/valider.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f38da2..5091159 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ ## 2026-07-07 ### Ajouté +- **`make valider` — recette d'acceptation fonctionnelle (phase 4, v1).** Vérifie que les services + *fonctionnent*, pas juste qu'ils sont déployés (complète les `*-verifier` statiques). `playbooks/valider.yml`, + lecture seule : **cibles Prometheus toutes UP** (API `/targets`) + **vhosts HTTPS exposés répondent** + (dérivés des `server_name` réels de l'edge, filtrés sur `domaine_interne` — générique). Éprouvé sur la + flotte vivante : 7 cibles UP, 8 vhosts OK (via l'edge, sous pare-feu actif). À venir : courriel + bout-en-bout (envoi→IMAP), restauration de sauvegarde. - **Pare-feu nftables activé sur TOUTE la flotte (14 nœuds, activation prudente).** Les 14 hôtes actifs tournent sous `policy drop` avec leur ruleset **résolu moindre-privilège** (flux est-ouest déclarés autorisés par source, reste refusé). Vérifié en conditions réelles : flux déclarés OPEN diff --git a/Makefile b/Makefile index 3b947fd..a3dbf2a 100644 --- a/Makefile +++ b/Makefile @@ -308,6 +308,10 @@ flux: ansible-runtime flux-verifier: python3 scripts/resoudre_flux.py verifier +.PHONY: valider +valider: ansible-runtime + ansible-playbook -i $(INVENTAIRE_PRODUCTION) playbooks/valider.yml + .PHONY: wiki-publier wiki-publier: @set -e; \ diff --git a/playbooks/valider.yml b/playbooks/valider.yml new file mode 100644 index 0000000..59812c4 --- /dev/null +++ b/playbooks/valider.yml @@ -0,0 +1,70 @@ +--- +# make valider — RECETTE D'ACCEPTATION : les services FONCTIONNENT (pas juste déployés). +# Lecture seule / faible impact. Complète les *-verifier (statiques, sur le plan) +# et verifier-hote (niveau hôte) par une vérif FONCTIONNELLE de bout en bout. +# +# v1 : cibles Prometheus UP + endpoints HTTPS exposés répondent. +# À venir : courriel bout-en-bout (envoi→IMAP), restauration de sauvegarde. + +- name: "Recette — Prometheus : toutes les cibles de supervision UP" + hosts: serveur_prometheus + gather_facts: false + become: false + tasks: + - name: Interroger l'API des cibles actives + ansible.builtin.uri: + url: "http://localhost:9090/api/v1/targets?state=active" + return_content: true + register: prom_targets + + - name: Analyser up / down + vars: + cibles: "{{ (prom_targets.content | from_json).data.activeTargets }}" + en_panne: "{{ cibles | rejectattr('health', 'equalto', 'up') | list }}" + ansible.builtin.set_fact: + recette_prom_total: "{{ cibles | length }}" + recette_prom_down: "{{ en_panne | map(attribute='labels.instance') | list }}" + + - name: "✔ Prometheus" + ansible.builtin.assert: + that: "recette_prom_down | length == 0" + success_msg: "OK — {{ recette_prom_total }} cibles, toutes UP." + fail_msg: "ÉCHEC — cibles DOWN : {{ recette_prom_down }}" + +- name: "Recette — Edge : les vhosts HTTPS exposés répondent" + hosts: serveur_nginx + gather_facts: false + become: true + tasks: + - name: Lister les valeurs de la directive server_name (première colonne exacte) + ansible.builtin.shell: + cmd: >- + nginx -T 2>/dev/null | awk '$1=="server_name"{for(i=2;i<=NF;i++){gsub(/;/,"",$i); + print $i}}' | sort -u + register: recette_server_names + changed_when: false + + - name: Retenir les vhosts du domaine interne (exclut défauts/placeholders) + ansible.builtin.set_fact: + recette_fqdns: >- + {{ recette_server_names.stdout_lines + | select('search', '\.' ~ (domaine_interne | regex_escape) ~ '$') | list }} + + - name: Interroger chaque vhost exposé en HTTPS (via l'edge) + ansible.builtin.uri: + url: "https://{{ item }}/" + validate_certs: false + status_code: [200, 301, 302, 401, 403] + follow_redirects: none + timeout: 10 + loop: "{{ recette_fqdns }}" + register: recette_http + failed_when: false + + - name: "✔ Endpoints HTTPS" + vars: + echecs: "{{ recette_http.results | rejectattr('status', 'in', [200, 301, 302, 401, 403]) | list }}" + ansible.builtin.assert: + that: "echecs | length == 0" + success_msg: "OK — {{ recette_http.results | length }} vhosts répondent : {{ recette_fqdns }}" + fail_msg: "ÉCHEC — vhosts KO : {{ echecs | map(attribute='item') | list }} (statuts {{ echecs | map(attribute='status') | list }})"