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:
parent
1363ebff59
commit
70e57076e8
11 changed files with 356 additions and 73 deletions
59
CHANGELOG.md
59
CHANGELOG.md
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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. |
|
||||||
|
|
|
||||||
|
|
@ -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 |
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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: ""
|
||||||
|
|
|
||||||
7
roles/serveur_collabora/handlers/main.yml
Normal file
7
roles/serveur_collabora/handlers/main.yml
Normal 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
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
8
roles/serveur_collabora/templates/collabora.sources.j2
Normal file
8
roles/serveur_collabora/templates/collabora.sources.j2
Normal 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 }}
|
||||||
17
roles/serveur_collabora/templates/setops.conf.j2
Normal file
17
roles/serveur_collabora/templates/setops.conf.j2
Normal 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 %}
|
||||||
|
|
@ -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]:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue