Set-OPS-Public/roles/client_journal/tasks/main.yml
Daniel Allaire 1b89e83b0c reconstruction a froid : les noms derives naissent justes, et une cle vide tombe
Quatrieme reconstruction depuis zero, 32 min 14 s, 14/14. Elle avait un but
precis : trois roles dependent desormais d une variable que le generateur
pose. A froid, si instancier ne la posait pas, le defaut du role reprendrait
la main en silence — juste pour forge et cloud, faux pour observatoire.

Le certificat ne a froid porte observatoire et vigie, aucun ancien nom, et les
deux retours SSO derivent juste sans reprise.

Un seul echec, sans rapport : sur une machine des quatorze, get_url a rendu
0 octet SANS ERREUR — le cache a servi un 200 au corps vide. Le defaut s est
lu deux cents lignes plus loin, dans un apt qui accusait la signature. La
garde posee hier ne mordait pas : retirer une ressource vide ne vaut qu au
passage suivant, quand le fichier existe deja.

Les cinq roles qui telechargent une cle la mesurent maintenant dans la meme
execution. P68 garde le motif. infra-mail-01 redeployee, make valider a
0 echec sur 14 machines.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-11 07:57:24 -04:00

227 lines
9.9 KiB
YAML

---
- name: Assurer le repertoire des trousseaux apt
ansible.builtin.file:
path: /etc/apt/keyrings
state: directory
owner: root
group: root
mode: "0755"
# Recuperee UNE FOIS. Sans garde, chaque deploiement recontactait le serveur du
# fournisseur : cinq cles x quatorze hotes = soixante-dix allers-retours externes pour
# des cles deja installees, et autant d'occasions qu'un tiers lent fasse tomber le
# deploiement. Arbitrage rendu le 2026-08-09 : une plateforme souveraine ne depend pas
# de six serveurs etrangers pour redeployer ce qu'elle possede deja.
#
# CONSEQUENCE ASSUMEE : une rotation de cle amont n'est plus recuperee toute seule. Elle
# ne passe pas inapercue pour autant — `apt` refuse alors le depot, bruyamment. Pour
# forcer le rafraichissement : supprimer le fichier et rejouer le role.
# UN FICHIER VIDE EXISTE (mesure du 2026-09-10).
#
# Cette garde demandait « ce fichier est-il la ? ». Un telechargement interrompu — un 503,
# un delai depasse, une coupure — laisse un fichier de ZERO octet, qui existe. La tache
# est donc sautee a tous les passages suivants, et la machine garde une ressource morte
# pour toujours, en silence.
#
# CE QUE CA A COUTE : `infra-mail-01` a garde une cle smallstep de 0 octet apres une
# epreuve hors ligne. Treize machines portaient 1022 octets, elle portait le vide :
#
# E: Le depot http://packages.smallstep.com/... n'est pas signe.
#
# Le meme jour, l'index `dists/trixie` du cache du site etait tombe de 140 416 a 4 332
# octets — meme famille, autre endroit. Un telechargement partiel ne se signale pas : il
# se fait passer pour un succes.
#
# On demande donc « est-il ENTIER ? » — a defaut de pouvoir demander « est-il JUSTE ? »,
# ce qui exigerait une empreinte de reference que l'amont ne publie pas toujours.
- name: Cette ressource est-elle deja recuperee ? (Telecharger la cle de signature Gr)
ansible.builtin.stat:
path: "{{ client_journal_depot_cle_fichier }}"
register: telecharger_la_cle_de_signature_grafana_present
# ... ET IL FAUT LE RETIRER, PAS SEULEMENT LE REDEMANDER (mesure du 2026-09-10).
#
# Laisser la garde autoriser la tache ne suffit PAS. `get_url` sur une destination qui
# existe emet une requete CONDITIONNELLE : l'amont repond « non modifie », le module rend
# `ok`, et le fichier corrompu reste en place. Mesure : la tache s'executait bien
# (`ok: [infra-mail-01]`), et la cle faisait toujours 0 octet au passage suivant. Un
# `force: yes` manuel l'a reparee en une seconde.
#
# On efface donc la ruine avant de reconstruire. `state: absent` ne mord que sur un
# fichier vide : une cle valide n'est jamais retiree, et la doctrine « on ne retelecharge
# pas ce qu'on possede deja » reste intacte.
- name: Retirer une ressource VIDE avant de la redemander
ansible.builtin.file:
path: "{{ client_journal_depot_cle_fichier }}"
state: absent
when:
- telecharger_la_cle_de_signature_grafana_present.stat.exists
- (telecharger_la_cle_de_signature_grafana_present.stat.size | default(0)) == 0
- name: Telecharger la cle de signature Grafana
when: not (telecharger_la_cle_de_signature_grafana_present.stat.exists and telecharger_la_cle_de_signature_grafana_present.stat.size | default(0) > 0)
ansible.builtin.get_url:
url: "{{ client_journal_depot_cle_url }}"
dest: "{{ client_journal_depot_cle_fichier }}"
owner: root
group: root
mode: "0644"
# LA CLE PASSE PAR LE CACHE, ELLE AUSSI (mesure du 2026-09-10).
#
# `get_url` ignore la configuration d'apt : le mandataire pose dans
# `/etc/apt/apt.conf.d/` ne vaut que pour apt. Les cles de signature sortaient donc
# TOUJOURS en direct, meme apres que tous les depots soient passes par le cache — un
# trou reste ouvert derriere une porte qu'on croyait fermee.
#
# Et ce n'est pas theorique : depuis `collab-01`, la route directe vers
# `www.collaboraoffice.com` EXPIRE, quand le cache l'atteint sans peine.
#
# en direct grafana 200 collabora TIMEOUT
# via le cache collabora 200
#
# Le meme geste corrige donc les deux : plus rien ne sort, et la machine qui n'avait
# pas de route en trouve une.
#
# DEGRADE, JAMAIS DEVINE : sans cache d'amorcage declare, la valeur est VIDE et
# `get_url` sort en direct comme avant. On n'invente pas un mandataire.
environment:
http_proxy: >-
{{ ('http://' ~ artefacts_amorcage) if (artefacts_amorcage | default('') | string | length > 0) else '' }}
https_proxy: ""
register: client_journal_cle_depot
retries: 5
delay: 6
until: client_journal_cle_depot is succeeded and (client_journal_cle_depot.size | default(0)) > 0
# `apt_repository` AJOUTE, IL NE REMPLACE PAS (mesure du 2026-09-10).
#
# Passer une source de `https://` a `http://` y ecrit une SECONDE ligne au lieu de
# corriger la premiere. apt interroge alors les deux — et l'ancienne continue de sortir
# en direct sur Internet, ce que le passage au cache visait justement a supprimer. Le
# symptome le disait, sur les quatorze machines :
#
# W: La cible Packages est specifiee plusieurs fois dans grafana.list:1 et :2
#
# On retire donc explicitement la forme precedente. `state: absent` ne mord que si la
# ligne existe : sur une machine neuve, cette tache ne fait rien.
- name: Mesurer la cle de signature Grafana qui vient d'etre posee
ansible.builtin.stat:
path: "{{ client_journal_depot_cle_fichier }}"
register: cle_grafana_posee
- name: Exiger une cle NON VIDE avant de s'en servir
# UNE REUSSITE N'EST PAS UN CONTENU (mesure du 2026-09-11). Sur une reconstruction a
# froid, `get_url` a rendu 0 octet sans erreur sur UNE machine des quatorze : le cache
# a servi un 200 au corps vide. La garde « retirer une ressource VIDE » ne mord qu'au
# passage SUIVANT, quand le fichier existe deja — au premier, elle n'a rien a retirer.
#
# Le defaut ne se lisait pas ici. Il se lisait deux cents lignes plus loin, dans un
# `apt` qui accusait la SIGNATURE : « Missing key ..., which is needed to verify
# signature ». Un message qui envoie chercher une cle revoquee chez le fournisseur,
# alors que le fichier local fait zero octet.
ansible.builtin.assert:
that:
- (cle_grafana_posee.stat.size | default(0)) > 0
fail_msg: >-
La cle de signature Grafana ({{ client_journal_depot_cle_fichier }}) fait 0 octet.
Le depot a repondu sans corps — cache vide, ou amont injoignable. apt echouera
plus loin en accusant la signature : c'est ICI qu'il faut regarder.
- name: Retirer la forme HTTPS du depot (elle contournait le cache)
ansible.builtin.apt_repository:
repo: "{{ client_journal_depot_source | replace('http://', 'https://') }}"
filename: grafana
state: absent
when: client_journal_depot_schema == 'http'
- name: Ajouter le depot apt Grafana
ansible.builtin.apt_repository:
repo: "{{ client_journal_depot_source }}"
filename: grafana
state: present
# LE CACHE DU CONTROLEUR D'ABORD, LE DEPOT DISTANT POUR LE RESTE (2026-09-03).
#
# Les depots tiers sont en HTTPS, et `client_artefacts` pose
# `Acquire::https::Proxy "DIRECT"` — ils CONTOURNENT donc le cache du site et sortent sur
# Internet a chaque construction de VM. `make cacher-paquets` les tire une fois, versions
# epinglees et empreintes verifiees ; ce role les depose depuis ce cache.
- name: Poser les paquets tiers depuis le cache du controleur
ansible.builtin.include_role:
name: paquets_tiers
vars:
paquets_tiers_noms: "{{ client_journal_paquets }}"
- name: Installer Grafana Alloy
ansible.builtin.apt:
# CE QUE LE CACHE N'A PAS FOURNI, ET RIEN D'AUTRE. Les dependances Debian passent
# par le cache du site en HTTP ; seuls les paquets tiers non caches exigent Internet.
# Reinstaller ce que le cache vient de poser ferait un `update_cache` inutile — et
# hors ligne, il echouerait APRES un travail deja fait.
name: "{{ client_journal_paquets | difference((paquets_tiers_disponibles | default({})).keys() | list) }}"
state: present
update_cache: true
when: (client_journal_paquets
| difference((paquets_tiers_disponibles | default({})).keys() | list)) | length > 0
- name: Autoriser Alloy a lire le journal systeme
ansible.builtin.user:
name: "{{ client_journal_utilisateur }}"
groups: systemd-journal
append: true
notify: Redemarrer alloy
- name: Deployer la configuration Alloy
ansible.builtin.template:
src: config.alloy.j2
dest: "{{ client_journal_config }}"
owner: root
group: "{{ client_journal_utilisateur }}"
mode: "0640"
notify: Redemarrer alloy
# Le port d'ecoute est IMPOSE ici plutot que subi : voir defaults/main.yml.
- name: Imposer le port de l interface locale d Alloy
ansible.builtin.lineinfile:
path: /etc/default/alloy
regexp: "^CUSTOM_ARGS="
line: >-
CUSTOM_ARGS="--server.http.listen-addr={{ client_journal_http_adresse }}:{{ client_journal_http_port }}"
create: false
notify: Redemarrer alloy
- name: Activer et demarrer Alloy
ansible.builtin.systemd:
name: "{{ client_journal_service }}"
enabled: true
state: started
# LA SONDE VIT AVEC LE ROLE QU'ELLE MESURE (docs/supervision-conception.md). Le porteur
# `client_sante` fait tourner tout ce qui vit dans ce repertoire ; il n'a pas a savoir ce
# que celle-ci 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"
# La sonde compare le compteur de lignes jetees a celui du passage precedent : il lui
# faut un endroit ou s'en souvenir. `/var/lib`, parce que ce n'est pas un cache — le
# perdre ferait passer une cicatrice ancienne pour une perte en cours.
- name: Assurer le repertoire d etat des sondes
ansible.builtin.file:
path: /var/lib/setops
state: directory
owner: root
group: root
mode: "0750"
- name: Deposer la sonde d expedition des journaux
ansible.builtin.template:
src: sonde-journaux.sh.j2
dest: /usr/local/lib/setops/sondes/journaux.sh
owner: root
group: root
mode: "0750"