Le cache du site couvrait apt ; quatre artefacts arrivaient autrement, parce qu ils ne vivent dans aucun depot apt. Le controleur les tire puis les pousse par SSH. Mesure du 2026-09-12 : le cache du runner du site est ABSENT. Un second locataire monte depuis lui sortait chercher 570 Mo sur codeberg.org, github.com et download.nextcloud.com, alors que le meme ecosysteme ne demandait plus un seul paquet a Debian. Le poste du mainteneur les a depuis toujours : personne ne l avait vu. Pas de relais transparent, et la mesure tranche : github.com redirige vers une URL signee valable une heure, differente a chaque requete. Un cache qui la prend pour cle ne fait jamais mouche. Le relais marcherait pour deux amonts sur quatre. Donc un vrai depot, dans le service qui existe deja. LocalDirs d apt-cacher-ng publie un repertoire du disque sous un prefixe, eprouve AVANT d ecrire le role. Aucun service, aucun port, aucun certificat, aucun flux nouveaux : l ingress 3142 pair flotte couvre exactement ce chemin. Les versions ne sont pas recopiees : le role lit les defauts des quatre consommateurs. Les quatre roles recoivent une tache AJOUTEE, placee avant leur stat de cache — si le depot sert, le stat le voit et la tache amont se saute d elle-meme. Aucune tache existante n a change. P70 exige que tout dest ecrit sous un cache_local figure au depot. Une liste qui suit une autre prend du retard ; celle-ci est nee avec sa garde. Deux marches payees en chemin : - failed_when: false REECRIT le verdict, donc la premiere garde de signature ne gardait rien. Elles mesurent le fichier desormais. - file: state=directory cree les parents en 0750 : apt-cacher-ng, qui ne tourne pas en root, rendait 403 sur chaque fichier. Un chemin se traverse en entier. Verifie sur l infrastructure : 6/6 artefacts servis (200/206) depuis le runner du site ET depuis une machine du locataire a travers la frontiere ; les 6 empreintes SHA-256 sont identiques a celles qui ont construit Chezlepro ; second passage changed=0. make prouver : 69 OK, 0 echec, 1 saute. ansible-lint : 0 failure, profil production. Inclut aussi force: true sur cinq telechargements de cles : une reprise conditionnelle ne reprend rien (304 Not Modified, size 0, attempts 5). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
204 lines
9 KiB
YAML
204 lines
9 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
|