# Architecture > **Pour qui :** quiconque doit comprendre comment Set-OPS est construit — avant de > l'installer, de l'exploiter ou de le modifier. Le détail de chaque mécanisme est dans > `docs/conception/` ; l'index complet dans [`README.md`](README.md). Set-OPS est un **moteur** Ansible et Python. Il lit un **plan** déclaratif, en dérive un inventaire et des politiques, et les applique à des VM Debian 13 clonées sur une grappe Proxmox. Le moteur est générique ; chaque écosystème réel vit dans **son propre dépôt**. ## Composants ```mermaid flowchart LR subgraph POSTE["Poste de l'exploitant (ou runner)"] MOT["Set-OPS
moteur : roles/, playbooks/, scripts/"] INST["instance/ → ../OPS-X
le locataire actif"] UND["underlay.yml → ../SITE-Y/underlay.yml
le site actif"] CLE["~/.config/setops-vault-*
une clé par voûte"] end subgraph SITE["Site d'hébergement"] PVE["Grappe Proxmox
SDN EVPN, pare-feu est-ouest"] OPN["Frontière OPNsense
nord/sud"] RUN["site-ops-01
runner du site"] FORGE["Forge du site
le génome"] VMS["VM des locataires
et du site"] end MOT -- "lit" --> INST & UND MOT -- "API Proxmox : clone, SDN, pare-feu" --> PVE MOT -- "API OPNsense : règles, routes" --> OPN MOT -- "SSH + Ansible" --> VMS POSTE -- "make publier / genome-pousser" --> FORGE RUN -- "tire" --> FORGE RUN -- "SSH + Ansible" --> VMS PVE --- VMS ``` | Composant | Ce que c'est | Où | |---|---|---| | Moteur | rôles, playbooks, scripts de dérivation, preuves | ce dépôt | | Locataire (tenant) | un écosystème : son plan, son inventaire généré, sa voûte | dépôt `OPS-`, monté par `instance/` | | Site | l'hébergeur : matériel, underlay, frontière, services prêtés aux locataires | dépôt `SITE-`, désigné par `underlay.yml` | | Voûtes | secrets chiffrés `ansible-vault`, une par dépôt, une clé par voûte | `group_vars/all/vault.yml` ; clés hors dépôt | | Runner du site | machine du site qui déploie à la place du poste | `site-ops-01`, compte `setops`, `/opt/setops` | | Forge du site | Forgejo qui porte le génome (moteur + plans) que les runners tirent | VM du site | ## Moteur, site, locataire ```mermaid flowchart TB M["Set-OPS (moteur)"] S["SITE-Y
underlay.yml · plan/ · opnsense.yml"] T1["OPS-A
plan/ · inventories/"] T2["OPS-B
plan/ · inventories/"] M -. "monte un site" .-> S M -. "monte un locataire" .-> T1 & T2 S -- "contrat du site → fiche-site.yml" --> T1 & T2 T1 & T2 -- "face réseau publiée" --> S ``` - **Deux classes d'écosystème, un tronc commun** : `scripts/contexte.py` est le seul endroit qui lit les indices (`instance/`, `underlay.yml`, `SETOPS_*`, dossiers frères) et rend un `Site` ou un `Locataire`. Conception : `docs/conception/conception-contextes.md`. - **Le site lit la face réseau des locataires, le locataire lit la fiche du site.** Le contrat (`make site-intrants`) dérive ce que le site expose : résolveur, cache de paquets, forge, adresses publiques, serveurs DNS publics. `make fiches-site-deposer` le dépose chez chaque locataire (`fiche-site.yml`) ; `make site-intrants-verifier` juge l'écart. - **Multi-instance par convention** : un dépôt par écosystème, découverts comme dossiers frères. Tout l'adressage dérive d'un seed `index` par écosystème ; P21 refuse une collision. Détail : `docs/conception/multi-instances.md`. - **Le site prête trois services** à un locataire jeune — paquets (`serveur_cache_site`), génome (`serveur_forge_site`), noms (`serveur_resolveur_site`) — jusqu'à ce qu'il ait les siens. Filiation et émancipation : `docs/conception/filiation-emancipation.md`. ## Du plan au déploiement ```mermaid flowchart LR PLAN["plan/
serveurs · applications · bases-donnees
domaines · nomenclature"] --> INS["make instancier
scripts/instancier.py
inventory_rules.py"] INS -- "diff sémantique" --> APP["make instancier-appliquer"] APP --> HOSTS["inventories/INVENTAIRE/hosts.yml
GÉNÉRÉ"] COUCHES["docs/couches-deploiement.yml
docs/dependances-groupes.yml"] --> ORCH["make site
scripts/orchestrer.py"] ORCH --> SITEYML["playbooks/site.yml
GÉNÉRÉ"] HOSTS & SITEYML --> ANS["make deployer · deployer-tout
ansible-playbook"] VAULT["vault.yml
ANSIBLE_VAULT_IDENTITY_LIST"] --> ANS ANS --> VM["VM : groupes → playbooks/groupes/GROUPE.yml → rôles"] ``` 1. **On édite le plan, jamais l'inventaire.** VMID, IP, VLAN, FQDN et appartenances aux groupes sont dérivés de la `fonction` d'une VM et des applications qui y tournent. 2. **L'inventaire est généré** et comparé à l'existant avant d'être écrit. 3. **L'ordre de déploiement est généré** depuis les couches et le graphe des dépendances entre groupes. 4. **Un groupe = un playbook homonyme** (`playbooks/groupes/.yml`) qui applique ses rôles. Le socle (`serveur_debian`) et le durcissement (`serveur_durci`) passent en premier. Les registres du plan : | Registre | Entité | Champs clés | |---|---|---| | `plan/serveurs.yml` | VM | `fonction`, `etat` (`planifie` → `actif`), placement | | `plan/applications.yml` | service (entité pivot) | `groupe` (rôle), `hote`, `port`, `liens`, `expose` | | `plan/bases-donnees.yml` | base | serveur, propriétaire, secret en voûte, `consommateur`, `portee` | | `plan/domaines.yml` | zone DNS | `autorite`, `edge`, `dnssec`, `enregistrements`, `resolution_interne` | | `plan/nomenclature.yml` | dérivation | `index` (seed), fonctions → VMID / VLAN / IP | Détail : `docs/conception/plan-et-generation.md`, `docs/conception/meta-classe.md`. ## Ce qu'un rôle déclare, ce que le moteur dérive Chaque rôle porte ses déclarations dans `roles//meta/`. Le moteur les agrège ; aucune liste n'est tenue à la main ailleurs. | Déclaration | Ce que le moteur en dérive | Doc | |---|---|---| | `meta/flux.yml` | nftables de l'hôte, pare-feu Proxmox, frontière OPNsense, registre des flux | `conception/flux-conception.md` | | `meta/liens.yml` | variables de connexion service → service | `conception/bindings-conception.md` | | `meta/empreinte.yml` | RAM, CPU, disque de la VM | `conception/dimensionnement-ressources.md` | | `meta/supervision.yml`, `meta/metriques.yml` | services Icinga, cibles Prometheus, tableaux | `conception/supervision-conception.md` | | `meta/authentification.yml` | portée et mécanisme d'authentification du rôle, gardés par P29 | `conception/authentification.md` | | `meta/acces.yml` | rôles d'accès projetés dans Keycloak | `exploitation/autorisation.md` | | `meta/integration.yml` | intégrations universelles (`client_*`) posées sur chaque VM | `conception/integrations-vm.md` | ## Réseau ```mermaid flowchart TB NET(("Internet")) --> OPN["Frontière OPNsense
nord/sud · seul équipement L3 de bordure"] OPN --> SDN["SDN EVPN sur les hyperviseurs
une zone (VRF) par écosystème · passerelles anycast"] SDN --> PFW["Pare-feu Proxmox
est-ouest, par VM"] PFW --> NFT["nftables de l'hôte"] NFT --> SVC["service"] FLUX["roles/*/meta/flux.yml"] -. "dérive" .-> OPN & PFW & NFT ``` - **Underlay** (`underlay.yml` du site) : la fabric physique — gestion, iSCSI, Ceph, ponts. `make underlay-plan` le confronte au réel. - **Overlay** : un écosystème = une zone EVPN et ses VNets, nommés depuis le seed (`make sdn-plan`). Détail : `docs/conception/sdn-evpn.md`. - **Quatre couches de filtrage, une seule source** : les flux déclarés par les rôles. `pair: externe` devient la politique de bordure. Détail : `docs/conception/frontiere-opnsense.md`, `docs/conception/registre-flux.md`. - **Deux sites reliés** par un tunnel WireGuard (`opnsense_liens_sites`) : réplication du DNS public, administration du site pair. DNS : `docs/conception/dns-interne.md`. ## Déclaré, dérivé, mesuré ```mermaid flowchart LR D["Déclaré
plan · meta/ · cartes"] --> V["Dérivé
devis : scripts/devis_*.py"] V --> P["make SUJET-plan
écart, aucune écriture"] P --> A["make SUJET-appliquer CONFIRMER=true
réconcilie"] R["Réel
API Proxmox · OPNsense · VM"] --> P D --> PR["make prouver / verifier
preuves statiques : lisent le dépôt"] ``` - **Les preuves** (`make verifier`, `make prouver`) lisent le dépôt seulement : cohérence du plan, des rôles, de la doc. Registre : `docs/audit/affirmations.md`. - **Les devis** lisent le système en marche et le comparent à ce que le plan dérive. Ils n'écrivent rien ; la cible `-appliquer` correspondante réconcilie, sous `CONFIRMER=true`. Détail : `docs/exploitation/devis-services.md`. ## Sécurité | Sujet | Mécanisme | Doc | |---|---|---| | Secrets | une voûte par dépôt, une clé par voûte, `scripts/voutes.py` construit `ANSIBLE_VAULT_IDENTITY_LIST` | `exploitation/sortir-les-cles-du-poste.md` | | Confiance machine | AC interne step-ca (ACME), certificats par `client_pki` | `conception/integrations-vm.md` | | Identité | OpenLDAP source unique, Keycloak fédéré, `oauth2-proxy` devant les services sans OIDC | `conception/identite-sso.md`, `conception/authentification.md` | | Habilitations | amorcées par `amorcage_acces`, jamais réconciliées | `exploitation/autorisation.md` | | Administration | tunnel WireGuard nominatif `admins`, rebond par la frontière | `exploitation/acces-administration.md` | | Transit | chaque flux déclare son `chiffrement` ; le registre en fait la synthèse | `conception/flux-conception.md`, `conception/registre-flux.md` | ## Invariants - Logiciel libre exclusivement ; ni Docker, ni Podman. - L'inventaire et `playbooks/site.yml` sont générés : ne pas les éditer. - Aucun nom d'inventaire codé en dur : `` est résolu par `ORDRE_INVENTAIRE`. - Le moteur s'exploite sans IA : la doc, `make` et le GUI suffisent. - L'autorité est `AGENTS.md`.