La bordure devient un artefact dérivé, comme le devis switch — et le chemin
qui y mène est enfin déclaré.
`make devis-opnsense` (+ preuve P24) dérive la politique de bordure du
registre des flux : les flux `pair: externe`, que `resoudre_flux.py` saute
volontairement parce qu'ils relèvent de la frontière et non du pare-feu
d'hôte. Aucun port, aucune adresse, aucun nom d'hôte dans le générateur.
Le lien manquait dans tous les fichiers : le devis switch ne contenait pas
une seule `ip route`. Un réseau underlay portant `passerelle_sortie` le
déclare — il vit dans l'underlay et non dans un tenant parce que la
frontière route vers TOUS les supernets tenants par le même saut, donc il
ne peut dériver d'aucun `index`. `devis-reseau` en tire deux routes :
l'aller (sortie générale) et le retour vers l'administration, dont l'absence
a coûté la passe de déploiement du 2026-07-29 — la réponse revient au
pare-feu par une autre interface que celle où l'état a été créé, et se fait
jeter en silence.
Les réseaux d'administration viennent de l'intrant `nftables_admin_ssh` :
même source unique que la garde anti-lockout des nftables et l'alias
SETOPS_ADMIN. Les trois pare-feux et les routes ne peuvent plus diverger.
La frontière est réglable depuis la console (section « Frontière » du
panneau Intrants) ; les identifiants d'API restent interdits d'écriture par
le GUI et vivent dans la voûte.
Correctifs de la même passe :
- le panneau refusait d'enregistrer les intrants de la frontière : le
garde-fou confondait une référence de voûte `{{ vault_* }}` préservée
avec un secret soumis. Il regarde désormais la valeur, pas le nom.
- `supprimer_vm_debian.yml` ne chargeait que `proxmox.vault.yml` pour ses
secrets ; retirer ce reliquat aurait cassé `make detruire`. Aligné sur le
playbook de clonage, voûte unique en dernier.
- documentation : la voûte est unique, `proxmox.vault.yml` n'est qu'un
reliquat de compatibilité.
Preuves : 24 OK, 0 échec. Cas de rejet du validateur d'underlay exercés un
par un ; résolution du jeton Proxmox vérifiée en exécution réelle.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
83 lines
7.6 KiB
Markdown
83 lines
7.6 KiB
Markdown
# Carte d'orientation Set-OPS
|
|
|
|
> **À lire en premier.** Point d'entrée vers le corpus documentaire, et **catalogue des
|
|
> mécanismes transverses** — ceux qui vivent dans le code et qu'on *re-découvre* sinon.
|
|
> Créée le 2026-07-03 après un audit du dépôt, **revue le 2026-07-29**. But : ne plus
|
|
> re-déterrer ce qui existe.
|
|
|
|
Le dépôt est **déjà bien documenté** (26 docs + 7 pièces d'audit + 21 unités de wiki, et un
|
|
README par rôle). Le manque n'était pas la doc du *modèle*,
|
|
mais (a) un index « par où commencer » et (b) une carte des *mécanismes* (dispersés dans le
|
|
code + les README de rôles). Cette page comble ces deux trous.
|
|
|
|
## 1. À lire d'abord (dans l'ordre)
|
|
|
|
| Sujet | Documents |
|
|
|---|---|
|
|
| **Autorité / gouvernance** | `AGENTS.md` (source d'autorité), `CLAUDE.md`, `docs/MISE-A-JOUR-CODEX-CLAUDE.md` |
|
|
| **Le modèle (plan)** | `docs/architecture-set-ops.md` (survol) → `docs/plan-et-generation.md` (à fond) → `docs/meta-classe.md` (concept) |
|
|
| **Services, maturité, dette** | `docs/catalogue-services.md` (**la carte de maturité + la cruft y sont déjà**) |
|
|
| **Exploitation / VM** | `docs/vm-lifecycle.md`, `docs/procedure-template-debian13-proxmox.md`, `docs/config-proxmox.md`, `docs/nomenclature-vm.md`, `docs/multi-instances.md` |
|
|
| **Conceptions de domaine** | `docs/identite-sso.md`, `docs/courriel-conception.md`, `docs/bindings-conception.md`, `docs/dns-interne.md`, `docs/dimensionnement-ressources.md`, `docs/integrations-vm.md` |
|
|
| **Réseau / pare-feu** | `docs/flux-conception.md` (le modèle) → `docs/registre-flux.md` (**généré**, matrice d'audit) → `docs/frontiere-opnsense.md` (la bordure nord/sud) ; underlay : `underlay.yml.example` + `make underlay` |
|
|
| **Ordre de déploiement** | `docs/couches-deploiement.yml` (couches) + `docs/dependances-groupes.yml` (graphe) → `playbooks/site.yml` (**généré**, `make site`) |
|
|
| **Preuve / recette** | `docs/audit/affirmations.md` (registre), `make prouver` → `docs/audit/preuve-<date>.md`, `docs/audit/plan-de-recette.md` (**généré** du wiki), `docs/audit/protocole-operateur-independant.md` |
|
|
| **Exploitation courante** | `docs/runbooks-exploitation.md`, `docs/intrants-communs.md`, `docs/intrants-base-gui-conception.md`, `docs/theme-forgejo-hors-flotte.md` |
|
|
| **Pédagogie (le wiki)** | `wiki/` — 21 unités (+ `_Sidebar`) publiées par `make wiki-publier` ; entrer par `wiki/Home.md` |
|
|
| **Vision / positionnement** | `docs/ecosysteme-chezlepro.md`, `docs/positionnement.md`, `docs/pouvoirs-set-ops.md` |
|
|
|
|
## 2. Les mécanismes transverses (et OÙ ils vivent)
|
|
|
|
Ce que je re-découvre sinon. **Consulter avant de concevoir un nouveau mécanisme.**
|
|
|
|
| Mécanisme | Ce que c'est | Où, dans le code | Doc |
|
|
|---|---|---|---|
|
|
| Plan → inventaire | `plan/*.yml` → `hosts.yml` généré | `scripts/instancier.py`, `scripts/inventory_rules.py` | `plan-et-generation.md` |
|
|
| Nomenclature dérivée | VMID / IP / VLAN / FQDN dérivés | `inventory_rules.deriver_nomenclature` + `plan/nomenclature.yml` | `nomenclature-vm.md` |
|
|
| Dimensionnement | RAM/CPU/disque sommés par logiciel | `roles/*/meta/empreinte.yml` → `deriver_ressources` | `dimensionnement-ressources.md` |
|
|
| **Bindings app→app** | lien **côté app** (`liens`) résolu en host_vars | `plan/applications.yml` `liens:` + `roles/*/meta/liens.yml` + `instancier.resoudre_liens` | `bindings-conception.md` |
|
|
| **Bindings app→base** | lien **côté base** (`consommateur`/`portee`) résolu **dans le rôle** | `plan/bases-donnees.yml` + rôle utilitaire `resoudre_base` (`lookup('vars', secret)`, `no_log`) inclus par le consommateur | `bindings-conception.md` §5 |
|
|
| Résolution d'annuaire | connexion LDAP (uri/base DN/bind) **dérivée**, jamais recopiée | rôle utilitaire `resoudre_annuaire` (inclus par dovecot/postfix/keycloak/icingaweb2) | `identite-sso.md` |
|
|
| Plancher de résolution | `/etc/hosts` généré depuis l'inventaire + alias d'`expose` → l'écosystème se résout **DNS éteint** | rôle `hosts_statiques` (appliqué dans la couche socle) | `dns-interne.md` |
|
|
| Pont de certificat | cert step_ca → service, resync au renouvellement | script `*-cert-sync` + unité `.path`, dans chaque rôle serveur ; cert déposé par `client_pki` | — |
|
|
| Ordonnancement socle-first | socle/durci avant les `client_*` | `serveur_debian`/`serveur_durci` d'abord (posent `/etc/hosts` via `hosts_statiques`) | — |
|
|
| Sûreté check-mode | dry-run fiable | `when: not ansible_check_mode` sur les tâches de service + handlers | — |
|
|
| Voûte au déploiement | secret jamais en clair | `ANSIBLE_VAULT_PASSWORD_FILE` / `~/.config/setops-vault-pass` ; déréférencé par `lookup('vars', <nom>)` | — |
|
|
| Multi-instance | un dépôt par écosystème ; l'active = symlink `instance/`, les autres **découvertes par convention** (dossiers frères, aucun registre) | active : symlink `instance/` ; découverte : `scripts/instances.py` / `devis_reseau.py` (glob `../*/plan/nomenclature.yml` avec `index`) ; garde-fou collision : preuve **P21** | `multi-instances.md` |
|
|
| Exposition → edge | app expose un FQDN public servi par un edge | `plan/domaines.yml` + `expose` (applications) | `bindings-conception.md` §4 |
|
|
| Frontière nord/sud | les flux `pair: externe` — **sautés** par le pare-feu d'hôte — sont la politique de bordure | `scripts/devis_opnsense.py` (`make devis-opnsense`) ; garde d'accès admin = preuve **P24** | `frontiere-opnsense.md` |
|
|
|
|
> ⚠️ **Deux directions de binding, assumées** : `app→app` côté app (instancier),
|
|
> `app→base` côté base (registre, résolu en rôle pour que le secret ne quitte jamais le rôle).
|
|
> Ne pas unifier l'un dans l'autre sans raison. Cf. `bindings-conception.md`.
|
|
|
|
## 3. Maturité & dette
|
|
|
|
- **Maturité des rôles, échafaudages, rôles-catégories inertes** : voir
|
|
`docs/catalogue-services.md` « État d'implémentation » (source de vérité, tenue à jour).
|
|
- **Dette (audit 2026-07-03, revu le 2026-07-29)** :
|
|
- ✅ **soldé** — README de rôles : **tous les rôles en ont un** (les 12 manquants écrits le
|
|
2026-07-29 : `serveur_debian`, `hosts_statiques`, `resoudre_base`, `resoudre_annuaire`,
|
|
`serveur_dovecot`, `serveur_postfix`, `serveur_rspamd`, `client_backup`, `serveur_backup`,
|
|
`client_unbound`, `serveur_oauth2_proxy`, `serveur_icingaweb2`).
|
|
- ✅ **soldé** — `expose` **est** consommé au déploiement : `plan/applications.yml` →
|
|
filtre `expositions_des_applications` → vhosts nginx dérivés
|
|
(`roles/serveur_nginx/tasks/main.yml`, template `expositions.conf.j2`, drapeau
|
|
`serveur_nginx_publier_expositions`), + alias `/etc/hosts` posés par `hosts_statiques`,
|
|
+ SANs des certificats d'edge dérivés par `scripts/instancier.py`.
|
|
- ⏳ **ouvert** — `meta/liens.yml` seulement sur `serveur_postfix` (`mailstore`, `milter`) :
|
|
les autres liens app→app viendront.
|
|
- ⏳ **ouvert** — `requiert` : câblé **côté GUI** (édition + validation
|
|
`valider_applications`) mais pas consommé au déploiement ; c'est un indice de dépendance
|
|
*applicative*. Les dépendances de **groupes** (celles qui pilotent l'ordre) vivent dans
|
|
`docs/dependances-groupes.yml` + `docs/couches-deploiement.yml`, et sont bel et bien
|
|
consommées par l'orchestrateur (`scripts/orchestrer.py` → `playbooks/site.yml`).
|
|
|
|
## 4. Discipline (pour ne plus re-déterrer)
|
|
|
|
Avant de concevoir ou d'ajouter un mécanisme :
|
|
1. lire **cette carte** + le doc du sujet ;
|
|
2. **arpenter le code** (`grep`) et **lire les README des rôles** concernés ;
|
|
3. **étendre / factoriser** l'existant plutôt qu'ajouter un chemin parallèle.
|
|
|
|
Un seul agent IA travaille dans le dépôt à la fois (Codex **ou** Claude) — cf. `AGENTS.md`.
|