Set-OPS-Public/roles/client_sante/tasks/main.yml
Daniel Allaire c6a7150c60 les depots tiers passent par le cache, et le tenant n a plus le sien
Deux mouvements d une seule doctrine : le site fournit tout ce dont un
tenant a besoin pour venir au monde.

1. LES DEPOTS TIERS. Grafana, Icinga, Smallstep et Collabora ne publient
qu en HTTPS ; apt-cacher-ng ne relaie pas un tunnel, et le socle pose donc
Acquire::https::Proxy DIRECT. Chaque machine sortait elle-meme sur
Internet. Le cache declare desormais un Remap par fournisseur, et chaque
role demande en {{ ..._depot_schema }}:// - http des qu un cache est
declare, https sinon. Le TLS n est rompu nulle part : il est TERMINE au
cache, qui est notre machine, et l integrite vient des signatures.

  avant : 4 fournisseurs en HTTPS direct, 14 machines sortant seules
  apres : 0 source en HTTPS direct, 0 erreur apt sur 14 machines

Trois lecons. Le remap appartient au cache qui SORT : pose sur un cache
chaine, il tente le HTTPS a travers son amont et rend 503. apt_repository
AJOUTE au lieu de remplacer, donc l ancienne ligne https sortait toujours.
Et la liste des fournisseurs ne se devine pas - j en avais trois, l audit
en a revele un quatrieme.

2. LE CACHE DU TENANT. Sa ligne portait son propre retrait depuis toujours
- service MUTUALISABLE, un ecosysteme au premier age peut pointer sur celui
de son hote. Retiree. Ce qu on perd, dit franchement : plus de trafic
inter-zone et plus de charge sur site-cache-01, contre un service de moins
a poser, superviser et reproduire.

3. UN ROLE QU ON RETIRE DOIT DEFAIRE CE QU IL A FAIT. Le retrait a montre
que rien ne nettoie derriere. Le fichier apt visait un cache eteint en
ecrasant le plancher qui fonctionnait - le defaut deja paye a quinze
machines. Et les sondes du cache restaient, le porteur poussant pour des
services qu Icinga ne definit plus (404). Le socle retire le premier,
client_sante derive les sondes attendues et retire les orphelines.

P65 refuse tout role visant un depot relaye en https ecrit en dur. Sa
limite est dite : elle empeche une regression sur ce qui est connu, elle ne
decouvre pas l inconnu.

Mesure : Icinga 87 OK sur 96, prouver 65 OK, lint 0 defaut.

Reste, et c est dit : apt-cacher-ng tourne toujours sur forge-01 que plus
aucun plan ne declare.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-10 14:53:48 -04:00

184 lines
6.9 KiB
YAML

---
# LE NOEUD RAPPORTE SES UNITES EN ECHEC. Voir le gabarit du script pour le pourquoi.
#
# CE ROLE N'A PAS A SE DEMANDER S'IL Y A UNE SUPERVISION — IL EN A UNE (2026-09-09).
#
# Il portait une branche « aucun serveur_icinga : rien a poser », et un `when` sur tout le
# bloc. Ni l'une ni l'autre ne pouvait s'executer : `client_sante` est une INTEGRATION
# UNIVERSELLE, et `instancier` ne pose une integration universelle que si son serveur
# existe dans l'ecosysteme. Eprouve sur le modele public — sans Icinga au plan, le groupe
# `client_sante` est ABSENT de l'inventaire genere ; avec, les quatre noeuds y entrent.
# Le role n'est donc jamais appele sans destinataire.
#
# Une garde qui ne peut pas se declencher n'est pas une garde : elle rassure sans rien
# tenir. Ce qui la remplace se declenche vraiment — si ce role s'execute, un destinataire
# ET son secret sont exiges, et l'echec dit LEQUEL des deux manque.
- name: Exiger un destinataire et de quoi lui parler
ansible.builtin.assert:
that:
- client_sante_icinga_hote | length > 0
- client_sante_icinga_motdepasse | length > 0
fail_msg: >-
Rapport de santé impossible :
{{ "aucun hôte `serveur_icinga` dans l'inventaire"
if not client_sante_icinga_hote
else "`vault_icinga_api_depot` absent de la voûte" }}.
Le nœud verrait ses unités en échec sans pouvoir le dire.
- name: Assurer le répertoire des secrets Set-OPS
ansible.builtin.file:
path: /etc/setops
state: directory
owner: root
group: root
mode: "0755"
- name: Déposer le mot de passe d'API Icinga
ansible.builtin.copy:
content: "{{ client_sante_icinga_motdepasse }}\n"
dest: /etc/setops/icinga-api.pass
owner: root
group: root
mode: "0600"
no_log: true
# L'AC d'ICINGA, PAS CELLE DE step-ca : Icinga refuse de servir un certificat qu'il
# n'a pas emis (il renouvelle tout ce qui expire sous 30 jours, nos certificats vivent
# 24 h). Domaine de confiance ferme, pair authentifie malgre tout.
- name: L'AC d'Icinga est-elle déjà disponible ?
ansible.builtin.stat:
path: "{{ client_sante_icinga_ca_source }}"
delegate_to: "{{ client_sante_icinga_hote }}"
register: client_sante_ca_presente
- name: Récupérer l'AC d'Icinga depuis l'hôte de supervision
when: client_sante_ca_presente.stat.exists
ansible.builtin.slurp:
src: "{{ client_sante_icinga_ca_source }}"
delegate_to: "{{ client_sante_icinga_hote }}"
register: client_sante_ca_icinga
- name: Déposer l'AC d'Icinga pour la vérification du pair
when: client_sante_ca_presente.stat.exists
ansible.builtin.copy:
content: "{{ client_sante_ca_icinga.content | b64decode }}"
dest: "{{ client_sante_ca_verification }}"
owner: root
group: root
mode: "0644"
- name: Installer curl pour le rapport passif
ansible.builtin.apt:
name: curl
state: present
# --- UN ROLE QU'ON RETIRE DOIT POUVOIR DEFAIRE CE QU'IL A FAIT (2026-09-10) -----------
#
# Chaque role depose SES sondes dans `/usr/local/lib/setops/sondes/`. Quand un ecosysteme
# cesse de declarer un service — `serveur_artefacts` retire du plan le 2026-09-10, parce
# que le SITE fournit les paquets — l'hote quitte le groupe, le role ne tourne plus, et
# PERSONNE ne retire ses sondes. Le porteur continue alors de les executer et de pousser
# un resultat pour un service qu'Icinga ne definit plus :
#
# ECHEC du rapport Icinga pour « cache-apt » : {"error":404,"status":"No objects found."}
#
# Ce n'est pas qu'un bruit : une sonde orpheline consomme le delai du porteur a chaque
# passage, et son echec ressemble a celui d'une vraie sonde en panne.
#
# CE QUI EST ATTENDU SE DERIVE, comme chez `serveur_icinga` : les `meta/supervision.yml`
# des roles, croises avec les GROUPES de cet hote. On ne retire donc jamais la sonde d'un
# role qui s'applique — seulement celles dont plus aucun groupe ne repond.
- name: Relever les sondes que les roles declarent
ansible.builtin.find:
paths: "{{ role_path }}/.."
patterns: supervision.yml
recurse: true
depth: 3
delegate_to: localhost
become: false
run_once: true
register: client_sante_metas
- name: Lire chaque declaration de supervision
ansible.builtin.slurp:
src: "{{ item.path }}"
delegate_to: localhost
become: false
run_once: true
loop: "{{ client_sante_metas.files }}"
loop_control:
label: "{{ item.path | dirname | dirname | basename }}"
register: client_sante_declarations
- name: Assembler le registre des sondes (role -> sondes)
ansible.builtin.set_fact:
client_sante_registre: >-
{{ dict(client_sante_declarations.results
| map(attribute='item.path') | map('dirname') | map('dirname') | map('basename')
| zip(client_sante_declarations.results
| map(attribute='content') | map('b64decode') | map('from_yaml')
| map(attribute='sondes'))) }}
run_once: true
- name: Deriver les sondes attendues sur CET hote
ansible.builtin.set_fact:
client_sante_attendues: >-
{{ group_names | map('extract', client_sante_registre) | select('defined')
| select('sequence') | flatten | map(attribute='nom') | list }}
- name: Relever les sondes reellement posees
ansible.builtin.find:
paths: "{{ client_sante_repertoire_sondes | default('/usr/local/lib/setops/sondes') }}"
patterns: "*.sh"
register: client_sante_posees
- name: Retirer les sondes qu'aucun groupe de cet hote ne declare plus
ansible.builtin.file:
path: "{{ item.path }}"
state: absent
loop: "{{ client_sante_posees.files }}"
loop_control:
label: "{{ item.path | basename }}"
when: (item.path | basename | regex_replace('\.sh$', '')) not in client_sante_attendues
- name: Déployer le script de rapport de santé
ansible.builtin.template:
src: setops-sante.sh.j2
dest: /usr/local/sbin/setops-sante.sh
owner: root
group: root
mode: "0750"
- name: Déployer l'unité systemd du rapport
ansible.builtin.template:
src: setops-sante.service.j2
dest: /etc/systemd/system/setops-sante.service
owner: root
group: root
mode: "0644"
notify: Recharger systemd
- name: Déployer le minuteur du rapport
ansible.builtin.template:
src: setops-sante.timer.j2
dest: /etc/systemd/system/setops-sante.timer
owner: root
group: root
mode: "0644"
notify: Recharger systemd
- name: Activer le rapport de santé
ansible.builtin.systemd:
name: setops-sante.timer
enabled: true
state: started
daemon_reload: true
# PREMIER RAPPORT IMMEDIAT, et ce n'est pas une commodite : sans lui, le service
# reste « en attente » jusqu'au premier declenchement, et un deploiement qui vient
# d'installer la supervision laisserait un tableau vide qu'on prendrait pour un
# tableau sain.
- name: Rapporter tout de suite (ne pas laisser un tableau vide passer pour sain)
ansible.builtin.command: /usr/local/sbin/setops-sante.sh
changed_when: false
failed_when: false