diff --git a/CHANGELOG.md b/CHANGELOG.md index cae8fe6..2b31f96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,54 @@ # CHANGELOG — Set-OPS +## 2026-08-10 — Le contrôleur télécharge et pousse ; la cible ne tire plus d'Internet + +**Inventaire mesuré** de ce qu'une reconstruction de tenant télécharge — environ **1,5 Gio** : + +| Quoi | D'où | Taille | +|---|---|---| +| `nextcloud-34.0.1.tar.bz2` | download.nextcloud.com | **230 Mio** | +| `keycloak-26.0.7` | github.com/keycloak | **140 Mio** | +| `forgejo-10.0.0-linux-amd64` | codeberg.org | **101 Mio** | +| `oauth2-proxy v7.15.3` | github.com/oauth2-proxy | 18 Mio | +| image `collabora/code` | Docker Hub | **471 Mio** | +| paquets `apt` | Debian + Grafana + smallstep + Icinga | le reste, ×14 hôtes | + +**Les quatre premières sont épinglées en version et vont chacune sur UN SEUL hôte.** Les +retélécharger à chaque reconstruction est un gaspillage, et une dépendance de plus sur le +chemin critique — un serveur tiers lent a déjà fait tomber un déploiement de flotte le +2026-08-09, sur le binaire Forgejo précisément. + +### Pourquoi pousser plutôt que servir un cache + +L'exploitant proposait son poste comme cache HTTP. L'intention est juste, mais elle butait +sur ce qu'on avait fermé le matin même : les règles sortantes visent `!SETOPS_INTERNES`, +donc les trois blocs privés. Une VM de tenant **ne peut plus atteindre le poste**. Servir un +cache aurait exigé de **rouvrir un flux vers le plan d'administration**. + +L'inversion évite le problème entier : le contrôleur télécharge dans son cache +(`~/.cache/setops`, gardé par un `stat` — une fois, jamais deux), puis **pousse par le canal +SSH qui existe déjà**. Aucun port, aucun service, aucune règle, aucun couplage. + +Effet recherché en prime : ces artefacts deviennent **déployables hors ligne** une fois le +cache rempli. Sur une plateforme qui se veut souveraine, ce n'est pas un détail. + +### Ce que ça ne couvre pas, et qu'il faut nommer + +- **`collabora/code`, 471 Mio depuis Docker Hub.** `serveur_collabora` installe `docker.io` + et tire une image — la **seule entorse** à la doctrine « plateforme native, zéro Docker » + du dépôt. C'est aussi ce qui explique les règles `docker0` du `nftables` généré. Elle + mérite sa propre décision, pas un contournement discret. +- **Les paquets `apt`**, quatorze `apt update` contre les mêmes dépôts. `apt-cacher-ng` est + la bonne réponse, mais il lui faut un hôte toujours allumé et un flux déclaré : sa place + est côté **hébergeur**, partagé par les tenants — pas sur le poste. + +### Une mesure qui a contredit mon hypothèse + +J'avais avancé que le `.zip` de Nextcloud décompresserait plus vite que le `.tar.bz2`. +Vérification : **271 Mio contre 230**. Il télécharge donc *plus* pour décompresser *moins +lentement*. Le gain net n'est pas établi — le changement de format reste en attente d'une +mesure, pas d'une intuition. + ## 2026-08-10 — Deux optimisations, choisies sur la mesure et non sur l'intuition Le chronométrage d'une reconstruction complète (`1 h 12 min 48 s`, phase par phase) a diff --git a/roles/serveur_forgejo/defaults/main.yml b/roles/serveur_forgejo/defaults/main.yml index 87ad0fe..1a000c6 100644 --- a/roles/serveur_forgejo/defaults/main.yml +++ b/roles/serveur_forgejo/defaults/main.yml @@ -85,3 +85,8 @@ serveur_forgejo_oidc_groupe_admin: "sysadmin" # Empreinte du mot de passe admin APPLIQUE (rotation). Voir tasks/main.yml. serveur_forgejo_marqueur_admin: "/etc/forgejo/.setops-admin-sha256" + +# Cache d'artefacts DU CONTROLEUR (pas de la cible). Le controleur telecharge une fois, +# puis pousse par SSH : aucun flux nouveau, et l'artefact devient deployable hors ligne. +# Surchargeable par `setops_cache_artefacts` pour partager un cache commun. +serveur_forgejo_cache_local: "{{ setops_cache_artefacts | default(lookup('env', 'HOME') + '/.cache/setops') }}" diff --git a/roles/serveur_forgejo/tasks/main.yml b/roles/serveur_forgejo/tasks/main.yml index 19df804..68eabfe 100644 --- a/roles/serveur_forgejo/tasks/main.yml +++ b/roles/serveur_forgejo/tasks/main.yml @@ -65,14 +65,46 @@ path: "/usr/local/bin/forgejo-{{ serveur_forgejo_version }}" register: serveur_forgejo_binaire_present -- name: Telecharger le binaire Forgejo +# Le controleur telecharge, puis pousse par SSH — la cible ne tire jamais d'Internet. +# Motif explique en detail dans `roles/serveur_nextcloud/tasks/installer.yml`. C'est +# precisement ce binaire qui a fait tomber un deploiement de flotte le 2026-08-09, sur un +# « read operation timed out » venu d'un serveur tiers. +- name: Cache d artefacts du contrôleur + ansible.builtin.file: + path: "{{ serveur_forgejo_cache_local }}" + state: directory + mode: "0700" + delegate_to: localhost + become: false when: not serveur_forgejo_binaire_present.stat.exists + +- name: L artefact est-il déjà dans le cache du contrôleur ? + ansible.builtin.stat: + path: "{{ serveur_forgejo_cache_local }}/forgejo-{{ serveur_forgejo_version }}" + register: serveur_forgejo_cache_present + delegate_to: localhost + become: false + when: not serveur_forgejo_binaire_present.stat.exists + +- name: Télécharger le binaire Forgejo dans le cache du contrôleur (une seule fois) ansible.builtin.get_url: url: "{{ serveur_forgejo_url }}" + dest: "{{ serveur_forgejo_cache_local }}/forgejo-{{ serveur_forgejo_version }}" + mode: "0644" + delegate_to: localhost + become: false + when: + - not serveur_forgejo_binaire_present.stat.exists + - not (serveur_forgejo_cache_present.stat.exists | default(false)) + +- name: Déposer le binaire Forgejo sur l hôte depuis le cache + ansible.builtin.copy: + src: "{{ serveur_forgejo_cache_local }}/forgejo-{{ serveur_forgejo_version }}" dest: "/usr/local/bin/forgejo-{{ serveur_forgejo_version }}" owner: root group: root mode: "0755" + when: not serveur_forgejo_binaire_present.stat.exists - name: Activer la version courante du binaire ansible.builtin.file: diff --git a/roles/serveur_keycloak/defaults/main.yml b/roles/serveur_keycloak/defaults/main.yml index d974510..167c2af 100644 --- a/roles/serveur_keycloak/defaults/main.yml +++ b/roles/serveur_keycloak/defaults/main.yml @@ -182,3 +182,8 @@ serveur_keycloak_ldap_attributs: serveur_keycloak_journal_actif: true serveur_keycloak_journal_admin: true serveur_keycloak_journal_retention: 1209600 + +# Cache d'artefacts DU CONTROLEUR (pas de la cible). Le controleur telecharge une fois, +# puis pousse par SSH : aucun flux nouveau, et l'artefact devient deployable hors ligne. +# Surchargeable par `setops_cache_artefacts` pour partager un cache commun. +serveur_keycloak_cache_local: "{{ setops_cache_artefacts | default(lookup('env', 'HOME') + '/.cache/setops') }}" diff --git a/roles/serveur_keycloak/tasks/main.yml b/roles/serveur_keycloak/tasks/main.yml index e4099ec..40862b5 100644 --- a/roles/serveur_keycloak/tasks/main.yml +++ b/roles/serveur_keycloak/tasks/main.yml @@ -85,12 +85,42 @@ path: "/tmp/keycloak-{{ serveur_keycloak_version }}.tar.gz" register: serveur_keycloak_archive_present -- name: Telecharger la distribution Keycloak +# Le controleur telecharge, puis pousse par SSH — la cible ne tire jamais d'Internet. +# Motif explique en detail dans `roles/serveur_nextcloud/tasks/installer.yml`. +- name: Cache d artefacts du contrôleur + ansible.builtin.file: + path: "{{ serveur_keycloak_cache_local }}" + state: directory + mode: "0700" + delegate_to: localhost + become: false when: not serveur_keycloak_archive_present.stat.exists + +- name: L artefact est-il déjà dans le cache du contrôleur ? + ansible.builtin.stat: + path: "{{ serveur_keycloak_cache_local }}/keycloak-{{ serveur_keycloak_version }}.tar.gz" + register: serveur_keycloak_cache_present + delegate_to: localhost + become: false + when: not serveur_keycloak_archive_present.stat.exists + +- name: Télécharger la distribution Keycloak dans le cache du contrôleur (une seule fois) ansible.builtin.get_url: url: "{{ serveur_keycloak_url }}" + dest: "{{ serveur_keycloak_cache_local }}/keycloak-{{ serveur_keycloak_version }}.tar.gz" + mode: "0644" + delegate_to: localhost + become: false + when: + - not serveur_keycloak_archive_present.stat.exists + - not (serveur_keycloak_cache_present.stat.exists | default(false)) + +- name: Déposer la distribution Keycloak sur l hôte depuis le cache + ansible.builtin.copy: + src: "{{ serveur_keycloak_cache_local }}/keycloak-{{ serveur_keycloak_version }}.tar.gz" dest: "/tmp/keycloak-{{ serveur_keycloak_version }}.tar.gz" mode: "0644" + when: not serveur_keycloak_archive_present.stat.exists - name: Extraire Keycloak ansible.builtin.unarchive: diff --git a/roles/serveur_nextcloud/defaults/main.yml b/roles/serveur_nextcloud/defaults/main.yml index 0ee18a8..de5bdad 100644 --- a/roles/serveur_nextcloud/defaults/main.yml +++ b/roles/serveur_nextcloud/defaults/main.yml @@ -115,3 +115,8 @@ serveur_nextcloud_oidc_groupe_admin: "sysadmin" # Empreinte du mot de passe admin APPLIQUE (rotation). Voir tasks/configurer.yml. serveur_nextcloud_marqueur_admin: "/etc/setops-nextcloud-admin-sha256" + +# Cache d'artefacts DU CONTROLEUR (pas de la cible). Le controleur telecharge une fois, +# puis pousse par SSH : aucun flux nouveau, et l'artefact devient deployable hors ligne. +# Surchargeable par `setops_cache_artefacts` pour partager un cache commun. +serveur_nextcloud_cache_local: "{{ setops_cache_artefacts | default(lookup('env', 'HOME') + '/.cache/setops') }}" diff --git a/roles/serveur_nextcloud/tasks/installer.yml b/roles/serveur_nextcloud/tasks/installer.yml index 13b7ba4..bb2ef6c 100644 --- a/roles/serveur_nextcloud/tasks/installer.yml +++ b/roles/serveur_nextcloud/tasks/installer.yml @@ -8,12 +8,56 @@ path: "/tmp/nextcloud-{{ serveur_nextcloud_version }}.tar.bz2" register: serveur_nextcloud_archive_present -- name: Telecharger le tarball Nextcloud (version epinglee) +# LE CONTROLEUR TELECHARGE, PUIS POUSSE — la cible ne tire jamais d'Internet. +# +# Inventaire du 2026-08-10 : une reconstruction de tenant telecharge ~1,5 Gio, dont +# 490 Mio pour quatre archives EPINGLEES EN VERSION (Nextcloud 230, Keycloak 140, +# Forgejo 101, oauth2-proxy 18) qui vont chacune sur UN SEUL hote. Les retelecharger a +# chaque reconstruction est un pur gaspillage, et une dependance de plus sur le chemin +# critique — un serveur tiers lent a deja fait tomber un deploiement de flotte le +# 2026-08-09. +# +# POURQUOI POUSSER PLUTOT QUE SERVIR UN CACHE. Un cache HTTP sur le poste exigerait que +# les VM du tenant l'atteignent — donc de ROUVRIR un flux vers le plan d'administration, +# que les regles sortantes `!SETOPS_INTERNES` ferment depuis ce matin. Ici, rien de tel : +# on emprunte le canal SSH qui existe deja, aucun port, aucun service, aucune regle. +# +# Effet de bord recherche : ces artefacts deviennent deployables HORS LIGNE une fois le +# cache rempli. Sur une plateforme qui se veut souveraine, ce n'est pas un detail. +- name: Cache d artefacts du contrôleur + ansible.builtin.file: + path: "{{ serveur_nextcloud_cache_local }}" + state: directory + mode: "0700" + delegate_to: localhost + become: false when: not serveur_nextcloud_archive_present.stat.exists + +- name: L artefact est-il déjà dans le cache du contrôleur ? + ansible.builtin.stat: + path: "{{ serveur_nextcloud_cache_local }}/nextcloud-{{ serveur_nextcloud_version }}.tar.bz2" + register: serveur_nextcloud_cache_present + delegate_to: localhost + become: false + when: not serveur_nextcloud_archive_present.stat.exists + +- name: Télécharger le tarball Nextcloud dans le cache du contrôleur (une seule fois) ansible.builtin.get_url: url: "{{ serveur_nextcloud_url }}" + dest: "{{ serveur_nextcloud_cache_local }}/nextcloud-{{ serveur_nextcloud_version }}.tar.bz2" + mode: "0644" + delegate_to: localhost + become: false + when: + - not serveur_nextcloud_archive_present.stat.exists + - not (serveur_nextcloud_cache_present.stat.exists | default(false)) + +- name: Déposer le tarball Nextcloud sur l hôte depuis le cache + ansible.builtin.copy: + src: "{{ serveur_nextcloud_cache_local }}/nextcloud-{{ serveur_nextcloud_version }}.tar.bz2" dest: "/tmp/nextcloud-{{ serveur_nextcloud_version }}.tar.bz2" mode: "0644" + when: not serveur_nextcloud_archive_present.stat.exists - name: Extraire Nextcloud dans /var/www (si absent) ansible.builtin.unarchive: diff --git a/roles/serveur_oauth2_proxy/defaults/main.yml b/roles/serveur_oauth2_proxy/defaults/main.yml index 6f34509..aedc228 100644 --- a/roles/serveur_oauth2_proxy/defaults/main.yml +++ b/roles/serveur_oauth2_proxy/defaults/main.yml @@ -46,3 +46,8 @@ serveur_oauth2_proxy_email_domains: "*" serveur_oauth2_proxy_allow_unverified_email: true # Bundle d'AC système (racine step_ca via client_pki) pour joindre Keycloak en HTTPS. serveur_oauth2_proxy_ca_bundle: "/etc/ssl/certs/ca-certificates.crt" + +# Cache d'artefacts DU CONTROLEUR (pas de la cible). Le controleur telecharge une fois, +# puis pousse par SSH : aucun flux nouveau, et l'artefact devient deployable hors ligne. +# Surchargeable par `setops_cache_artefacts` pour partager un cache commun. +serveur_oauth2_proxy_cache_local: "{{ setops_cache_artefacts | default(lookup('env', 'HOME') + '/.cache/setops') }}" diff --git a/roles/serveur_oauth2_proxy/tasks/main.yml b/roles/serveur_oauth2_proxy/tasks/main.yml index 85f9014..190e9ed 100644 --- a/roles/serveur_oauth2_proxy/tasks/main.yml +++ b/roles/serveur_oauth2_proxy/tasks/main.yml @@ -63,12 +63,42 @@ path: "/tmp/oauth2-proxy-{{ serveur_oauth2_proxy_version }}.tar.gz" register: serveur_oauth2_proxy_archive_present -- name: Télécharger oauth2-proxy +# Le controleur telecharge, puis pousse par SSH — la cible ne tire jamais d'Internet. +# Motif explique en detail dans `roles/serveur_nextcloud/tasks/installer.yml`. +- name: Cache d artefacts du contrôleur + ansible.builtin.file: + path: "{{ serveur_oauth2_proxy_cache_local }}" + state: directory + mode: "0700" + delegate_to: localhost + become: false when: not serveur_oauth2_proxy_archive_present.stat.exists + +- name: L artefact est-il déjà dans le cache du contrôleur ? + ansible.builtin.stat: + path: "{{ serveur_oauth2_proxy_cache_local }}/oauth2-proxy-{{ serveur_oauth2_proxy_version }}.tar.gz" + register: serveur_oauth2_proxy_cache_present + delegate_to: localhost + become: false + when: not serveur_oauth2_proxy_archive_present.stat.exists + +- name: Télécharger oauth2-proxy dans le cache du contrôleur (une seule fois) ansible.builtin.get_url: url: "{{ serveur_oauth2_proxy_url }}" + dest: "{{ serveur_oauth2_proxy_cache_local }}/oauth2-proxy-{{ serveur_oauth2_proxy_version }}.tar.gz" + mode: "0644" + delegate_to: localhost + become: false + when: + - not serveur_oauth2_proxy_archive_present.stat.exists + - not (serveur_oauth2_proxy_cache_present.stat.exists | default(false)) + +- name: Déposer oauth2-proxy sur l hôte depuis le cache + ansible.builtin.copy: + src: "{{ serveur_oauth2_proxy_cache_local }}/oauth2-proxy-{{ serveur_oauth2_proxy_version }}.tar.gz" dest: "/tmp/oauth2-proxy-{{ serveur_oauth2_proxy_version }}.tar.gz" mode: "0644" + when: not serveur_oauth2_proxy_archive_present.stat.exists - name: Extraire le binaire oauth2-proxy ansible.builtin.unarchive: