Une revision de documentation vieillit comme le reste. Ce qui tient, c est ce
qu une machine verifie — et trois lacunes etaient nommees sans etre gardees.
P58 HABILITATIONS. autorisation.md posait la regle (un service nomme un
GROUPE, jamais une personne, D-66) et meta/acces.yml la portait ; rien ne
la verifiait. P29 gardait les POSITIONS d authentification, personne ne
gardait les DROITS.
Le controle qui porte la preuve est un croisement : une entree
porte_par: role-realm affirme que l habilitation voyage par un role de
realm projete depuis un groupe LDAP. P58 le confronte a serveur_keycloak.
Sans ca, un service annonce une habilitation que rien ne transporte, et l
ecran reste vide sans que personne sache pourquoi.
CE QU ELLE N EXIGE PAS, et c est le point le plus important : que les
groupes nommes existent dans l annuaire. Ce serait contredire le regime du
paragraphe 2 — le depot AMORCE un acces et se retire, les appartenances
appartiennent a une personne. dev et personnel n existent dans aucun code,
et ce n est pas un defaut.
P59 ENUMERATIONS ANNONCEES. Les deux ecarts trouves a la main pendant la
tournee — cinq portes annoncees devant une table de six, huit lignes
renvoyees vers une fiche qui en compte dix — etaient d une forme que P57
ne voit pas.
Ma premiere version a signale CINQ ecarts, et les cinq etaient du bruit :
dans « reprise dans les deux devis : », le nombre qualifie autre chose que
la liste. Cent pour cent de faux positifs — la preuve qui crie sur un cas
sain et qu on apprend a ignorer. Resserree aux deux formes ou le nombre ne
peut compter rien d autre. Etroite et vraie plutot que large et devineuse.
P60 WIKI PUBLIE. Le wiki est publie DEPUIS le depot ; rien ne mesurait l ecart,
et il s est creuse de VINGT-SEPT JOURS en silence. Deux unites jamais
publiees, vingt et une differentes : pour qui lit la forge plutot que le
depot, toute la revision n existait pas.
Le harnais est STATIQUE, zero appel reseau — cloner la forge romprait la
seule propriete qui fasse qu une preuve vaille hors de ce poste. La mesure
passe donc par un TEMOIN que make wiki-publier depose. Amorce avec la
valeur MESUREE : le wiki d eregion porte b6167f2, dont le message dit
source: ac85278.
Ce qu elle ne prouve pas : un temoin dit ce qui est PARTI, jamais ce qui
est ARRIVE.
LES TROIS SONT EPROUVEES DANS LES DEUX SENS
Douze essais negatifs, douze refus : groupe non projete, acces.yml disparu,
personne au lieu d un groupe, mecanisme invente, raison manquante, compte
revenu a cinq, septieme porte ajoutee sans toucher au compte, renvoi croise
fausse, temoin absent, temoin d un autre depot. Une garantie qu on n a jamais vu
dire non n est pas une garantie, c est une habitude.
ETAT : NON CONFORME, 58 OK, 1 echec, 1 saute.
P60 est rouge, et c est le comportement voulu : le registre a le droit de
perdre. Le retard qu elle signale est reel et anterieur a elle. Une commande le
ferme, et elle vient ensuite.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
739 lines
28 KiB
Markdown
739 lines
28 KiB
Markdown
# AGENTS.md — Set-OPS
|
||
|
||
## Rôle du dépôt
|
||
|
||
Ce dépôt contient les playbooks, rôles, inventaires et templates Ansible servant à construire, configurer, maintenir et documenter les serveurs d’un écosystème numérique souverain.
|
||
|
||
`Set-OPS` est le moteur global d’exploitation d’écosystèmes numériques souverains.
|
||
|
||
Le template Debian 13 Proxmox est seulement un sous-ensemble du dépôt. Le dépôt ne doit jamais être restructuré autour d’un seul besoin ponctuel.
|
||
|
||
---
|
||
|
||
## Mission et identité
|
||
|
||
Au-delà de l’exploitation, `Set-OPS` **définit et construit un écosystème numérique souverain** : une infrastructure interne auto-suffisante, sans dépendance SaaS, dont tous les piliers sont décrits en code et reliés entre eux.
|
||
|
||
Piliers de l’écosystème :
|
||
|
||
- identité — machines (PKI / certificats) et utilisateurs (annuaire + SSO) ;
|
||
- confiance — autorité de certification interne (ACME) ;
|
||
- nommage et adressage — DNS interne et nomenclature dérivable ;
|
||
- données — bases relationnelles et cache, avec registre des connexions ;
|
||
- communication — service de courriel souverain (boîtes LDAP, SMTP, IMAP, antispam, DKIM) ; le relais des notifications système en est distinct (`client_smtp`) ;
|
||
- observabilité et supervision — métriques, journaux, tableaux de bord, supervision active ;
|
||
- applicatif — services internes (forge, etc.) et couche web.
|
||
|
||
Propriétés visées, avec leurs nuances honnêtes :
|
||
|
||
- **Souverain / auto-suffisant** : c’est l’objectif. Aucune dépendance à un service externe pour la confiance, l’identité, le nom ou la communication. Cela justifie le choix de construire plutôt qu’assembler des SaaS.
|
||
- **Déclaratif et convergent** : l’état voulu est décrit dans des registres machine-lisibles (`instance/plan/nomenclature.yml`, `docs/dependances-groupes.yml`, `instance/plan/bases-donnees.yml`) et appliqué par les groupes Ansible. Ces registres sont la **source unique de vérité**.
|
||
- **Pas (encore) auto-réparé** : la convergence est pilotée par l’opérateur (GUI / CLI / `make`), pas une boucle fermée d’auto-remédiation.
|
||
- **Éprouvé sur VM réelles — mais la nuance tient toujours.** Cette ligne a dit, jusqu’au 2026-09-06, que « une grande partie est planifiée et validée mais pas encore exécutée contre des VM réelles ». C’est faux depuis longtemps : la flotte a été **rasée et remontée depuis zéro** le 2026-08-13, puis **deux fois le 2026-09-02** (15/15 puis 14/14 hôtes, 0 échec, `make valider` à 0 échec sur 13 hôtes). Ce qui reste vrai, et qu’il faut garder : **un `--syntax-check` vert ne prouve rien de l’exécution**, et le tableau de maturité de `docs/catalogue-services.md` — pas ce fichier — dit ce qui est éprouvé et ce qui ne l’est pas. Ne jamais présenter comme « en production » un rôle que ce tableau ne donne pas pour tel.
|
||
|
||
Conséquence pour le travail : préserver la discipline qui tient l’ensemble — registres comme source unique, dépendances explicites, validation de chaque pièce, secrets hors dépôt. C’est ce qui empêche l’écosystème de devenir un objet ingérable. Le template Debian 13 reste la fondation (le moule des VM), pas la finalité.
|
||
|
||
---
|
||
|
||
## Le plan et la génération de l’inventaire (méta-classe)
|
||
|
||
`Set-OPS` se pilote par un **plan**, pas par l’édition directe de l’inventaire.
|
||
L’inventaire Ansible est **généré** depuis le plan.
|
||
|
||
**RÈGLE D’OR : `instance/inventories/<inventaire>/hosts.yml` est un artefact GÉNÉRÉ. Ne jamais l’éditer à la main.** On édite le *plan*, puis on régénère.
|
||
|
||
`<inventaire>` est une **place, pas un nom** : le moteur le résout (`principal`, sinon `production` — `scripts/inventory_rules.py`, `ORDRE_INVENTAIRE`). La flotte dit `principal`, le modèle public dit `production`. Ne coder ni l’un ni l’autre en dur dans un document.
|
||
|
||
- L’**application** est l’entité pivot ; le **groupe** Ansible n’est qu’une capacité (le rôle appliqué), plus une cible de liaison.
|
||
- Le plan vit dans des registres machine-lisibles : `instance/plan/serveurs.yml` (les VM), `instance/plan/applications.yml` (les services et leurs liens `requiert`/`utilise`/`expose`), `instance/plan/bases-donnees.yml`, `instance/plan/domaines.yml`, `instance/plan/nomenclature.yml`.
|
||
- Génération : `make instancier` (génère + diff sémantique), `make instancier-appliquer` (régénère `hosts.yml`, refuse si le diff n’est pas vide — `FORCE=1` pour un changement intentionnel).
|
||
- Flux : **éditer le plan → `make instancier` (revoir le diff) → `make instancier-appliquer` → déployer**. Via le GUI : vues **Serveurs** et **Applications**, puis « Appliquer le plan » (la vue Inventaire est en lecture seule).
|
||
- VMID / IP / VLAN / passerelle sont **dérivés** de la `fonction` via la nomenclature ; les groupes d’une VM sont dérivés (socle + services des applications + intégrations + état).
|
||
- Garde-fous : validateurs de registres, diff-vide, `node --check` du JS du GUI (`scripts/verifier_gui.py`, dans `make inventaire-verifier`), git comme filet.
|
||
|
||
Référence complète : **`docs/plan-et-generation.md`**.
|
||
|
||
**Plan de contrôle gelé en périmètre** : le GUI, le générateur, l'IPAM et la modélisation sont volontairement *maison* et **souverains**, mais leur périmètre est gelé. Ne pas y ajouter de fonctionnalités de type NetBox/AWX (RBAC, historique d'audit, détection de conflits IPAM, API riche) : le besoin réel d'une de ces fonctions est le **signal d'adopter l'outil mûr correspondant** (NetBox pour la source de vérité, AWX pour l'exécution), pas de le réimplémenter. Décision et seuils : **`docs/positionnement.md`**.
|
||
|
||
---
|
||
|
||
## Règle d’or IA
|
||
|
||
Un seul agent IA travaille dans ce dépôt à la fois.
|
||
|
||
- Soit Codex.
|
||
- Soit Claude Code.
|
||
- Jamais les deux simultanément.
|
||
|
||
Tout changement significatif doit être consigné dans `CHANGELOG.md`.
|
||
|
||
---
|
||
|
||
## Principes
|
||
|
||
1. Toujours lire l’existant avant de modifier.
|
||
2. Ne jamais introduire de secret en clair.
|
||
3. Ne jamais casser l’idempotence Ansible.
|
||
4. Ne jamais exécuter d’action destructive sans confirmation explicite.
|
||
5. Préférer la simplicité à l’ingénierie excessive.
|
||
6. Documenter ce qui est utile à l’exploitation réelle.
|
||
7. Préférer les correctifs ciblés aux régénérations massives.
|
||
8. Ne jamais supposer qu’un rôle est complet sans l’avoir inspecté.
|
||
9. Ne jamais déclarer un playbook prêt si la validation minimale échoue.
|
||
10. **Impératif : Set-OPS doit rester pleinement exploitable par un humain SANS IA.** La doc, `make` et le GUI sont l’interface primaire et complète. Ne jamais introduire de fonctionnalité qui *exige* une IA pour s’en servir. L’IA n’assiste que le mainteneur pour faire évoluer l’outil — jamais l’utilisateur final. (Le bon étalon : un sysadmin humain réussit depuis la doc. `AGENTS.md`/`CLAUDE.md` ne font pas partie de l’outil livré.)
|
||
|
||
---
|
||
|
||
## Sécurité
|
||
|
||
Ne jamais commiter :
|
||
|
||
- mots de passe ;
|
||
- clés privées SSH ;
|
||
- tokens API ;
|
||
- secrets non chiffrés ;
|
||
- fichiers `.env` sensibles ;
|
||
- certificats privés ;
|
||
- backups réels ;
|
||
- exports de production non anonymisés.
|
||
|
||
Les secrets doivent être gérés hors dépôt ou avec un mécanisme explicitement prévu, par exemple :
|
||
|
||
- Ansible Vault ;
|
||
- fichier local non versionné ;
|
||
- secret injecté hors dépôt ;
|
||
- gestionnaire de secrets approuvé.
|
||
|
||
---
|
||
|
||
## Actions destructives
|
||
|
||
Toute action destructive doit exiger une variable explicite, par exemple :
|
||
|
||
```yaml
|
||
confirm_destructive_action: true
|
||
```
|
||
|
||
Sont considérées comme destructives ou risquées :
|
||
|
||
- modification bloquante de SSH ;
|
||
- activation ou modification d’un pare-feu ;
|
||
- suppression d’utilisateurs ;
|
||
- suppression de paquets critiques ;
|
||
- formatage disque ;
|
||
- modification de partitions ;
|
||
- redémarrage massif ;
|
||
- purge de données ;
|
||
- changement réseau pouvant couper l’accès ;
|
||
- modification d’un hyperviseur Proxmox ;
|
||
- opération sur stockage, iSCSI, ZFS ou Ceph.
|
||
|
||
Sans confirmation explicite, refuser l’exécution.
|
||
|
||
---
|
||
|
||
## Règle de modification du dépôt
|
||
|
||
Avant toute modification, exécuter ou demander l’équivalent de :
|
||
|
||
```bash
|
||
git status --short
|
||
find . -maxdepth 3 -type f | sort
|
||
```
|
||
|
||
Ne pas remplacer massivement l’arborescence sans demande explicite.
|
||
|
||
Avant de modifier un fichier, lire son contenu actuel.
|
||
|
||
Après modification, indiquer clairement :
|
||
|
||
- les fichiers créés ;
|
||
- les fichiers modifiés ;
|
||
- les commandes de validation exécutées ;
|
||
- les tests non exécutés ;
|
||
- les limites connues.
|
||
|
||
---
|
||
|
||
## Validation Ansible obligatoire
|
||
|
||
Avant de proposer un changement comme terminé, vérifier au minimum la syntaxe du playbook touché.
|
||
|
||
Exemple pour le template Debian 13 Proxmox :
|
||
|
||
```bash
|
||
ansible-playbook -i "$SETOPS_INVENTAIRE" playbooks/modeles_vm/debian13_proxmox_preparer.yml --syntax-check
|
||
```
|
||
|
||
`SETOPS_INVENTAIRE` est **exporté par le `Makefile`**, qui résout le nom de l’inventaire au lieu de le coder en dur (cf. la règle d’or ci-dessus) — la variable est donc déjà là dans toute recette `make`. Pour couvrir tous les playbooks d’un coup : `make syntaxe`. Depuis un shell nu, la variable n’existe pas : passer le chemin réel de l’inventaire de l’instance montée.
|
||
|
||
Pour un autre playbook, remplacer le chemin par le playbook concerné.
|
||
|
||
Ne jamais déclarer un playbook prêt si `--syntax-check` échoue.
|
||
|
||
Si `ansible-lint` est disponible, l’utiliser :
|
||
|
||
```bash
|
||
ansible-lint
|
||
```
|
||
|
||
Si `ansible-lint` n’est pas disponible, le signaler clairement. Ne pas inventer un résultat.
|
||
|
||
---
|
||
|
||
## Écrire, puis relire (D-68)
|
||
|
||
`--syntax-check` et `ansible-lint` prouvent que le dépôt est cohérent **avec lui-même**.
|
||
C'est aussi ce que font les 60 preuves de `make prouver` : elles lisent le dépôt, sans le
|
||
moindre appel réseau. **Aucune ne demande au système déployé s'il ressemble à ce que le
|
||
dépôt annonce.**
|
||
|
||
C'est dans cet angle que vivaient les défauts du 2026-08-08 : une politique de mot de
|
||
passe déclarée des deux côtés et appliquée d'aucun, une entrée LDAP figée à sa création,
|
||
le certificat de l'autorité expiré depuis huit heures, la livraison de courriel interne
|
||
différée en silence. Tous découverts en **relisant après avoir écrit**, aucun signalé par
|
||
un test.
|
||
|
||
**La règle : écrire, puis relire et comparer — quelle que soit l'interface.** Choisir
|
||
celle dont le chemin de *lecture* parle le même langage que le chemin d'*écriture*. Ce
|
||
n'est **pas** « toujours préférer l'API » : la plupart de la flotte n'en a pas, et sur six
|
||
familles de défauts ce jour-là, deux seulement venaient d'un CLI — un module Ansible
|
||
(`ldap_entry`, qui crée sans jamais modifier) a commis la même faute.
|
||
|
||
Cas connu à ne pas réapprendre : **`kcadm -s` sur une map** (`smtpServer`, `attributes`,
|
||
`config`) accepte la commande, **sort en succès et n'écrit rien**. Passer par l'API
|
||
d'administration pour ces objets, et relire (D-69).
|
||
|
||
Les **devis de service** industrialisent cette relecture — voir `docs/devis-services.md` :
|
||
|
||
```bash
|
||
make identite-plan make certificats-plan make expositions-plan
|
||
make postgresql-plan make courriel-plan
|
||
```
|
||
|
||
Le même patron existe **sous** les services, pour le monde physique — `make frontiere-plan`,
|
||
`proxmox-fw-plan`, `sdn-plan`, `underlay-plan`, `placement-plan`.
|
||
|
||
Ils ne modifient rien et sortent en code 1 s'il y a un écart. Après un changement qui
|
||
touche l'identité, les certificats, une exposition, la base ou le courriel, **lancer le
|
||
devis correspondant** : une tâche verte ne prouve pas que le service rend son service.
|
||
|
||
---
|
||
|
||
## Règle pour les handlers Ansible
|
||
|
||
Chaque rôle qui utilise `notify` doit contenir son handler dans le rôle lui-même.
|
||
|
||
Exemple :
|
||
|
||
```text
|
||
roles/ssh_baseline/tasks/main.yml
|
||
roles/ssh_baseline/handlers/main.yml
|
||
```
|
||
|
||
Ne pas dépendre d’un handler défini dans un autre rôle, sauf justification explicite.
|
||
|
||
Tout `notify` doit pointer vers un handler existant.
|
||
|
||
Commandes de vérification utiles :
|
||
|
||
```bash
|
||
find roles -path '*/tasks/*.yml' -exec grep -H "notify:" {} \;
|
||
find roles -path '*/handlers/main.yml' -print
|
||
```
|
||
|
||
Avant de livrer un rôle, vérifier que chaque handler référencé existe réellement.
|
||
|
||
---
|
||
|
||
## Style Ansible attendu
|
||
|
||
Les playbooks doivent être :
|
||
|
||
- idempotents ;
|
||
- lisibles ;
|
||
- sobres ;
|
||
- compatibles Debian 13 sauf exception documentée ;
|
||
- testables avec `--check` autant que possible ;
|
||
- sécuritaires par défaut ;
|
||
- sans dépendances SaaS ou cloud inutiles.
|
||
|
||
Préférer les modules Ansible standards :
|
||
|
||
- `ansible.builtin.apt`
|
||
- `ansible.builtin.template`
|
||
- `ansible.builtin.copy`
|
||
- `ansible.builtin.service`
|
||
- `ansible.builtin.systemd`
|
||
- `ansible.builtin.lineinfile`
|
||
- `ansible.builtin.file`
|
||
- `ansible.builtin.user`
|
||
- `ansible.builtin.group`
|
||
|
||
Éviter `shell` et `command` sauf nécessité réelle.
|
||
|
||
Lorsqu’une commande shell est nécessaire, elle doit être encadrée avec les paramètres appropriés selon le cas :
|
||
|
||
- `changed_when`
|
||
- `failed_when`
|
||
- `creates`
|
||
- `removes`
|
||
|
||
---
|
||
|
||
## Structure générale du dépôt
|
||
|
||
Ce que la racine porte réellement (mesuré le 2026-09-06) :
|
||
|
||
```text
|
||
roles/ les rôles Ansible
|
||
playbooks/ les playbooks, classés par domaine
|
||
scripts/ le moteur de plan, la GUI, les devis, les preuves
|
||
filter_plugins/ les filtres Jinja du dépôt
|
||
docs/ wiki/ la documentation
|
||
exemples/ les modèles d'instance prêts à copier
|
||
instance/ SYMLINK vers le dépôt de l'instance active — jamais un vrai dossier ici
|
||
```
|
||
|
||
**Il n'y a ni `inventories/`, ni `templates/`, ni `files/` à la racine**, et il ne doit pas y en avoir : les inventaires appartiennent à l'instance (derrière le symlink), les gabarits et fichiers appartiennent à leur rôle.
|
||
|
||
Les playbooks sont classés par domaine :
|
||
|
||
```text
|
||
playbooks/
|
||
├── groupes/ un playbook par groupe opérationnel — la conformité passe par là
|
||
├── maintenance/ les devis et les manœuvres ponctuelles
|
||
├── modeles_vm/ la fabrication du gabarit doré
|
||
├── proxmox/ le clonage et le cycle de vie des VM
|
||
└── applications/ backup/ database/ monitoring/ web/ — vides, un README d'espace réservé
|
||
```
|
||
|
||
Seuls les quatre premiers sont peuplés. Les cinq autres ne contiennent qu'un README : ce sont des **espaces réservés**, et leur existence contredit à demi la règle « ne pas créer de structure inutile » ci-dessous — les laisser vides est un choix assumé, en créer d'autres ne l'est pas.
|
||
|
||
Les rôles peuvent être ajoutés progressivement selon les besoins.
|
||
|
||
Ne pas créer de structure inutile uniquement pour donner une impression de complétude.
|
||
|
||
La conformité normale des VM déployées doit passer par `playbooks/groupes/`.
|
||
|
||
Ne pas maintenir en parallèle des playbooks de couches génériques comme `playbooks/socle/` ou `playbooks/durcissement/` lorsqu'un groupe opérationnel exprime déjà cet état voulu.
|
||
|
||
---
|
||
|
||
## Interface opérateur Makefile
|
||
|
||
Le `Makefile` est l'interface opérateur privilégiée pour les gestes courants.
|
||
|
||
Les commandes `make` doivent simplifier l'exploitation Ansible sans masquer les playbooks réellement exécutés.
|
||
|
||
Préférer quelques cibles claires et utiles :
|
||
|
||
- validation du dépôt ;
|
||
- inspection des inventaires ;
|
||
- ajout ou mise à jour d'un hôte dans un inventaire ;
|
||
- association d'un hôte à ses groupes ;
|
||
- déploiement ou remise en conformité d'un hôte ;
|
||
- déploiement ou remise en conformité d'un groupe ;
|
||
- préparation, vérification et nettoyage protégé d'un modèle de VM.
|
||
|
||
Ne pas multiplier les cibles `make` secondaires si elles ne correspondent pas à un geste réel d'exploitation.
|
||
|
||
Les cibles d'exploitation des VM doivent privilégier les groupes :
|
||
|
||
```text
|
||
make deployer HOTE=web-frontal-01
|
||
make deployer-groupe GROUPE=serveur_debian
|
||
```
|
||
|
||
**`make hote-planifier`, `hote-ajouter` et `hote-groupes` sont DÉPRÉCIÉES** — elles refusent et sortent en 2. Elles éditaient l'inventaire à la main, ce que la RÈGLE D'OR interdit. Pour ajouter un hôte : le déclarer dans `instance/plan/serveurs.yml` (ou la vue **Serveurs** du GUI), puis `make instancier-appliquer`. Le VMID n'est plus saisi : il est **dérivé** (neuf chiffres, miroir de l'IP — `117602101`, et non le format à cinq chiffres d'avant).
|
||
|
||
Éviter les cibles parallèles qui réappliquent les mêmes rôles par couche, par exemple `make socle`, `make durcissement`, `make converger` ou `make deployer-vm`.
|
||
|
||
Toute cible `make` qui lance une action destructive ou risquée doit exiger une confirmation explicite.
|
||
|
||
---
|
||
|
||
## Convention groupes et playbooks
|
||
|
||
Chaque groupe opérationnel Ansible doit avoir un playbook homonyme dans `playbooks/groupes/`.
|
||
|
||
La convention attendue est :
|
||
|
||
```text
|
||
groupe Ansible : serveur_debian
|
||
playbook : playbooks/groupes/serveur_debian.yml
|
||
```
|
||
|
||
L'appartenance aux groupes détermine les services, intégrations et politiques appliqués à une VM.
|
||
|
||
`playbooks/groupes/` est la source officielle de conformité pour les VM déployées.
|
||
|
||
Un playbook de groupe doit cibler son groupe homonyme, pas `all`, sauf justification explicite.
|
||
|
||
Les concepts comme socle Debian ou durcissement commun doivent être représentés par des groupes explicites :
|
||
|
||
```text
|
||
serveur_debian -> socle commun Debian
|
||
serveur_durci -> durcissement commun
|
||
client_pki -> intégration cliente PKI / ACME
|
||
```
|
||
|
||
Ne pas dupliquer ces mêmes rôles dans des playbooks de couches séparés.
|
||
|
||
Quand un nouveau groupe opérationnel est ajouté :
|
||
|
||
- créer le playbook homonyme dans `playbooks/groupes/` ;
|
||
- documenter son intention opérationnelle ;
|
||
- déclarer ses dépendances causales dans `docs/dependances-groupes.yml` si son exécution requiert un autre service actif ;
|
||
- prévoir les rôles nécessaires ;
|
||
- valider au minimum sa syntaxe ;
|
||
- l'ajouter aux facilités d'exploitation si l'opérateur doit l'utiliser directement.
|
||
|
||
---
|
||
|
||
## Cycle de vie et conformité des VM
|
||
|
||
Set-OPS doit gérer le cycle de vie complet des VM de l’instance :
|
||
|
||
```text
|
||
VM Debian minimale
|
||
→ goldenisation du template
|
||
→ clonage
|
||
→ identité initiale cloud-init
|
||
→ conformité par groupes Ansible
|
||
→ intégrations transversales
|
||
→ conformité continue
|
||
```
|
||
|
||
Le template est seulement la fondation. Les VM existantes et futures doivent converger vers l'état voulu par les playbooks de groupes.
|
||
|
||
Les rôles et playbooks doivent donc être conçus pour être relancés régulièrement, sans effet secondaire inutile.
|
||
|
||
Le groupe d'inventaire attendu pour les VM Debian gérées est :
|
||
|
||
```text
|
||
serveur_debian
|
||
```
|
||
|
||
Les groupes spécialisés doivent s'ajouter selon les besoins réels, par exemple :
|
||
|
||
```text
|
||
client_pki
|
||
client_metrique
|
||
serveur_postgresql
|
||
serveur_prometheus
|
||
serveur_keycloak
|
||
```
|
||
|
||
Ne pas cibler `all` par défaut pour un playbook qui ne s'applique pas réellement à tous les hôtes.
|
||
|
||
---
|
||
|
||
## Services centraux et intégrations clientes
|
||
|
||
Pour chaque service d'infrastructure, distinguer deux responsabilités :
|
||
|
||
```text
|
||
service serveur : installe et configure le service central
|
||
intégration cliente : raccorde les VM au service central
|
||
```
|
||
|
||
Exemples :
|
||
|
||
```text
|
||
PowerDNS serveur → hosts_statiques (plancher) + client_resolveur (opt-in)
|
||
step-ca serveur → client_pki (certificat + renouvellement + rechargement du service)
|
||
LDAP serveur → resoudre_annuaire, et les applications qui s'y lient (Postfix, Dovecot, Keycloak)
|
||
Keycloak serveur → intégrations OIDC applicatives, ou serveur_oauth2_proxy si l'app n'a pas d'OIDC
|
||
Prometheus → client_metrique (node_exporter) sur les VM
|
||
Icinga2 → SANS AGENT : contrôles actifs depuis le cœur, résultats passifs poussés par l'API
|
||
Grafana → datasources et dashboards côté plateforme
|
||
```
|
||
|
||
Deux précisions qui ont manqué ici longtemps, et qui changent la conception d'un nouveau service :
|
||
|
||
- **Pas de login LDAP au niveau du système.** `SSSD` / `NSS` / `PAM` ne sont pas la couche cliente de l'annuaire : le rôle `client_ldap` a été **retiré (2026-07-04), hors conception**. L'annuaire sert les *applications*, pas l'ouverture de session Unix.
|
||
- **Pas d'agent de supervision.** Icinga ne pose rien sur les hôtes. Un nœud qui doit rapporter le fait **lui-même**, en poussant son résultat à l'API — c'est ainsi que `client_backup` rapporte l'état de son propre dépôt. Ne pas prévoir de rôle « agent ».
|
||
|
||
Quand un nouveau service central est ajouté, prévoir aussi le ou les rôles clients nécessaires pour intégrer les VM existantes et futures.
|
||
|
||
Les dépendances entre groupes doivent être déclarées dans :
|
||
|
||
```text
|
||
docs/dependances-groupes.yml
|
||
```
|
||
|
||
Ces dépendances servent à refuser un déploiement lorsque les prérequis actifs sont absents et à préparer les futurs checks de supervision.
|
||
|
||
Les intégrations clientes doivent être :
|
||
|
||
- idempotentes ;
|
||
- activables par inventaire ou variables ;
|
||
- désactivées par défaut si leur dépendance centrale n'existe pas ;
|
||
- documentées avec leurs prérequis ;
|
||
- testables avec `--syntax-check` et, lorsque possible, `--check`.
|
||
|
||
Ne pas mettre les intégrations applicatives ou les agents spécialisés dans le golden template, sauf justification opérationnelle explicite.
|
||
|
||
---
|
||
|
||
## Variables et inventaires
|
||
|
||
Les variables de rôle doivent utiliser un préfixe correspondant au rôle.
|
||
|
||
Exemples :
|
||
|
||
```yaml
|
||
ssh_hardening_port: 22 # rôle ssh_hardening
|
||
nftables_baseline_enabled: false # rôle nftables_baseline
|
||
fail2ban_ssh_enabled: true # rôle fail2ban_ssh
|
||
```
|
||
|
||
Le préfixe est **le nom du rôle, tel qu'il est**, pas sa traduction : ces trois lignes sont copiées des `defaults/main.yml` réels. (Elles disaient `ssh_durcissement_port` et `nftables_socle_enabled` jusqu'au 2026-09-06 — deux préfixes qui ne correspondaient à aucun rôle, dans l'exemple censé illustrer la règle.)
|
||
|
||
Séparer clairement :
|
||
|
||
```text
|
||
variables de template : construction du golden template
|
||
variables de conformité : état voulu des VM déployées
|
||
variables applicatives : services et rôles spécialisés
|
||
```
|
||
|
||
Les variables de conformité ne doivent pas couper l'accès SSH, DNS ou réseau sans validation explicite.
|
||
|
||
Toute variable susceptible de provoquer une action destructive, bloquante ou irréversible doit exiger une confirmation explicite.
|
||
|
||
---
|
||
|
||
## Langue et nommage
|
||
|
||
La surface destinée à l'opérateur doit être en français.
|
||
|
||
À franciser :
|
||
|
||
- noms de groupes d'inventaire ;
|
||
- cibles `make` ;
|
||
- variables de commande maison ;
|
||
- libellés et messages des scripts maison ;
|
||
- noms des playbooks maison quand ils sont exposés à l'opérateur ;
|
||
- documentation d'exploitation.
|
||
|
||
À ne pas franciser automatiquement :
|
||
|
||
- mots-clés Ansible (`hosts`, `roles`, `tasks`, `handlers`, `vars`, `become`) ;
|
||
- noms de modules Ansible (`ansible.builtin.apt`, `ansible.builtin.template`, etc.) ;
|
||
- conventions techniques imposées par un outil ;
|
||
- noms de rôles existants, sauf demande explicite ou migration ciblée justifiée.
|
||
|
||
Les variables de rôle peuvent rester alignées sur le nom technique du rôle, mais elles doivent conserver un préfixe clair et stable.
|
||
|
||
Ne pas faire de renommage massif uniquement pour franciser si cela augmente le risque sans bénéfice opérationnel immédiat.
|
||
|
||
---
|
||
|
||
## Template Debian 13 Proxmox
|
||
|
||
Le template Debian 13 Proxmox doit rester un socle commun.
|
||
|
||
Il peut contenir :
|
||
|
||
- Debian minimal ;
|
||
- SSH ;
|
||
- sudo ;
|
||
- compte technique `ansible` ;
|
||
- sudo NOPASSWD pour `ansible` lorsque requis ;
|
||
- `qemu-guest-agent` ;
|
||
- `cloud-init` ;
|
||
- `cloud-guest-utils` ;
|
||
- chrony ;
|
||
- outils de diagnostic de base ;
|
||
- durcissement raisonnable ;
|
||
- AppArmor ;
|
||
- auditd ;
|
||
- fail2ban SSH ;
|
||
- unattended-upgrades ;
|
||
- configuration journald ;
|
||
- sysctl de sécurité ;
|
||
- nftables installé et préparé, mais pas forcément activé.
|
||
|
||
Il ne doit pas contenir par défaut :
|
||
|
||
- NGINX ;
|
||
- PostgreSQL ;
|
||
- MariaDB ;
|
||
- Docker ;
|
||
- Podman ;
|
||
- Redis ;
|
||
- GitLab ;
|
||
- Nextcloud ;
|
||
- monitoring complet ;
|
||
- agents applicatifs spécialisés ;
|
||
- données propres à un clone ;
|
||
- secrets ;
|
||
- clés privées.
|
||
|
||
Les services spécialisés doivent être installés ensuite par des playbooks dédiés sur les clones.
|
||
|
||
---
|
||
|
||
## SSH
|
||
|
||
Le template Debian 13 Proxmox doit être construit avec un accès SSH par clé dès le départ.
|
||
|
||
Cloud-init doit injecter le `ciuser` et sa clé publique avant l'exécution d'Ansible.
|
||
|
||
État attendu :
|
||
|
||
```text
|
||
PasswordAuthentication no
|
||
PermitRootLogin no
|
||
PubkeyAuthentication yes
|
||
AuthenticationMethods publickey
|
||
```
|
||
|
||
Ne pas lancer le playbook de préparation tant que l'accès SSH par clé au compte technique n'est pas confirmé.
|
||
|
||
Toute modification SSH doit valider la configuration avant rechargement :
|
||
|
||
```bash
|
||
sshd -t
|
||
```
|
||
|
||
---
|
||
|
||
## Pare-feu
|
||
|
||
Ne pas activer un pare-feu générique dans un template sans confirmation explicite.
|
||
|
||
Pour le template Debian 13, `nftables` peut être installé et préparé, mais rester désactivé par défaut.
|
||
|
||
L’activation du pare-feu doit être faite sur un clone ou sur un serveur final. **Ses règles ne s’écrivent pas à la main** : elles sont *dérivées* du registre des flux — chaque rôle déclare ce qu’il reçoit dans son `meta/flux.yml`, et `scripts/resoudre_flux.py` en produit le jeu de règles. Le même registre alimente le pare-feu de l’hyperviseur et la frontière OPNsense, ce qui leur interdit de se contredire. Voir `docs/flux-conception.md` et `docs/registre-flux.md`.
|
||
|
||
---
|
||
|
||
## Cloud-init
|
||
|
||
Cloud-init sert à donner l’identité initiale d’un clone :
|
||
|
||
- hostname ;
|
||
- utilisateur initial ;
|
||
- clé SSH ;
|
||
- adresse IP ;
|
||
- passerelle ;
|
||
- DNS ;
|
||
- agrandissement de la partition racine.
|
||
|
||
Cloud-init ne doit pas remplacer Ansible pour la configuration applicative.
|
||
|
||
Séparation attendue :
|
||
|
||
```text
|
||
Proxmox + cloud-init : identité initiale de la VM
|
||
Set-OPS + Ansible : configuration réelle du serveur
|
||
```
|
||
|
||
---
|
||
|
||
## Nettoyage avant template
|
||
|
||
Le nettoyage final avant conversion en template doit être protégé par une confirmation explicite.
|
||
|
||
Exemple :
|
||
|
||
```bash
|
||
ansible-playbook -i "$SETOPS_INVENTAIRE" playbooks/modeles_vm/debian13_proxmox_nettoyer.yml -e template_cleanup_confirm=true
|
||
```
|
||
|
||
Le nettoyage peut inclure :
|
||
|
||
- `cloud-init clean --logs` ;
|
||
- nettoyage du cache APT ;
|
||
- rotation ou purge contrôlée des journaux ;
|
||
- vidage de `/etc/machine-id` ;
|
||
- remise en place du lien `/var/lib/dbus/machine-id` ;
|
||
- suppression des historiques shell.
|
||
|
||
Ne jamais lancer ce nettoyage sur un serveur de production sans confirmation explicite.
|
||
|
||
---
|
||
|
||
## CHANGELOG
|
||
|
||
Chaque modification significative doit être inscrite dans `CHANGELOG.md`.
|
||
|
||
**Le format a changé, et ce fichier disait encore l'ancien.** Les entrées d'avant le
|
||
2026-08-03 suivaient trois sections fixes (`### Ajouté` / `### Modifié` / `### Corrigé`).
|
||
Depuis, chaque entrée est un **récit** : ce qui était cassé, pourquoi personne ne le voyait,
|
||
ce que ça coûte de le réapprendre. Suivre la pratique en vigueur :
|
||
|
||
```markdown
|
||
## AAAA-MM-JJ — un titre qui dit CE QUI A ÉTÉ APPRIS, pas ce qui a été touché
|
||
|
||
**N preuves.** Une ou deux phrases : l'état du harnais, et l'enjeu.
|
||
|
||
### Un sous-titre par piège payé
|
||
Ce que le dépôt croyait, ce que la machine faisait, et la mesure qui a tranché.
|
||
```
|
||
|
||
Deux entrées le même jour se distinguent par un rang : `## 2026-09-02 (6) — …`.
|
||
|
||
Les corrections de rôles, handlers, playbooks et templates doivent être consignées.
|
||
|
||
---
|
||
|
||
## Stabilisation des changements
|
||
|
||
Après une série cohérente de changements validés, recommander un commit propre avant de poursuivre vers un nouveau chantier.
|
||
|
||
Avant de recommander un commit :
|
||
|
||
- vérifier l'état Git ;
|
||
- résumer les fichiers créés, modifiés, déplacés ou supprimés ;
|
||
- indiquer les validations exécutées ;
|
||
- signaler les validations non exécutées ;
|
||
- mentionner les risques ou limites connus.
|
||
|
||
Ne pas créer de commit sans demande explicite.
|
||
|
||
---
|
||
|
||
## Comportement attendu des agents IA
|
||
|
||
Ce comportement s'applique à **tout agent IA** travaillant dans ce dépôt (Codex, Claude
|
||
Code, ou autre), conformément à la règle d'or « un seul agent IA à la fois ».
|
||
|
||
Avant de modifier :
|
||
|
||
1. Lire `AGENTS.md`.
|
||
2. Lire `README.md`, `CHANGELOG.md` et `ansible.cfg` s’ils existent.
|
||
3. Vérifier l’état Git.
|
||
4. Inspecter les fichiers concernés.
|
||
5. Identifier la portée exacte de la demande.
|
||
6. Proposer le plus petit changement utile.
|
||
7. Préserver les conventions existantes.
|
||
|
||
Après modification :
|
||
|
||
1. Résumer les fichiers créés ou modifiés.
|
||
2. Indiquer les commandes de validation exécutées.
|
||
3. Signaler clairement ce qui n’a pas été testé.
|
||
4. Mettre à jour `CHANGELOG.md` si pertinent.
|
||
5. Ne pas affirmer que c’est prêt si la validation a échoué.
|
||
|
||
---
|
||
|
||
## Philosophie
|
||
|
||
Set-OPS est un outil d’exploitation réelle, pas une démonstration technique.
|
||
|
||
Priorités :
|
||
|
||
- reproductibilité ;
|
||
- sobriété ;
|
||
- clarté ;
|
||
- sécurité ;
|
||
- maintenance ;
|
||
- autonomie ;
|
||
- résilience.
|
||
|
||
La complexité doit toujours être justifiée par un bénéfice opérationnel clair.
|