Set-OPS-Public/roles/serveur_collabora/tasks/main.yml
Daniel Allaire f815b069f7 supervision : le lot check_http, et les greffons qui manquaient a la flotte
La doctrine dit qu un greffon Nagios EST une sonde valide et compte sur les 54
de monitoring-plugins. Mesure : aucun n etait installe. Le document decrivait
une possibilite qui n existait pas, et chaque role ayant besoin d un controle
HTTP n avait d autre choix que d ecrire du shell.

Le PORTEUR les installe desormais — c est son affaire de pouvoir executer des
sondes, et les poser role par role en ferait autant de copies de la decision.

Trois sondes, trois enveloppes qui delegue a check_http et rendent son code tel
quel :

  collabora    edition     /hosting/discovery — l adresse que Nextcloud interroge
                           lui-meme ; sans elle le bouton ouvrir meurt et
                           Nextcloud reste vert
  rspamd       filtrage    un filtre muet ne bloque pas le courrier, il le laisse
                           passer
  oauth2_proxy passerelle  ce qui tombe avec elle n est pas elle : les services
                           derriere restent debout et deviennent injoignables

Chacune exige une CHAINE que seul le bon service produit — un 200 peut venir
d une page d erreur. Cette chaine est aussi la mise en defaut.

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

222 lines
9.5 KiB
YAML

---
# LA CONDITION EST ECRITE D'UN SEUL TENANT, ET C'EST DELIBERE.
#
# `scripts/verifier_intrants.py` lit les `assert` de la forme `var | length > 0` comme un
# CONTRAT : l'instance doit fournir cette valeur. Et il ignore volontairement les `when:`
# — un intrant exige sous condition reste un intrant exige.
#
# Ecrite en deux morceaux (`that: var | length > 0` + `when: actif`), cette garde
# promettait donc un secret pour une console FERMEE, et P32 le refusait a juste titre.
# Ecrite en une implication, elle dit ce qu'elle veut vraiment dire : SI la console est
# ouverte, ALORS un mot de passe. Ne pas la « simplifier » en la recoupant en deux.
- name: Exiger un secret si — et seulement si — la console d'administration est ouverte
ansible.builtin.assert:
that:
- not (serveur_collabora_admin_actif | bool) or (serveur_collabora_admin_mot_de_passe | length > 0)
fail_msg: >-
La console d'administration est ouverte mais son mot de passe est vide.
Le renseigner depuis la voûte de l'instance, ou refermer la console
(`serveur_collabora_admin_actif: false`).
- name: Assurer le repertoire des trousseaux apt
ansible.builtin.file:
path: /etc/apt/keyrings
state: directory
owner: root
group: root
mode: "0755"
# Récupérée UNE FOIS, comme la clé Smallstep : une plateforme souveraine ne recontacte pas
# un serveur étranger à chaque déploiement pour une clé déjà installée. Une rotation amont
# ne passe pas inaperçue pour autant — `apt` refuse alors le dépôt, bruyamment.
# 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 ? (cle Collabora)
ansible.builtin.stat:
path: "{{ serveur_collabora_depot_cle_fichier }}"
register: serveur_collabora_cle_presente
# ... 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: "{{ serveur_collabora_depot_cle_fichier }}"
state: absent
when:
- serveur_collabora_cle_presente.stat.exists
- (serveur_collabora_cle_presente.stat.size | default(0)) == 0
- name: Telecharger la cle de signature Collabora
when: not (serveur_collabora_cle_presente.stat.exists and serveur_collabora_cle_presente.stat.size | default(0) > 0)
# `force` PARCE QU'UNE REPRISE CONDITIONNELLE NE REPREND RIEN (2026-09-12).
#
# Sans lui, `get_url` envoie un `If-Modified-Since` des que le fichier existe. Le cache
# repond `304 Not Modified`, la tache reussit sans rien ecrire, et la boucle `until`
# — qui exige une taille non nulle — retente cinq fois contre un serveur qui repondra
# toujours 304 :
#
# HTTP Error 304: Not Modified size: 0 attempts: 5
#
# C'est la boucle de reprise qui se bat contre elle-meme. Le `when:` ci-dessus garantit
# deja qu'on ne retelecharge pas une cle valide ; `force` ne concerne donc que le cas
# ou l'on a DECIDE d'aller chercher.
ansible.builtin.get_url:
force: true
url: "{{ serveur_collabora_depot_cle_url }}"
dest: "{{ serveur_collabora_depot_cle_fichier }}"
owner: root
group: root
mode: "0644"
timeout: 30
register: serveur_collabora_cle
retries: 5
delay: 6
until: serveur_collabora_cle is succeeded and (serveur_collabora_cle.size | default(0)) > 0
# 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: ""
# DEPOT PLAT : `Suites: ./`, aucun composant. Une source classique ne le trouverait pas.
- name: Mesurer la cle de signature Collabora qui vient d'etre posee
ansible.builtin.stat:
path: "{{ serveur_collabora_depot_cle_fichier }}"
register: cle_collabora_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_collabora_posee.stat.size | default(0)) > 0
fail_msg: >-
La cle de signature Collabora ({{ serveur_collabora_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: Ajouter le depot apt Collabora
ansible.builtin.template:
src: collabora.sources.j2
dest: /etc/apt/sources.list.d/collabora.sources
owner: root
group: root
mode: "0644"
- name: Installer Collabora Online
when: not ansible_check_mode # dry-run : le depot n'est pas reellement ajoute
ansible.builtin.apt:
name: "{{ serveur_collabora_paquets }}"
state: present
update_cache: true
- name: Assurer le repertoire du fragment systemd
ansible.builtin.file:
path: "{{ serveur_collabora_fragment | dirname }}"
state: directory
owner: root
group: root
mode: "0755"
# LA CONFIGURATION DE SET-OPS TIENT EN UN FICHIER, et le coolwsd.xml du paquet reste
# intact. Voir defaults/main.yml pour le raisonnement.
- name: Deployer la surcharge Set-OPS
ansible.builtin.template:
src: setops.conf.j2
dest: "{{ serveur_collabora_fragment }}"
owner: root
group: root
mode: "0640"
notify: Redemarrer Collabora
- name: Appliquer les redemarrages avant de verifier
ansible.builtin.meta: flush_handlers
- name: Activer et demarrer Collabora
when: not ansible_check_mode
ansible.builtin.systemd:
name: "{{ serveur_collabora_service }}"
enabled: true
state: started
daemon_reload: true
# ECRIRE, PUIS RELIRE (D-68). Un service « active » qui ne repond pas a `capabilities`
# est un vert sur un perimetre vide : Nextcloud le decouvrirait a la premiere ouverture
# de document, c'est-a-dire devant un usager.
- name: Collabora repond-il vraiment ?
when: not ansible_check_mode
ansible.builtin.uri:
url: "http://127.0.0.1:{{ serveur_collabora_port }}/hosting/capabilities"
status_code: [200]
timeout: 20
register: serveur_collabora_capacites
retries: 12
delay: 5
until: serveur_collabora_capacites is succeeded
# LE ROLE QUI POSSEDE LA VERITE DEPOSE SA PROPRE SONDE. `client_sante` la fait tourner et
# pousse le resultat ; les greffons standard, eux, sont poses par le porteur.
- 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 « edition »
ansible.builtin.template:
src: sonde-edition.sh.j2
dest: /usr/local/lib/setops/sondes/edition.sh
owner: root
group: root
mode: "0750"