Set-OPS-Public/docs/architecture.md
Daniel Allaire aab1bcc83a doc : rangee par lecteur, trois portes d'entree (architecture, setup, usage) et un index
README reduit au role du projet et au demarrage rapide. docs/architecture.md (schemas
Mermaid), docs/setup.md, docs/usage.md (154 cibles en 13 familles), docs/README.md (index).
Documents ranges en conception/, exploitation/, hebergement/, projet/ ; 598 chemins suivis.
QUICKSTART.md, SOLUTION.md, architecture-set-ops.md et la carte fondus puis retires.

Preuves : P48 lit l'index et conception/mecanismes.md ; les gardes parcourent docs/ sous-
dossiers compris (_documents) ; usage.md suit le Makefile dans les deux sens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 01:01:54 -04:00

9.8 KiB

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.

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

flowchart LR
  subgraph POSTE["Poste de l'exploitant (ou runner)"]
    MOT["Set-OPS<br/>moteur : roles/, playbooks/, scripts/"]
    INST["instance/ → ../OPS-X<br/>le locataire actif"]
    UND["underlay.yml → ../SITE-Y/underlay.yml<br/>le site actif"]
    CLE["~/.config/setops-vault-*<br/>une clé par voûte"]
  end
  subgraph SITE["Site d'hébergement"]
    PVE["Grappe Proxmox<br/>SDN EVPN, pare-feu est-ouest"]
    OPN["Frontière OPNsense<br/>nord/sud"]
    RUN["site-ops-01<br/>runner du site"]
    FORGE["Forge du site<br/>le génome"]
    VMS["VM des locataires<br/>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-<nom>, monté par instance/
Site l'hébergeur : matériel, underlay, frontière, services prêtés aux locataires dépôt SITE-<nom>, 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

flowchart TB
  M["Set-OPS (moteur)"]
  S["SITE-Y<br/>underlay.yml · plan/ · opnsense.yml"]
  T1["OPS-A<br/>plan/ · inventories/"]
  T2["OPS-B<br/>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

flowchart LR
  PLAN["plan/<br/>serveurs · applications · bases-donnees<br/>domaines · nomenclature"] --> INS["make instancier<br/>scripts/instancier.py<br/>inventory_rules.py"]
  INS -- "diff sémantique" --> APP["make instancier-appliquer"]
  APP --> HOSTS["inventories/INVENTAIRE/hosts.yml<br/>GÉNÉRÉ"]
  COUCHES["docs/couches-deploiement.yml<br/>docs/dependances-groupes.yml"] --> ORCH["make site<br/>scripts/orchestrer.py"]
  ORCH --> SITEYML["playbooks/site.yml<br/>GÉNÉRÉ"]
  HOSTS & SITEYML --> ANS["make deployer · deployer-tout<br/>ansible-playbook"]
  VAULT["vault.yml<br/>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/<groupe>.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/<rôle>/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

flowchart TB
  NET(("Internet")) --> OPN["Frontière OPNsense<br/>nord/sud · seul équipement L3 de bordure"]
  OPN --> SDN["SDN EVPN sur les hyperviseurs<br/>une zone (VRF) par écosystème · passerelles anycast"]
  SDN --> PFW["Pare-feu Proxmox<br/>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é

flowchart LR
  D["Déclaré<br/>plan · meta/ · cartes"] --> V["Dérivé<br/>devis : scripts/devis_*.py"]
  V --> P["make SUJET-plan<br/>écart, aucune écriture"]
  P --> A["make SUJET-appliquer CONFIRMER=true<br/>réconcilie"]
  R["Réel<br/>API Proxmox · OPNsense · VM"] --> P
  D --> PR["make prouver / verifier<br/>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 : <inventaire> est résolu par ORDRE_INVENTAIRE.
  • Le moteur s'exploite sans IA : la doc, make et le GUI suffisent.
  • L'autorité est AGENTS.md.