collabora : en natif, la derniere exception conteneurisee tombe

Le role lancait collabora/code dans Docker -- seule exception de la flotte au
principe « logiciel libre en natif ». Desormais : paquet coolwsd du depot amont,
systemd, derriere l'edge nginx. community.docker est retiree de requirements :
plus aucun role n'a besoin de Docker.

Eprouve avant d'ecrire, sur une Debian 13.6 reelle et sans rien installer :
apt-get install --simulate coolwsd resout jusqu'a « Conf coolwsd (26.04.3.1-1) »,
libgcc1 est fourni par libgcc-s1, et le depot CODE-deb est PLAT.

La configuration passe par un fragment systemd (--o:) : le coolwsd.xml livre,
439 lignes commentees, reste intact.

Trois outils du harnais ont pese. voute.py lisait les COMMENTAIRES : documenter
le nom d'une clef suffisait a l'exiger -- corrige, controle negatif fait. Et
verifier_intrants avait raison : une garde ecrite en deux morceaux promettait un
secret pour une console fermee ; reecrite en implication, elle dit le vrai
contrat.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-08-24 16:23:35 -04:00
parent 1363ebff59
commit 70e57076e8
11 changed files with 356 additions and 73 deletions

View file

@ -1,5 +1,64 @@
# CHANGELOG — Set-OPS # CHANGELOG — Set-OPS
## 2026-08-24 — Collabora en natif : la dernière exception conteneurisée tombe
`serveur_collabora` lançait `collabora/code` dans Docker — **seule exception** de la
flotte au principe fondateur : logiciel libre en natif, systemd + paquets + nginx. Elle
traînait une dépendance entière, `community.docker`, qui n'était même pas déclarée dans
`requirements.yml` et donc absente de tout runner.
**Plus aucun rôle du dépôt n'a besoin de Docker.** La collection est retirée.
### Éprouver l'outil avant le rôle
Sur une Debian 13.6 réelle, sans rien installer :
```
apt-get install --simulate coolwsd -> résout jusqu'à « Conf coolwsd (26.04.3.1-1) »
libgcc1 (exigé par le paquet) -> fourni par libgcc-s1 sur trixie
le dépôt CODE-deb -> PLAT : Packages et Release à la racine, pas de dists/
```
Le paquet livre aussi `/etc/nginx/snippets/coolwsd.conf` et un profil AppArmor — deux
choses que cette flotte sait exploiter, contrairement à une image opaque.
### La configuration par surcharge
Le `coolwsd.xml` livré fait **439 lignes richement commentées**. Le remplacer par un
gabarit maison obligerait à suivre son évolution amont et ferait perdre ses explications.
`coolwsd` acceptant des surcharges `--o:<clé>=<valeur>`, le rôle pose un **fragment
systemd** : la configuration de Set-OPS tient en un fichier, celle du paquet reste intacte.
### La console d'administration est fermée
Elle expose un formulaire hors de tout SSO, avec un secret à créer, faire tourner et
surveiller — pour une fonction dont personne n'a besoin. Même raisonnement que
`serveur_forgejo_connexion_locale`.
### Trois outils du harnais ont eu raison, et l'un avait tort
**`voute.py` lisait les commentaires.** Expliquer en commentaire « pour ouvrir cette
option, fournir `vault_x` » suffisait à rendre `vault_x` obligatoire dans le gabarit ET
dans la voûte réelle. Documenter le nom d'une clef ne doit pas l'exiger : le scanner
ignore désormais ce qui suit un `#`. Contrôle négatif fait — une référence réelle, dans du
code, reste exigée.
**Le message d'erreur d'un `assert` comptait comme une référence.** Il nommait la clef de
voûte ; il nomme maintenant la variable que l'exploitant règle, ce qui est plus utile de
toute façon.
**Et `verifier_intrants.py` avait raison.** Un `assert` de la forme `var | length > 0`
déclare un **contrat** — et l'outil ignore délibérément les `when:`, parce qu'un intrant
exigé sous condition reste un intrant exigé. Écrite en deux morceaux, ma garde promettait
un secret pour une console fermée. Réécrite en **implication** — *si ouverte, alors un mot
de passe* — elle dit ce qu'elle veut vraiment dire.
### Ce qui n'est pas prouvé
L'**installabilité** est établie. Le **fonctionnement** ne l'est pas : aucun écosystème de
la flotte ne fait tourner Collabora aujourd'hui. La première mise en service devra
vérifier l'édition d'un document de bout en bout, depuis Nextcloud.
## 2026-08-24 — La clé du runner, et trois silences fermés en chemin ## 2026-08-24 — La clé du runner, et trois silences fermés en chemin
Le poste d'exploitation fabriquait sa propre paire SSH depuis son premier déploiement, et Le poste d'exploitation fabriquait sa propre paire SSH depuis son premier déploiement, et

View file

@ -30,7 +30,7 @@
| P15 | Modele public socle valide | AFF-022, AFF-099 | ✅ OK | Modele public socle : domaines/serveurs/applications/bases valides. | | P15 | Modele public socle valide | AFF-022, AFF-099 | ✅ OK | Modele public socle : domaines/serveurs/applications/bases valides. |
| P16 | Inventaire Ansible complet (--list) | AFF-030 | ✅ OK | 15 hotes, 32 groupes (inventaire dechiffre et parse). | | P16 | Inventaire Ansible complet (--list) | AFF-030 | ✅ OK | 15 hotes, 32 groupes (inventaire dechiffre et parse). |
| P17 | Tous les modeles valident (registres + underlay) | AFF-022, AFF-099 | ✅ OK | Les 1 modele(s) decouvert(s) valident. | | P17 | Tous les modeles valident (registres + underlay) | AFF-022, AFF-099 | ✅ OK | Les 1 modele(s) decouvert(s) valident. |
| P18 | Gabarit de voute complet | AFF-026 | ✅ OK | Gabarit de voute complet : 24 secret(s) exige(s), tous presents. Voute reelle : 25 cle(s), aucun manque. | | P18 | Gabarit de voute complet | AFF-026 | ✅ OK | Gabarit de voute complet : 23 secret(s) exige(s), tous presents. Voute reelle : 25 cle(s), aucun manque. |
| P19 | Le GUI couvre le schema du plan | AFF-002, AFF-095 | ✅ OK | GUI : les 28 champ(s) des plans reels sont editables (2 plan(s) inspecte(s)), registres toleres : nomenclature. | | P19 | Le GUI couvre le schema du plan | AFF-002, AFF-095 | ✅ OK | GUI : les 28 champ(s) des plans reels sont editables (2 plan(s) inspecte(s)), registres toleres : nomenclature. |
| P20 | Adressage 100% derive du seed (aucun stocke) | AFF-001, AFF-003 | ✅ OK | 2 nomenclature(s) : adressage 100% derive du seed index. | | P20 | Adressage 100% derive du seed (aucun stocke) | AFF-001, AFF-003 | ✅ OK | 2 nomenclature(s) : adressage 100% derive du seed index. |
| P21 | Federation : aucun index en collision | AFF-102 | ✅ OK | Federation coherente : 3 instance(s) federee(s), aucun index en collision. | | P21 | Federation : aucun index en collision | AFF-102 | ✅ OK | Federation coherente : 3 instance(s) federee(s), aucun index en collision. |

View file

@ -46,7 +46,7 @@ Ce que la reconstruction couvre, par capacité :
| Observabilité | `serveur_prometheus`, `serveur_loki`, `serveur_grafana` | métriques, journaux, tableaux sous SSO | | Observabilité | `serveur_prometheus`, `serveur_loki`, `serveur_grafana` | métriques, journaux, tableaux sous SSO |
| Supervision | `serveur_icinga`, `serveur_icingaweb2` | Icinga 2 + IcingaDB + Web 2 + BPM, au SSO | | Supervision | `serveur_icinga`, `serveur_icingaweb2` | Icinga 2 + IcingaDB + Web 2 + BPM, au SSO |
| Forge | `serveur_forgejo` | Git + PostgreSQL + SSO OIDC | | Forge | `serveur_forgejo` | Git + PostgreSQL + SSO OIDC |
| Collaboration | `serveur_nextcloud`, `serveur_collabora` | déployés par la reconstruction, base et client OIDC dérivés du plan — **usage** (dépôt de fichier, édition partagée) non consigné comme preuve | | Collaboration | `serveur_nextcloud`, `serveur_collabora` (**natif**, plus de conteneur) | déployés par la reconstruction, base et client OIDC dérivés du plan — **usage** (dépôt de fichier, édition partagée) non consigné comme preuve |
| Plateforme webapp | `serveur_web_frontal`, `serveur_web_dorsal` | sites statiques et webapps natives (venv + systemd + nginx), **zéro conteneur** | | Plateforme webapp | `serveur_web_frontal`, `serveur_web_dorsal` | sites statiques et webapps natives (venv + systemd + nginx), **zéro conteneur** |
| Sauvegardes | `serveur_backup`, `client_backup` | restic hors-nœud, **restauration éprouvée** (2026-08-12 : la donnée revient) | | Sauvegardes | `serveur_backup`, `client_backup` | restic hors-nœud, **restauration éprouvée** (2026-08-12 : la donnée revient) |
| Exploitation | `serveur_ops` | le poste depuis lequel l'ecosysteme se reconstruit : Ansible epingle, genome clone depuis **sa propre forge**, cle SSH propre — **sans** la voute ni son mot de passe | | Exploitation | `serveur_ops` | le poste depuis lequel l'ecosysteme se reconstruit : Ansible epingle, genome clone depuis **sa propre forge**, cle SSH propre — **sans** la voute ni son mot de passe |

View file

@ -13,11 +13,6 @@ collections:
# `serveur_backup` : ansible.posix.authorized_key. Utilisee depuis longtemps, jamais # `serveur_backup` : ansible.posix.authorized_key. Utilisee depuis longtemps, jamais
# declaree — donc absente du runner, qui n'aurait pas pu deployer les sauvegardes. # declaree — donc absente du runner, qui n'aurait pas pu deployer les sauvegardes.
- name: ansible.posix - name: ansible.posix
# `serveur_collabora` : community.docker.docker_container. # `community.docker` a ete RETIREE le 2026-08-24 : `serveur_collabora` etait la seule
# # exception conteneurisee de la flotte, et il tourne desormais en natif (paquet coolwsd
# A TRANCHER. La doctrine du depot dit « zero conteneur » pour la plateforme webapp, et # du depot amont Collabora). Plus aucun role n'a besoin de Docker.
# le principe general est le logiciel libre en natif. Collabora fait exception, en
# conteneur Docker. On declare la dependance parce qu'elle EXISTE — mieux vaut une
# contradiction visible qu'un role qui echoue en silence sur un runner — mais la
# question reste ouverte : Collabora en natif, ou l'exception assumee et documentee ?
- name: community.docker

View file

@ -1,27 +1,69 @@
# serveur_collabora # serveur_collabora
Serveur d'édition de documents **Collabora Online (CODE)**, protocole WOPI, consommé **Collabora Online, en natif** — paquet `coolwsd`, systemd, derrière l'edge nginx. Aucun
par Nextcloud (`richdocuments`). **Codifié depuis le spike du 2026-07-05** (Docker, prouvé). conteneur.
## Ce que le rôle fait ## Pourquoi ce rôle a été réécrit
- Installe Docker + lance le conteneur `collabora/code` (`restart: always`) ;
- publie sur `127.0.0.1:9980` (Nextcloud co-localisé) **ET** l'IP LAN `:9980` (l'edge) — gotcha spike : bind localhost seul ⇒ **502** depuis l'edge ;
- `aliasgroup1` = le domaine Nextcloud (`cloud.<domaine>`) — Collabora n'accepte le WOPI que de cet hôte ;
- `ssl.enable=false` + `ssl.termination=true` (TLS terminé à l'edge) ;
- attend la santé (`/hosting/capabilities` = 200).
## Dépendances / exposition (à fournir) Il lançait `collabora/code` dans Docker. C'était la **seule exception conteneurisée** de
- **Vhost edge WebSocket-aware** : `bureau.<domaine>` → ce nœud:9980 avec `map $http_upgrade` la flotte, et elle contredisait le principe fondateur : logiciel libre en natif, systemd +
+ en-têtes `Upgrade`/`Connection` + `proxy_read_timeout` long. **Le vhost générique ne suffit pas** paquets + nginx. Elle traînait en plus une dépendance entière — `community.docker` — qui
(l'édition live échoue sans WebSocket) ; n'était même pas déclarée dans `requirements.yml`, donc absente de tout runner.
- FQDN `bureau.<domaine>` dans les SAN du cert edge ;
- port 9980 à firewaller (voir `meta/flux.yml` : ingress depuis l'edge seulement).
## Docker vs natif Depuis la réécriture, **plus aucun rôle du dépôt n'a besoin de Docker**.
Le spike a prouvé **Docker** → on codifie Docker. Le **CODE natif** (paquets Collabora,
`loolwsd`) serait plus « Set-OPS » (natif, pas de moteur Docker) mais exigerait **son propre
spike**. Décision à trancher.
## Robustesse ## Ce qui a été éprouvé avant d'écrire
- **Épingler `serveur_collabora_tag`** en prod (pas de `:latest` surprise) ;
- surveillance Icinga sur `/hosting/capabilities`. Doctrine du dépôt : *éprouver l'outil avant le rôle*. Sur une Debian 13.6 réelle, sans
rien installer :
```
apt-get install --simulate coolwsd -> résout jusqu'à « Conf coolwsd (26.04.3.1-1) »
libgcc1 (exigé par le paquet) -> fourni par libgcc-s1 sur trixie
le dépôt CODE-deb -> PLAT : Packages et Release à la racine, pas de dists/
```
Le paquet livre aussi `/etc/nginx/snippets/coolwsd.conf` et un profil AppArmor — deux
choses que cette flotte sait exploiter, contrairement à une image opaque.
## La configuration : par surcharge, pas par réécriture
Le `coolwsd.xml` livré fait **439 lignes richement commentées**. Le remplacer par un
gabarit maison obligerait à suivre son évolution à chaque version amont, et ferait perdre
les commentaires qui expliquent chaque réglage.
`coolwsd` accepte des surcharges `--o:<clé>=<valeur>`. Le rôle pose donc un **fragment
systemd** : la configuration de Set-OPS tient en un fichier lisible, celle du paquet reste
intacte, et `apt purge` rend la machine à son état d'origine.
## La console d'administration est FERMÉE
Elle expose un formulaire identifiant/mot de passe hors de tout SSO, avec un secret à
créer, faire tourner et surveiller — pour une fonction dont personne n'a besoin au
quotidien. Même raisonnement que `serveur_forgejo_connexion_locale`.
L'ouvrir (`serveur_collabora_admin_actif: true`) exige alors `vault_collabora_admin` en
voûte, et de l'ajouter au gabarit de l'instance.
## Variables principales
| Variable | Défaut | Rôle |
|---|---|---|
| `serveur_collabora_port` | `9980` | écoute, derrière l'edge |
| `serveur_collabora_server_name` | `bureau.<domaine>` | nom externe — vide, coolwsd le déduit mal derrière un mandataire |
| `serveur_collabora_domaine_nextcloud` | `cloud.<domaine>` | l'hôte WOPI autorisé |
| `serveur_collabora_ssl_termination` | `true` | TLS terminé à l'edge, comme le reste de la flotte |
| `serveur_collabora_admin_actif` | `false` | console d'administration |
## Ce que ce rôle vérifie
Il interroge `/hosting/capabilities` après démarrage. Un service « active » qui ne répond
pas là serait découvert par Nextcloud **devant un usager**, à la première ouverture de
document.
## Ce qui n'est pas encore prouvé
L'**installabilité** est établie par simulation sur Debian 13.6. Le **fonctionnement** ne
l'est pas : aucun écosystème de la flotte ne fait tourner Collabora aujourd'hui. La
première mise en service devra vérifier l'édition d'un document de bout en bout, depuis
Nextcloud.

View file

@ -1,22 +1,87 @@
--- ---
# ========================================================================= # COLLABORA ONLINE, EN NATIF — plus de conteneur.
# serveur_collabora — serveur d'édition de documents (Collabora Online / CODE), #
# protocole WOPI, consommé par Nextcloud (richdocuments). Codifié depuis le spike # Ce rôle lançait `collabora/code` dans Docker, ce qui contredisait le principe fondateur
# du 2026-07-05 (Docker, prouvé). Le CODE natif (paquets Collabora) reste une # du dépôt : logiciel libre **en natif**, systemd + paquets + nginx, zéro conteneur. C'était
# option future — nécessiterait son propre spike. # la seule exception de la flotte, et elle traînait une dépendance entière
# ========================================================================= # (`community.docker`) qui n'était même pas déclarée dans `requirements.yml`.
serveur_collabora_conteneur: "collabora" #
serveur_collabora_image: "collabora/code" # CE QUI A ÉTÉ ÉPROUVÉ AVANT D'ÉCRIRE CE RÔLE (2026-08-24), sur une Debian 13.6 réelle,
serveur_collabora_tag: "latest" # À ÉPINGLER en prod (robustesse / pas d'upgrade surprise) # sans rien installer :
#
# apt-get install --simulate coolwsd -> résout jusqu'à « Conf coolwsd (26.04.3.1-1) »
# libgcc1 (exigé) -> fourni par libgcc-s1 sur trixie
# le dépôt CODE-deb est PLAT -> Packages et Release à la racine, pas de dists/
#
# Le paquet livre aussi `/etc/nginx/snippets/coolwsd.conf` et un profil AppArmor — deux
# choses que cette flotte sait déjà exploiter, contrairement à une image opaque.
serveur_collabora_paquets:
- coolwsd
serveur_collabora_service: "coolwsd"
serveur_collabora_port: 9980 serveur_collabora_port: 9980
# L'edge (autre hôte) doit joindre le conteneur → publier sur localhost ET l'IP LAN
# (gotcha spike : bind 127.0.0.1 seul => 502 depuis l'edge). Firewaller via le registre des flux.
serveur_collabora_lan_ip: "{{ ansible_default_ipv4.address | default(ansible_host) }}"
# Domaine Nextcloud autorisé (aliasgroup) — Collabora n'accepte le WOPI que de cet hôte. # --- LE DÉPÔT AMONT ----------------------------------------------------------
#
# Dépôt PLAT : `Suites: ./` et pas de composant. Une source classique (`deb … stable
# main`) ne le trouverait pas.
serveur_collabora_depot_uri: "https://www.collaboraoffice.com/repos/CollaboraOnline/CODE-deb"
serveur_collabora_depot_cle_url: "https://www.collaboraoffice.com/downloads/gpg/collaboraonline-release-keyring.gpg"
serveur_collabora_depot_cle_fichier: "/etc/apt/keyrings/collaboraonline.gpg"
# --- CONFIGURATION : PAR SURCHARGE, PAS PAR RÉÉCRITURE -----------------------
#
# Le `coolwsd.xml` livré fait 439 lignes richement commentées. Le remplacer par un gabarit
# maison, ce serait s'engager à suivre son évolution à chaque version amont — et perdre
# les commentaires qui expliquent chaque réglage.
#
# `coolwsd` accepte des surcharges `--o:<clé>=<valeur>` sur sa ligne de commande. On pose
# donc un fragment systemd : la configuration de Set-OPS tient en un seul fichier lisible,
# le fichier du paquet reste intact, et `apt purge` rend la machine à son état d'origine.
serveur_collabora_fragment: "/etc/systemd/system/coolwsd.service.d/setops.conf"
# TLS TERMINÉ À L'EDGE, comme tout le reste de la flotte : `serveur_nginx` présente le
# certificat step-ca, Collabora écoute en clair derrière lui.
serveur_collabora_ssl_enable: false
serveur_collabora_ssl_termination: true
# Nom externe, tel que le navigateur l'atteint. Vide = déduit de la requête, ce qui échoue
# derrière un mandataire inverse — d'où la valeur explicite.
serveur_collabora_server_name: "bureau.{{ domaine_interne }}"
# Hôtes WOPI autorisés — qui a le droit de faire éditer un document.
#
# Les défauts du paquet couvrent déjà les plages privées (10/8, 172.16/12, 192.168/16,
# loopback), donc l'adressage dérivé de Set-OPS. On ajoute le FQDN de Nextcloud pour que
# l'autorisation soit LISIBLE dans le plan plutôt que déduite d'un masque.
serveur_collabora_domaine_nextcloud: "cloud.{{ domaine_interne }}" serveur_collabora_domaine_nextcloud: "cloud.{{ domaine_interne }}"
serveur_collabora_aliasgroup: "https://{{ serveur_collabora_domaine_nextcloud | regex_escape }}:443"
# TLS terminé à l'edge : Collabora sert en HTTP derrière le proxy. # --- CONSOLE D'ADMINISTRATION : FERMÉE PAR DÉFAUT ----------------------------
serveur_collabora_extra_params: "--o:ssl.enable=false --o:ssl.termination=true" #
# Elle sert à surveiller les sessions et les documents ouverts. Personne n'en a besoin au
# quotidien, et elle expose un formulaire identifiant/mot de passe de plus — hors de tout
# SSO, avec un secret à créer, à faire tourner et à surveiller.
#
# Même raisonnement que `serveur_forgejo_connexion_locale` : une porte ouverte en
# permanence contourne la politique centrale et ne se révoque nulle part. On la ferme, et
# l'on garde l'accès par SSH à l'hôte — le mécanisme de secours retenu par ce dépôt.
#
# L'ouvrir exige alors un secret en voûte, et de l'ajouter au gabarit de l'instance.
# LE MOT DE PASSE N'EST PAS TIRE D'UNE CLE DE VOUTE PAR DEFAUT, ET C'EST VOULU.
#
# `scripts/voute.py` exige de la voute TOUTE reference `vault_*` trouvee dans un role
# actif — c'est ce qui garantit qu'aucun secret n'est oublie. Mais une porte FERMEE ne
# doit pas reclamer de clef : nommer ici `vault_collabora_admin` obligerait chaque
# ecosysteme installant Collabora a inventer, chiffrer et faire tourner un secret pour
# une console que personne n'ouvre.
#
# Qui veut la console la declare chez lui :
#
# # group_vars/serveur_collabora.yml de l'instance
# serveur_collabora_admin_actif: true
# serveur_collabora_admin_mot_de_passe: "{{ vault_collabora_admin }}"
#
# et ajoute `vault_collabora_admin` a son gabarit de voute — ou P18 le lui rappellera.
serveur_collabora_admin_actif: false
serveur_collabora_admin_utilisateur: "collabora-admin"
serveur_collabora_admin_mot_de_passe: ""

View file

@ -0,0 +1,7 @@
---
- name: Redemarrer Collabora
when: not ansible_check_mode
ansible.builtin.systemd:
name: "{{ serveur_collabora_service }}"
state: restarted
daemon_reload: true

View file

@ -1,39 +1,109 @@
--- ---
- name: Installer Docker (moteur du conteneur Collabora) # 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.
- name: Cette ressource est-elle deja recuperee ? (cle Collabora)
ansible.builtin.stat:
path: "{{ serveur_collabora_depot_cle_fichier }}"
register: serveur_collabora_cle_presente
- name: Telecharger la cle de signature Collabora
when: not serveur_collabora_cle_presente.stat.exists
ansible.builtin.get_url:
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
# DEPOT PLAT : `Suites: ./`, aucun composant. Une source classique ne le trouverait pas.
- 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: ansible.builtin.apt:
name: docker.io name: "{{ serveur_collabora_paquets }}"
state: present state: present
update_cache: true update_cache: true
cache_valid_time: 3600
- name: Activer et demarrer Docker - 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: ansible.builtin.systemd:
name: docker name: "{{ serveur_collabora_service }}"
enabled: true enabled: true
state: started state: started
when: not ansible_check_mode daemon_reload: true
- name: Lancer le conteneur Collabora CODE (WOPI, publie sur localhost + IP LAN) # ECRIRE, PUIS RELIRE (D-68). Un service « active » qui ne repond pas a `capabilities`
community.docker.docker_container: # est un vert sur un perimetre vide : Nextcloud le decouvrirait a la premiere ouverture
name: "{{ serveur_collabora_conteneur }}" # de document, c'est-a-dire devant un usager.
image: "{{ serveur_collabora_image }}:{{ serveur_collabora_tag }}" - name: Collabora repond-il vraiment ?
state: started
pull: true
restart_policy: always
ports:
- "127.0.0.1:{{ serveur_collabora_port }}:9980"
- "{{ serveur_collabora_lan_ip }}:{{ serveur_collabora_port }}:9980"
env:
aliasgroup1: "{{ serveur_collabora_aliasgroup }}"
extra_params: "{{ serveur_collabora_extra_params }}"
when: not ansible_check_mode when: not ansible_check_mode
- name: Attendre que Collabora reponde (capabilities)
ansible.builtin.uri: ansible.builtin.uri:
url: "http://127.0.0.1:{{ serveur_collabora_port }}/hosting/capabilities" url: "http://127.0.0.1:{{ serveur_collabora_port }}/hosting/capabilities"
status_code: 200 status_code: [200]
register: serveur_collabora_sante timeout: 20
until: serveur_collabora_sante.status == 200 register: serveur_collabora_capacites
retries: 12 retries: 12
delay: 5 delay: 5
when: not ansible_check_mode until: serveur_collabora_capacites is succeeded

View file

@ -0,0 +1,8 @@
# GÉNÉRÉ par Set-OPS (rôle serveur_collabora). NE PAS éditer à la main.
#
# Dépôt PLAT : `Suites: ./` et aucun composant. Constaté le 2026-08-24 — `Packages` et
# `Release` vivent à la racine, il n'y a pas de `dists/`.
Types: deb
URIs: {{ serveur_collabora_depot_uri }}
Suites: ./
Signed-By: {{ serveur_collabora_depot_cle_fichier }}

View file

@ -0,0 +1,17 @@
# GÉNÉRÉ par Set-OPS (rôle serveur_collabora). NE PAS éditer à la main.
#
# Surcharge de l'unité du paquet. Le `coolwsd.xml` livré (439 lignes commentées) reste
# intact : le remplacer par un gabarit maison obligerait à suivre son évolution à chaque
# version amont, et ferait perdre les commentaires qui expliquent chaque réglage.
[Service]
ExecStart=
ExecStart=/usr/bin/coolwsd --version --o:sys_template_path=/opt/cool/systemplate \
--o:child_root_path=/opt/cool/child-roots \
--o:file_server_root_path=/usr/share/coolwsd \
--o:ssl.enable={{ serveur_collabora_ssl_enable | bool | lower }} \
--o:ssl.termination={{ serveur_collabora_ssl_termination | bool | lower }} \
--o:server_name={{ serveur_collabora_server_name }} \
--o:storage.wopi.alias_group[0].host=https://{{ serveur_collabora_domaine_nextcloud }}:443 \
--o:admin_console.enable={{ serveur_collabora_admin_actif | bool | lower }}{% if serveur_collabora_admin_actif | bool %} \
--o:admin_console.username={{ serveur_collabora_admin_utilisateur }} \
--o:admin_console.password={{ serveur_collabora_admin_mot_de_passe }}{% endif %}

View file

@ -65,10 +65,30 @@ def gabarit() -> Path | None:
def _refs_vault(chemin: Path) -> set[str]: def _refs_vault(chemin: Path) -> set[str]:
"""Les `vault_*` qu'un fichier UTILISE — pas ceux dont il PARLE.
ON IGNORE LES COMMENTAIRES (2026-08-24). Le scanner lisait le fichier entier, si bien
qu'expliquer en commentaire « pour ouvrir cette option, fournir vault_x » SUFFISAIT a
rendre `vault_x` obligatoire dans le gabarit et dans la voute reelle.
Constate sur `serveur_collabora` : sa console d'administration est FERMEE par defaut,
et le role se contentait de documenter comment l'ouvrir. Trois phrases de commentaire
imposaient donc a tout ecosysteme installant Collabora d'inventer, chiffrer et faire
tourner un secret pour une porte que personne n'ouvre.
Documenter le nom d'une clef ne doit pas l'exiger. Une clef reellement utilisee
apparait dans du code — c'est ce qu'on lit ici.
"""
try: try:
return set(MOTIF_VAULT.findall(chemin.read_text(encoding="utf-8", errors="ignore"))) texte = chemin.read_text(encoding="utf-8", errors="ignore")
except OSError: except OSError:
return set() return set()
# Suffisant pour YAML, Jinja (`{# … #}` compris) et Python : ce qui suit un `#` hors
# guillemets est de la prose. On ne cherche pas a analyser finement — un `#` dans une
# chaine rendrait au pire une reference ignoree, et une reference reellement utilisee
# apparait ailleurs dans du code.
utile = "\n".join(ligne.split("#", 1)[0] for ligne in texte.splitlines())
return set(MOTIF_VAULT.findall(utile))
def groupes_du_plan() -> set[str]: def groupes_du_plan() -> set[str]: