reconstruction a froid : les noms derives naissent justes, et une cle vide tombe
Quatrieme reconstruction depuis zero, 32 min 14 s, 14/14. Elle avait un but precis : trois roles dependent desormais d une variable que le generateur pose. A froid, si instancier ne la posait pas, le defaut du role reprendrait la main en silence — juste pour forge et cloud, faux pour observatoire. Le certificat ne a froid porte observatoire et vigie, aucun ancien nom, et les deux retours SSO derivent juste sans reprise. Un seul echec, sans rapport : sur une machine des quatorze, get_url a rendu 0 octet SANS ERREUR — le cache a servi un 200 au corps vide. Le defaut s est lu deux cents lignes plus loin, dans un apt qui accusait la signature. La garde posee hier ne mordait pas : retirer une ressource vide ne vaut qu au passage suivant, quand le fichier existe deja. Les cinq roles qui telechargent une cle la mesurent maintenant dans la meme execution. P68 garde le motif. infra-mail-01 redeployee, make valider a 0 echec sur 14 machines. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
This commit is contained in:
parent
53e2f78148
commit
1b89e83b0c
12 changed files with 353 additions and 7 deletions
|
|
@ -188,7 +188,7 @@ Si `ansible-lint` n’est pas disponible, le signaler clairement. Ne pas invente
|
|||
## É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 67 preuves de `make prouver` : elles lisent le dépôt, sans le
|
||||
C'est aussi ce que font les 68 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.**
|
||||
|
||||
|
|
|
|||
52
CHANGELOG.md
52
CHANGELOG.md
|
|
@ -1,5 +1,57 @@
|
|||
# CHANGELOG — Set-OPS
|
||||
|
||||
## 2026-09-11 (1) — Reconstruction a froid : les noms derives naissent justes
|
||||
|
||||
Quatrieme reconstruction depuis zero de Chezlepro, demandee pour une raison precise : trois
|
||||
roles dependent desormais d'une variable que le GENERATEUR pose. En incrementiel ils avaient
|
||||
marche parce que la configuration existait deja. A froid, si `instancier` ne posait pas
|
||||
`<groupe>_hostname` sur un chemin quelconque, le defaut du role reprendrait la main **en
|
||||
silence** — et il tomberait juste pour `forge` et `cloud` (la convention coincide), faux pour
|
||||
`observatoire`.
|
||||
|
||||
14/14 VM rasees puis recreees 32 min 14 s
|
||||
un seul echec, et il n'a rien a voir avec les noms
|
||||
|
||||
Le certificat ne A FROID porte `observatoire` et `vigie`, et aucun ancien nom. Les deux
|
||||
retours SSO derivent juste, sans reprise :
|
||||
|
||||
observatoire redirect_uri=https%3A%2F%2Fobservatoire.chezlepro.internal%2Flogin%2Fgeneric_oauth
|
||||
vigie redirect_uri=https%3A%2F%2Fvigie.chezlepro.internal%2Foauth2%2Fcallback
|
||||
|
||||
En incrementiel, le SAN avait demande un second passage de `client_pki`. A froid, il naît
|
||||
juste du premier coup — la sequence penible n'existe qu'en incrementiel.
|
||||
|
||||
### Une reussite n'est pas un contenu
|
||||
|
||||
L'echec unique, sur `infra-mail-01` et sur elle seule : `get_url` a rendu **0 octet sans
|
||||
erreur**. Le cache a servi un 200 au corps vide. Le role a continue, satisfait.
|
||||
|
||||
Le defaut ne s'est pas lu la. Il s'est lu deux cents lignes plus loin, dans un `apt` qui
|
||||
accusait la SIGNATURE :
|
||||
|
||||
Missing key 35BAA0B33E9EB396F59CA838C0BA5CE6DC6315A3, which is needed to verify signature
|
||||
|
||||
Un message qui envoie chercher une cle revoquee chez le fournisseur, alors que le fichier
|
||||
local faisait zero octet. Meme famille que l'index tronque du cache la veille : l'octet
|
||||
manquant se denonce toujours ailleurs qu'ou il manque.
|
||||
|
||||
**Et la garde posee hier ne mordait pas.** « Retirer une ressource VIDE avant de la
|
||||
redemander » ne vaut qu'au passage SUIVANT : au premier, le fichier n'existe pas encore, il
|
||||
n'y a rien a retirer. Elle repare le second essai, elle ne protege pas le premier.
|
||||
|
||||
Les cinq roles qui telechargent une cle la MESURENT maintenant dans la meme execution — le
|
||||
`until` exige une taille non nulle (cinq tentatives), puis une assertion nomme la vraie
|
||||
cause. **P68** garde le motif. Les roles qui deposent une cle embarquee (`copy` depuis
|
||||
`files/`) n'entrent pas dans le compte : rien de reseau ne s'interpose.
|
||||
|
||||
`infra-mail-01` redeployee : la cle fait 1022 octets, comme les treize autres.
|
||||
|
||||
### Les trois bruits connus
|
||||
|
||||
Revenus comme prevu, et ce ne sont pas des regressions : `auditd` livre sans regles au
|
||||
gabarit, la course sur le verrou `dpkg`, l'index d'`apt.grafana.com` absent du cache
|
||||
hors-ligne.
|
||||
|
||||
## 2026-09-10 (18) — `vigie` pour Icinga, et la quatrieme liste tombe
|
||||
|
||||
Suite du renommage precedent, decide par l'exploitant : `vigie` est le nom de la console de
|
||||
|
|
|
|||
104
docs/audit/preuve-2026-09-11.md
Normal file
104
docs/audit/preuve-2026-09-11.md
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
# Preuve de conformite — Set-OPS — 2026-09-11
|
||||
|
||||
> Genere par `make prouver` (`scripts/prouver.py`). **Rejouable** : relancer
|
||||
> reproduit ce rapport. Chaque preuve rejoue l'outillage existant du depot ;
|
||||
> aucune validation n'est reimplementee ici. Voir le mode d'emploi :
|
||||
> [`docs/audit/README.md`](README.md), et le registre trace :
|
||||
> [`docs/audit/affirmations.md`](affirmations.md).
|
||||
|
||||
- **Instance** : `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance` — inventaire `/home/danallaire/Espace Chezlepro/DépôtsSurForge/Set-OPS-public/instance/inventories/principal/hosts.yml`
|
||||
- **Verdict** : ✅ CONFORME (67 OK · 0 echec · 1 saute)
|
||||
|
||||
## Preuves
|
||||
|
||||
| # | Preuve | Affirmations | Statut | Detail |
|
||||
|---|---|---|---|---|
|
||||
| P01 | Lint (ansible-lint) | AFF-006 | ✅ OK | [0m[0m |
|
||||
| P02 | Tests unitaires (inventaire, raser, ecritures du plan, rendu du GUI) | — | ✅ OK | OK |
|
||||
| P03 | Diff-vide du plan — TOUTES les instances | AFF-001, AFF-004, AFF-030, AFF-031, AFF-032 | ✅ OK | 4 instance(s) verifiee(s) — OPS-Chezlepro-lab, OPS-Chezlepro, OPS-Technolibre, OPS-Patient0 : plan et inventaire applique coincident. |
|
||||
| P04 | Groupes <-> playbooks homonymes | AFF-008 | ✅ OK | |
|
||||
| P05 | Dependances causales de groupes | AFF-009, AFF-084 | ✅ OK | |
|
||||
| P06 | Validateurs de registres (serveurs/apps/bases/domaines) | AFF-003 | ✅ OK | Registre des domaines valide. |
|
||||
| P07 | GUI (node --check) | AFF-033 | ✅ OK | JS du GUI : syntaxe valide (node --check), 1 nom(s) surveille(s) sans reference orpheline. |
|
||||
| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 41 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 39 rôles, 107 flux, schéma + matrice OK. |
|
||||
| P10 | Handlers <-> notify | AFF-034, AFF-035 | ✅ OK | Tout notify pointe vers un handler du meme role (49 roles). |
|
||||
| P11 | Syntaxe des playbooks (--syntax-check) | AFF-083 | ✅ OK | serveur_resolveur_site |
|
||||
| P12 | Existence des runbooks cites | AFF-010, AFF-011, AFF-012, AFF-083 | ✅ OK | 17/17 runbooks/registres cites presents. |
|
||||
| P13 | Invariants structurels/doctrinaux | AFF-015, AFF-022, AFF-037, AFF-038, AFF-062 | ✅ OK | LICENSE, socle dossier, pas de couches paralleles, SSH clef-only, nftables off : OK. |
|
||||
| P14 | Pas de chemin lab/ code en dur | AFF-097 | ✅ OK | Aucun chemin instance/inventories/lab/group_vars code en dur. |
|
||||
| P15 | Modele public socle valide | AFF-022, AFF-099 | ✅ OK | Modele public socle : domaines/serveurs/applications/bases valides. |
|
||||
| P16 | Inventaire Ansible complet (--list) | AFF-030 | ⚪ SAUTE | Voute chiffree sans ANSIBLE_VAULT_PASSWORD_FILE (prerequis AFF-026). |
|
||||
| P17 | Tous les modeles valident (registres + underlay) | AFF-022, AFF-099 | ✅ OK | Les 1 modele(s) decouvert(s) valident. |
|
||||
| P18 | Gabarit de voute complet | AFF-026 | ✅ OK | Gabarit de voute complet : 23 secret(s) exige(s), tous presents. (Voute reelle non lisible ici : verification sautee.) |
|
||||
| P19 | Le GUI couvre le schema du plan | AFF-002, AFF-095 | ✅ OK | GUI : les 29 champ(s) des plans reels sont editables (2 plan(s) inspecte(s)), registres toleres : nomenclature. |
|
||||
| P20 | Adressage 100% derive du seed (aucun stocke) | AFF-001, AFF-003 | ✅ OK | 2 nomenclature(s) : adressage 100% derive du seed index. |
|
||||
| P21 | Federation : aucun index en collision | AFF-102 | ✅ OK | Federation coherente : 3 instance(s) federee(s), aucun index en collision. |
|
||||
| P22 | Plan de recette a jour (genere du wiki) | AFF-002 | ✅ OK | Plan de recette à jour (22 sections). |
|
||||
| P23 | Underlay sans collision avec la plage tenant | AFF-103 | ✅ OK | Underlay conforme : 13 reseau(x), aucune collision avec la plage tenant. |
|
||||
| P24 | Frontiere nord/sud : acces d'administration declare | AFF-104 | ✅ OK | note : serveur_powerdns declare un port `derive` que le plan du site ne resout pas — aucune regle emise. |
|
||||
| P25 | Pare-feu Proxmox : est-ouest intra-tenant derive | AFF-107 | ✅ OK | CONFORME : pare-feu Proxmox, 3 tenant(s), 49 groupe(s), 87 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 14 hote(s) x 6 integration(s) universelle(s) : aucune lacune, aucune recopie (0 exemption(s) derivee(s) du service rendu). |
|
||||
| P27 | Propriete des intrants : hebergeur et tenant separes | AFF-109 | ✅ OK | 8 cle(s) de cluster chez l'hebergeur, aucune recopiee dans les group_vars du tenant. |
|
||||
| P28 | Pools Proxmox : un par tenant, sans collision | AFF-110 | ✅ OK | CONFORME : 3 pool(s) Proxmox, 34 VM placee(s), aucun nom ni VMID en collision. |
|
||||
| P29 | Authentification : chaque role declare sa position | AFF-111 | ✅ OK | 32 role(s) serveur declares (interne-sans-auth 2, ldap-direct 2, sans-auth-humaine 21, socle-identite 2, web-sso 5) ; 2 lacune(s) nommee(s) : serveur_loki, serv |
|
||||
| P30 | SDN EVPN : zones, VNets et sous-reseaux derives | AFF-112 | ✅ OK | CONFORME : SDN EVPN, 3 zone(s), 15 VNet(s), 15 sous-reseau(x), aucune collision. |
|
||||
| P31 | Documentation : tout ce que le depot FAIT est nomme | — | ✅ OK | 59 scripts expliques et atteignables, 116 cibles make documentees, 67 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 38 exigence(s) de role, toutes satisfaites (137 cle(s) declaree(s) par l'instance). |
|
||||
| P33 | Aucune collision de port entre roles co-localises | — | ✅ OK | CONFORME : 34 revendication(s) de port, aucune collision entre roles co-localises (36 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 44 document(s) declarent leur lecteur (37 genere(s) exempte(s)). |
|
||||
| P35 | Toute application exigeant une base en a une au plan | — | ✅ OK | 5 application(s) exigeant une base l'ont toutes (4 entree(s) au registre). |
|
||||
| P36 | Tout detenteur d'etat porte une sauvegarde | — | ✅ OK | 9 hote(s) de l'ecosysteme et 3 du site detiennent de l'etat, tous porteurs de `client_backup` (9 groupe(s) au catalogue). |
|
||||
| P37 | Le placement du tenant existe chez son hebergeur | — | ✅ OK | placement confronte a l'hebergeur monte (SITE-Chezlepro) : noeud, stockage, pont — tous offerts. |
|
||||
| P38 | Catalogue des services : la carte dit ce que le moteur fait | — | ✅ OK | Catalogue a jour : 40 role(s) serveur/client tous nommes, 41 groupe(s) cite(s) en table existent tous. |
|
||||
| P39 | Glossaire : tout mot employe est enseigne | — | ✅ OK | Glossaire complet : 81 terme(s) du jargon expliques, 15 lien(s) valides, 27 page(s) de wiki toutes atteignables. |
|
||||
| P40 | Parente : l'ecosysteme sait de quoi il descend | — | ✅ OK | Parente coherente : 4 depot(s), tous retrouves, tous porteurs d'un remote. |
|
||||
| P41 | Resolution d'instance : une seule, partagee | — | ✅ OK | Resolution unique : 55 script(s) passent par `inventory_rules`, 3 exemption(s) nommee(s). |
|
||||
| P42 | L'edge porte les noms qu'il publie | — | ✅ OK | 4 edge(s) emettent un certificat portant les noms publies (OPS-Chezlepro-lab/principal, OPS-Chezlepro/principal, OPS-Technolibre/principal, OPS-Patient0/product |
|
||||
| P43 | Frontiere : le devis voit les machines du site | — | ✅ OK | Devis de la frontiere : 7 machine(s) du plan retrouvees, 155 regle(s) du site. |
|
||||
| P44 | Integrations : le serveur avant ses clients | — | ✅ OK | 5 integration(s) appliquent leur serveur avant leurs clients. |
|
||||
| P45 | Pare-feu Proxmox : arme sur les VNet SDN, jamais ailleurs | — | ✅ OK | Le pare-feu Proxmox ne s'arme que sur un VNet SDN (4 cas evalues, dont un qui doit rendre VRAI). |
|
||||
| P46 | Plancher /etc/hosts : un seul role en decide | — | ✅ OK | Un seul maitre du plancher — roles/hosts_statiques/tasks/main.yml : manage_etc_hosts: false ; et le gabarit maitre est pose (roles/hosts_statiques/templates/hos |
|
||||
| P47 | Zones inverses : couvrir l'occupe, et rien de plus | — | ✅ OK | Les zones inverses couvrent l'occupe et rien de plus (5 cas evalues, dont un site a quatre zones et un tenant a une). |
|
||||
| P48 | La carte d'orientation designe ce qui existe, et compte juste | — | ✅ OK | La carte designe 89 chemin(s) qui existent, et ses 7 chiffres correspondent a la mesure. |
|
||||
| P49 | Registre des flux : la matrice d'audit est a jour | — | ✅ OK | Le registre des flux reproduit exactement ce que les `meta/flux.yml` declarent (125 lignes). |
|
||||
| P50 | Silences : un refus muet est declare, place en dernier, et motive | — | ✅ OK | 2 silence(s) declare(s), tous en sequence > 1 (la plus haute des 217 regles `pass`), tous non consignes et tous motives. |
|
||||
| P51 | Collections : toutes declarees, toutes epinglees | — | ✅ OK | 3 collection(s) et 2 bibliotheque(s) Python declarees et epinglees : ansible.posix==1.6.2, community.general==10.3.0, community.postgresql==3.10.2 |
|
||||
| P52 | Materialiser n'exige pas d'entrer dans le tenant | — | ✅ OK | `creer-vm` confirme par l'agent invite (API des hyperviseurs, deja utilisee pour creer), sans exiger d'entrer dans le tenant. |
|
||||
| P53 | L'interne refuse a voix haute, la bordure se tait | — | ✅ OK | L'interne parle, la bordure se tait — 15 ruleset(s) nftables refusent a voix haute ; pare-feu est-ouest en REJECT, source unique ; frontiere muette (actions : b |
|
||||
| P54 | L'insemination ne reclame aucun secret du tenant | — | ✅ OK | 2 couche(s) d'insemination (serveur_debian, serveur_ops), 9 role(s) applique(s), aucun secret de tenant reclame. |
|
||||
| P55 | La cle du SITE ne nait que sur le runner d'un tenant | — | ✅ OK | 14 hote(s) : la cle du SITE ne nait que sur 1 runner(s) de tenant, celle du tenant sur 14. |
|
||||
| P56 | Gabarit minimal, et rien de retire n'est perdu | — | ✅ OK | Gabarit minimal : 4 role(s), tous indispensables au premier demarrage ; 14 role(s) retire(s), tous repris par le socle ou le durcissement. |
|
||||
| P57 | Comptes en prose : les chiffres du depot sur lui-meme | — | ✅ OK | Les comptes ecrits en prose correspondent a la mesure (68 preuves, 67 roles, 41 groupes). |
|
||||
| P58 | Habilitations : chaque service dit a quel GROUPE, et par quoi | — | ✅ OK | 8 habilitation(s) declarees, toutes nommant un groupe, un mecanisme connu et une raison ; les `role-realm` sont projetees. |
|
||||
| P59 | Enumerations annoncees : le nombre correspond a ce qui suit | — | ✅ OK | 2 enumeration(s) annoncee(s) correspondent a ce qu'elles annoncent (formes non ambigues seulement). |
|
||||
| P60 | Wiki publie : la forge sert ce que le depot dit | AFF-002 | ✅ OK | Le wiki publie correspond au depot : `wiki/` n'a pas bouge depuis `53e2f78` (publie le 2026-09-11). |
|
||||
| P61 | Schema du plan : il decrit tout ce que les plans contiennent | AFF-033 | ✅ OK | Le schema decrit 47 champ(s) sur 6 registres ; il couvre tout ce que les plans reels contiennent, et la FORME de chaque champ (scalaire / objet / table) corresp |
|
||||
| P62 | Schema du plan : il decrit tout ce que le MOTEUR accepte | AFF-033 | ✅ OK | Les 4 validateurs n'acceptent aucun champ que le schema ignore (applications:8, bases_donnees:4, domaines_publics:5, serveurs:3 champ(s) lus par validateur). |
|
||||
| P63 | cloud-init nait avec la VM et ne lui survit pas | — | ✅ OK | cloud-init est au gabarit (la premiere seconde), absent du socle (pas de va-et-vient), et retire par le durcissement — avec la garde qui verifie que le reseau s |
|
||||
| P64 | Sondes de supervision : declarees ET deposees | — | ✅ OK | 24 sonde(s) declaree(s) ET deposee(s), chacune avec sa raison et son `ttl` : client_journal/journaux, client_metrique/metriques, client_pki/certificat, serveur_ |
|
||||
| P65 | Depots tiers : demandes au cache, jamais en HTTPS direct | — | ✅ OK | 4 depot(s) tiers relaye(s) par le cache, aucun role ne les vise en https:// ecrit en dur. |
|
||||
| P66 | Clients OIDC : chaque URI vise un nom que le plan expose | — | ✅ OK | 4 client(s) OIDC, toutes leurs URI visent un FQDN que le plan expose (6 exposition(s)). |
|
||||
| P67 | Nom public : le service porte celui du plan, pas celui du role | — | ✅ OK | 6 service(s) expose(s) portent le nom du plan (6 groupe(s) derive(s)). |
|
||||
| P68 | Cle de depot telechargee : mesuree avant d'etre utilisee | — | ✅ OK | 5 role(s) telechargent une cle de depot, tous la mesurent avant de s'en servir. |
|
||||
|
||||
## Couverture des affirmations ✅ du registre
|
||||
|
||||
Chaque affirmation ✅ automatisable est couverte par la preuve indiquee ci-dessus.
|
||||
Les ✅ **structurelles/doctrinales** non rejouables par une commande (ex. AFF-005
|
||||
`make`=aide, AFF-014 ciblage groupe, AFF-024 `instancier-appliquer`, AFF-051 autorite
|
||||
d'AGENTS.md, AFF-073/075 gardes `make`, AFF-090 wiki) ont ete verifiees a l'audit ;
|
||||
elles restent hors du harnais recurrent (rien d'executable a rejouer).
|
||||
|
||||
## Declarations d'intention (⚪ invérifiables localement — assumees)
|
||||
|
||||
Ces affirmations ne sont pas rejouables hors production ; elles sont **assumees**
|
||||
comme declarations d'intention, non comme preuves :
|
||||
|
||||
- **AFF-036** — « testables avec `--check` autant que possible » : verifiable seulement
|
||||
contre une flotte vivante.
|
||||
- **AFF-091** — contenu pedagogique du wiki : affirmations conceptuelles.
|
||||
- **AFF-096** — « GUI 100 % francais » : revue exhaustive des libelles rendus, non automatisee.
|
||||
- **AFF-007** — hote d'exemple `web-frontal-01` : placeholder assume.
|
||||
|
||||
_Rapport genere le 2026-09-11._
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
# Ecrit par `make wiki-publier`, lu par la preuve P60. Ne pas editer a la main.
|
||||
remote: ssh://git@forge.genese.internal/genome/set-ops-public.wiki.git
|
||||
source: d1ae41c
|
||||
date: 2026-09-10
|
||||
source: 53e2f78
|
||||
date: 2026-09-11
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ README de rôles). Cette page comble ces deux trous.
|
|||
| rôles | 67 | `roles/*/` |
|
||||
| README de rôles | 67 | `roles/*/README.md` — l'écart avec la ligne au-dessus est la dette |
|
||||
| documents | 40 | `docs/*.md` |
|
||||
| pièces d'audit | 41 | `docs/audit/*` |
|
||||
| pièces d'audit | 42 | `docs/audit/*` |
|
||||
| unités de wiki | 27 | `wiki/*.md` |
|
||||
| décisions en vigueur | 85 | lignes `\| **D-nn** \|` de `decisions-architecture.md` |
|
||||
| décisions renversées | 3 | lignes `\| **D-nn** —` du même document |
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ make placement-plan # chaque VM est-elle là où le plan la met
|
|||
|
||||
## Le trou qu'il comble
|
||||
|
||||
`scripts/prouver.py` porte 67 preuves (dont une conditionnelle, sautée sans la clé de la voûte). Elles sont toutes **statiques** : elles lisent le
|
||||
`scripts/prouver.py` porte 68 preuves (dont une conditionnelle, sautée sans la clé de la voûte). Elles sont toutes **statiques** : elles lisent le
|
||||
dépôt. Zéro appel réseau, zéro SSH, zéro `ansible`. Elles établissent que le dépôt est
|
||||
cohérent **avec lui-même** — que les handlers existent, que les intrants ont un
|
||||
propriétaire, que rien n'est codé en dur.
|
||||
|
|
|
|||
|
|
@ -88,6 +88,10 @@
|
|||
http_proxy: >-
|
||||
{{ ('http://' ~ artefacts_amorcage) if (artefacts_amorcage | default('') | string | length > 0) else '' }}
|
||||
https_proxy: ""
|
||||
register: client_journal_cle_depot
|
||||
retries: 5
|
||||
delay: 6
|
||||
until: client_journal_cle_depot is succeeded and (client_journal_cle_depot.size | default(0)) > 0
|
||||
|
||||
# `apt_repository` AJOUTE, IL NE REMPLACE PAS (mesure du 2026-09-10).
|
||||
#
|
||||
|
|
@ -100,6 +104,29 @@
|
|||
#
|
||||
# On retire donc explicitement la forme precedente. `state: absent` ne mord que si la
|
||||
# ligne existe : sur une machine neuve, cette tache ne fait rien.
|
||||
- name: Mesurer la cle de signature Grafana qui vient d'etre posee
|
||||
ansible.builtin.stat:
|
||||
path: "{{ client_journal_depot_cle_fichier }}"
|
||||
register: cle_grafana_posee
|
||||
|
||||
- name: Exiger une cle NON VIDE avant de s'en servir
|
||||
# UNE REUSSITE N'EST PAS UN CONTENU (mesure du 2026-09-11). Sur une reconstruction a
|
||||
# froid, `get_url` a rendu 0 octet sans erreur sur UNE machine des quatorze : le cache
|
||||
# a servi un 200 au corps vide. La garde « retirer une ressource VIDE » ne mord qu'au
|
||||
# passage SUIVANT, quand le fichier existe deja — au premier, elle n'a rien a retirer.
|
||||
#
|
||||
# Le defaut ne se lisait pas ici. Il se lisait deux cents lignes plus loin, dans un
|
||||
# `apt` qui accusait la SIGNATURE : « Missing key ..., which is needed to verify
|
||||
# signature ». Un message qui envoie chercher une cle revoquee chez le fournisseur,
|
||||
# alors que le fichier local fait zero octet.
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- (cle_grafana_posee.stat.size | default(0)) > 0
|
||||
fail_msg: >-
|
||||
La cle de signature Grafana ({{ client_journal_depot_cle_fichier }}) fait 0 octet.
|
||||
Le depot a repondu sans corps — cache vide, ou amont injoignable. apt echouera
|
||||
plus loin en accusant la signature : c'est ICI qu'il faut regarder.
|
||||
|
||||
- name: Retirer la forme HTTPS du depot (elle contournait le cache)
|
||||
ansible.builtin.apt_repository:
|
||||
repo: "{{ client_journal_depot_source | replace('http://', 'https://') }}"
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@
|
|||
register: client_pki_cle_depot_telechargee
|
||||
retries: 5
|
||||
delay: 6
|
||||
until: client_pki_cle_depot_telechargee is succeeded
|
||||
until: client_pki_cle_depot_telechargee is succeeded and (client_pki_cle_depot_telechargee.size | default(0)) > 0
|
||||
# LA CLE PASSE PAR LE CACHE, ELLE AUSSI (mesure du 2026-09-10).
|
||||
#
|
||||
# `get_url` ignore la configuration d'apt : le mandataire pose dans
|
||||
|
|
@ -139,6 +139,29 @@
|
|||
{{ ('http://' ~ artefacts_amorcage) if (artefacts_amorcage | default('') | string | length > 0) else '' }}
|
||||
https_proxy: ""
|
||||
|
||||
- name: Mesurer la cle de signature Smallstep qui vient d'etre posee
|
||||
ansible.builtin.stat:
|
||||
path: "{{ client_pki_depot_cle_fichier }}"
|
||||
register: cle_smallstep_posee
|
||||
|
||||
- name: Exiger une cle NON VIDE avant de s'en servir
|
||||
# UNE REUSSITE N'EST PAS UN CONTENU (mesure du 2026-09-11). Sur une reconstruction a
|
||||
# froid, `get_url` a rendu 0 octet sans erreur sur UNE machine des quatorze : le cache
|
||||
# a servi un 200 au corps vide. La garde « retirer une ressource VIDE » ne mord qu'au
|
||||
# passage SUIVANT, quand le fichier existe deja — au premier, elle n'a rien a retirer.
|
||||
#
|
||||
# Le defaut ne se lisait pas ici. Il se lisait deux cents lignes plus loin, dans un
|
||||
# `apt` qui accusait la SIGNATURE : « Missing key ..., which is needed to verify
|
||||
# signature ». Un message qui envoie chercher une cle revoquee chez le fournisseur,
|
||||
# alors que le fichier local fait zero octet.
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- (cle_smallstep_posee.stat.size | default(0)) > 0
|
||||
fail_msg: >-
|
||||
La cle de signature Smallstep ({{ client_pki_depot_cle_fichier }}) fait 0 octet.
|
||||
Le depot a repondu sans corps — cache vide, ou amont injoignable. apt echouera
|
||||
plus loin en accusant la signature : c'est ICI qu'il faut regarder.
|
||||
|
||||
- name: Ajouter le depot apt Smallstep
|
||||
ansible.builtin.template:
|
||||
src: smallstep.sources.j2
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@
|
|||
register: serveur_collabora_cle
|
||||
retries: 5
|
||||
delay: 6
|
||||
until: serveur_collabora_cle is succeeded
|
||||
until: serveur_collabora_cle is succeeded and (serveur_collabora_cle.size | default(0)) > 0
|
||||
# LA CLE PASSE PAR LE CACHE, ELLE AUSSI (mesure du 2026-09-10).
|
||||
#
|
||||
# `get_url` ignore la configuration d'apt : le mandataire pose dans
|
||||
|
|
@ -108,6 +108,29 @@
|
|||
https_proxy: ""
|
||||
|
||||
# DEPOT PLAT : `Suites: ./`, aucun composant. Une source classique ne le trouverait pas.
|
||||
- name: Mesurer la cle de signature Collabora qui vient d'etre posee
|
||||
ansible.builtin.stat:
|
||||
path: "{{ serveur_collabora_depot_cle_fichier }}"
|
||||
register: cle_collabora_posee
|
||||
|
||||
- name: Exiger une cle NON VIDE avant de s'en servir
|
||||
# UNE REUSSITE N'EST PAS UN CONTENU (mesure du 2026-09-11). Sur une reconstruction a
|
||||
# froid, `get_url` a rendu 0 octet sans erreur sur UNE machine des quatorze : le cache
|
||||
# a servi un 200 au corps vide. La garde « retirer une ressource VIDE » ne mord qu'au
|
||||
# passage SUIVANT, quand le fichier existe deja — au premier, elle n'a rien a retirer.
|
||||
#
|
||||
# Le defaut ne se lisait pas ici. Il se lisait deux cents lignes plus loin, dans un
|
||||
# `apt` qui accusait la SIGNATURE : « Missing key ..., which is needed to verify
|
||||
# signature ». Un message qui envoie chercher une cle revoquee chez le fournisseur,
|
||||
# alors que le fichier local fait zero octet.
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- (cle_collabora_posee.stat.size | default(0)) > 0
|
||||
fail_msg: >-
|
||||
La cle de signature Collabora ({{ serveur_collabora_depot_cle_fichier }}) fait 0 octet.
|
||||
Le depot a repondu sans corps — cache vide, ou amont injoignable. apt echouera
|
||||
plus loin en accusant la signature : c'est ICI qu'il faut regarder.
|
||||
|
||||
- name: Ajouter le depot apt Collabora
|
||||
ansible.builtin.template:
|
||||
src: collabora.sources.j2
|
||||
|
|
|
|||
|
|
@ -120,6 +120,10 @@
|
|||
http_proxy: >-
|
||||
{{ ('http://' ~ artefacts_amorcage) if (artefacts_amorcage | default('') | string | length > 0) else '' }}
|
||||
https_proxy: ""
|
||||
register: serveur_grafana_cle_depot
|
||||
retries: 5
|
||||
delay: 6
|
||||
until: serveur_grafana_cle_depot is succeeded and (serveur_grafana_cle_depot.size | default(0)) > 0
|
||||
|
||||
# `apt_repository` AJOUTE, IL NE REMPLACE PAS (mesure du 2026-09-10).
|
||||
#
|
||||
|
|
@ -132,6 +136,29 @@
|
|||
#
|
||||
# On retire donc explicitement la forme precedente. `state: absent` ne mord que si la
|
||||
# ligne existe : sur une machine neuve, cette tache ne fait rien.
|
||||
- name: Mesurer la cle de signature Grafana qui vient d'etre posee
|
||||
ansible.builtin.stat:
|
||||
path: "{{ serveur_grafana_depot_cle_fichier }}"
|
||||
register: cle_grafana_posee
|
||||
|
||||
- name: Exiger une cle NON VIDE avant de s'en servir
|
||||
# UNE REUSSITE N'EST PAS UN CONTENU (mesure du 2026-09-11). Sur une reconstruction a
|
||||
# froid, `get_url` a rendu 0 octet sans erreur sur UNE machine des quatorze : le cache
|
||||
# a servi un 200 au corps vide. La garde « retirer une ressource VIDE » ne mord qu'au
|
||||
# passage SUIVANT, quand le fichier existe deja — au premier, elle n'a rien a retirer.
|
||||
#
|
||||
# Le defaut ne se lisait pas ici. Il se lisait deux cents lignes plus loin, dans un
|
||||
# `apt` qui accusait la SIGNATURE : « Missing key ..., which is needed to verify
|
||||
# signature ». Un message qui envoie chercher une cle revoquee chez le fournisseur,
|
||||
# alors que le fichier local fait zero octet.
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- (cle_grafana_posee.stat.size | default(0)) > 0
|
||||
fail_msg: >-
|
||||
La cle de signature Grafana ({{ serveur_grafana_depot_cle_fichier }}) fait 0 octet.
|
||||
Le depot a repondu sans corps — cache vide, ou amont injoignable. apt echouera
|
||||
plus loin en accusant la signature : c'est ICI qu'il faut regarder.
|
||||
|
||||
- name: Retirer la forme HTTPS du depot (elle contournait le cache)
|
||||
ansible.builtin.apt_repository:
|
||||
repo: "{{ serveur_grafana_depot_source | replace('http://', 'https://') }}"
|
||||
|
|
|
|||
|
|
@ -88,6 +88,10 @@
|
|||
http_proxy: >-
|
||||
{{ ('http://' ~ artefacts_amorcage) if (artefacts_amorcage | default('') | string | length > 0) else '' }}
|
||||
https_proxy: ""
|
||||
register: serveur_loki_cle_depot
|
||||
retries: 5
|
||||
delay: 6
|
||||
until: serveur_loki_cle_depot is succeeded and (serveur_loki_cle_depot.size | default(0)) > 0
|
||||
|
||||
# `apt_repository` AJOUTE, IL NE REMPLACE PAS (mesure du 2026-09-10).
|
||||
#
|
||||
|
|
@ -100,6 +104,29 @@
|
|||
#
|
||||
# On retire donc explicitement la forme precedente. `state: absent` ne mord que si la
|
||||
# ligne existe : sur une machine neuve, cette tache ne fait rien.
|
||||
- name: Mesurer la cle de signature Grafana qui vient d'etre posee
|
||||
ansible.builtin.stat:
|
||||
path: "{{ serveur_loki_depot_cle_fichier }}"
|
||||
register: cle_grafana_posee
|
||||
|
||||
- name: Exiger une cle NON VIDE avant de s'en servir
|
||||
# UNE REUSSITE N'EST PAS UN CONTENU (mesure du 2026-09-11). Sur une reconstruction a
|
||||
# froid, `get_url` a rendu 0 octet sans erreur sur UNE machine des quatorze : le cache
|
||||
# a servi un 200 au corps vide. La garde « retirer une ressource VIDE » ne mord qu'au
|
||||
# passage SUIVANT, quand le fichier existe deja — au premier, elle n'a rien a retirer.
|
||||
#
|
||||
# Le defaut ne se lisait pas ici. Il se lisait deux cents lignes plus loin, dans un
|
||||
# `apt` qui accusait la SIGNATURE : « Missing key ..., which is needed to verify
|
||||
# signature ». Un message qui envoie chercher une cle revoquee chez le fournisseur,
|
||||
# alors que le fichier local fait zero octet.
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- (cle_grafana_posee.stat.size | default(0)) > 0
|
||||
fail_msg: >-
|
||||
La cle de signature Grafana ({{ serveur_loki_depot_cle_fichier }}) fait 0 octet.
|
||||
Le depot a repondu sans corps — cache vide, ou amont injoignable. apt echouera
|
||||
plus loin en accusant la signature : c'est ICI qu'il faut regarder.
|
||||
|
||||
- name: Retirer la forme HTTPS du depot (elle contournait le cache)
|
||||
ansible.builtin.apt_repository:
|
||||
repo: "{{ serveur_loki_depot_source | replace('http://', 'https://') }}"
|
||||
|
|
|
|||
|
|
@ -3217,6 +3217,67 @@ def preuve_nom_public_vient_du_plan() -> tuple[bool, str]:
|
|||
f"({len(attendu)} groupe(s) derive(s)).")
|
||||
|
||||
|
||||
def preuve_cle_telechargee_non_vide() -> tuple[bool, str]:
|
||||
"""Une cle de depot TELECHARGEE est mesuree avant d'etre utilisee.
|
||||
|
||||
UNE REUSSITE N'EST PAS UN CONTENU (mesure le 2026-09-11). Sur une reconstruction a
|
||||
froid, `get_url` a rendu 0 octet SANS ERREUR sur une machine des quatorze : le cache a
|
||||
servi un 200 au corps vide. Le role a continue, satisfait.
|
||||
|
||||
Le defaut ne s'est pas lu la. Il s'est lu deux cents lignes plus loin, dans un `apt` qui
|
||||
accusait la SIGNATURE : « Missing key 35BAA0B3..., which is needed to verify signature ».
|
||||
Un message qui envoie chercher une cle revoquee chez le fournisseur, alors que le
|
||||
fichier local faisait zero octet. C'est la meme famille que l'index tronque du cache la
|
||||
veille : l'octet manquant se denonce toujours ailleurs qu'ou il manque.
|
||||
|
||||
La garde posee d'abord — « retirer une ressource VIDE avant de la redemander » — ne
|
||||
mordait qu'au passage SUIVANT : au premier, le fichier n'existe pas encore, il n'y a
|
||||
rien a retirer. Elle repare le second essai, elle ne protege pas le premier.
|
||||
|
||||
Cette preuve exige donc que tout role qui TELECHARGE une cle la MESURE dans la meme
|
||||
execution. Les roles qui deposent une cle embarquee (`copy` depuis `files/`) n'entrent
|
||||
pas dans le compte : rien de reseau ne s'interpose, l'octet ne peut pas manquer.
|
||||
"""
|
||||
manquants: list[str] = []
|
||||
verifies = 0
|
||||
for chemin in sorted((RACINE / "roles").glob("*/tasks/main.yml")):
|
||||
role = chemin.parent.parent.name
|
||||
try:
|
||||
taches = yaml.safe_load(chemin.read_text(encoding="utf-8")) or []
|
||||
except Exception:
|
||||
continue
|
||||
if not isinstance(taches, list):
|
||||
continue
|
||||
|
||||
def _mod(t: dict, nom: str) -> dict | None:
|
||||
for cle in (nom, f"ansible.builtin.{nom}"):
|
||||
if isinstance(t.get(cle), dict):
|
||||
return t[cle]
|
||||
return None
|
||||
|
||||
telecharge = [t for t in taches if isinstance(t, dict)
|
||||
and (_mod(t, "get_url") or {}).get("dest", "")
|
||||
and "depot_cle_fichier" in str((_mod(t, "get_url") or {}).get("dest"))]
|
||||
if not telecharge:
|
||||
continue
|
||||
verifies += 1
|
||||
mesure = any(
|
||||
isinstance(t, dict) and _mod(t, "assert")
|
||||
and any("stat.size" in str(c) and ">" in str(c)
|
||||
for c in (_mod(t, "assert") or {}).get("that") or [])
|
||||
for t in taches)
|
||||
if not mesure:
|
||||
manquants.append(f"{role} telecharge une cle de depot sans mesurer sa taille")
|
||||
if not verifies:
|
||||
return False, ("Aucun role ne telecharge de cle de depot : la preuve a perdu son "
|
||||
"objet, ou le motif a change.")
|
||||
if manquants:
|
||||
return False, ("Cle de depot telechargee sans mesure — un corps vide passera, et "
|
||||
"apt accusera la signature :\n - " + "\n - ".join(manquants))
|
||||
return True, (f"{verifies} role(s) telechargent une cle de depot, tous la mesurent "
|
||||
f"avant de s'en servir.")
|
||||
|
||||
|
||||
PREUVES: list[dict] = [
|
||||
{"id": "P01", "titre": "Lint (ansible-lint)", "refs": ["AFF-006"],
|
||||
"cmds": [["ansible-lint", "-q"]]},
|
||||
|
|
@ -3371,6 +3432,8 @@ PREUVES: list[dict] = [
|
|||
"refs": [], "func": preuve_clients_oidc_vises_sur_une_exposition},
|
||||
{"id": "P67", "titre": "Nom public : le service porte celui du plan, pas celui du role",
|
||||
"refs": [], "func": preuve_nom_public_vient_du_plan},
|
||||
{"id": "P68", "titre": "Cle de depot telechargee : mesuree avant d'etre utilisee",
|
||||
"refs": [], "func": preuve_cle_telechargee_non_vide},
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue