Deuxieme application du patron devis/applicateur aux services. Trouve a la premiere execution : sur infra-pki-01 — l'autorite elle-meme — le certificat etait expire depuis plus de 8 h et le renouvellement echouait toutes les 14 minutes sur « 'step ca renew' requires the '--ca-url' flag ». Rien ne le signalait. Cause : sur l'hote de l'AC, /etc/step est le STEPPATH du SERVEUR, pas un amorcage client — pas de defaults.json, et l'unite de renouvellement en dependait. La lecon etait deja ecrite dans le commentaire de la tache d'emission (« l'autorite ne bootstrape pas »), jamais reportee sur l'unite. Le role ne pouvait pas non plus se soigner : la re-emission ne regardait que la FORME (cert absent ou SAN manquant), jamais la validite. client_pki verifie desormais l'echeance (client_pki_marge_renouvellement). Ce qu'il a fallu desapprendre : les certificats vivent 24 h et se renouvellent toutes les ~14 min ; « empreinte servie != empreinte disque » est l'etat NORMAL. Comparer les empreintes aurait donne un verificateur qui crie en permanence. Le signal est l'echeance de ce qui est SERVI, plus l'absence de client_pki_reload_services. Le devis a d'abord menti, du defaut meme qu'il traque : include_vars au niveau du play prime sur les group_vars. Et le premier correctif a PARU marcher — set_fact accepte un dictionnaire entier en argument libre sans erreur et n'en fait rien. Il faut reimposer cle par cle. Les deux devis sont corriges et le piege est consigne dans docs/devis-services.md avant d'ecrire le prochain. Verifie dans les deux sens : CONFORME sur 14 hotes ; sur un releve ou l'on rejoue une copie perimee en memoire, 2 ecarts et code de sortie 1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
89 lines
4.7 KiB
Markdown
89 lines
4.7 KiB
Markdown
# Les devis de service : ce qui tourne correspond-il à ce qui est déclaré ?
|
|
|
|
> **Instrument ajouté le 2026-08-08**, après une série de défauts qu'aucun test n'avait
|
|
> signalés. Lecture seule — il ne modifie rien.
|
|
|
|
```
|
|
make identite-plan # realm, fédération, mappeurs, politique, comptes
|
|
make certificats-plan # certificats sur disque contre certificats réellement servis
|
|
```
|
|
|
|
## Le trou qu'il comble
|
|
|
|
`scripts/prouver.py` porte 30 preuves. 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.
|
|
|
|
Aucune ne demande au système déployé s'il ressemble à ce que le dépôt annonce.
|
|
|
|
Les quatre défauts du 2026-08-08 vivaient tous dans cet angle :
|
|
|
|
| Défaut | Le dépôt disait | La machine portait |
|
|
|---|---|---|
|
|
| politique de mot de passe | `length(12)…` | *aucune* — `abcd` accepté |
|
|
| entrée `ppolicy` figée | `pwdMustChange: TRUE` | `FALSE` |
|
|
| fédération en écriture | comptes écrits dans LDAP | `syncRegistrations` absent |
|
|
| prénom | `givenName` | `cn` (le nom complet) |
|
|
|
|
Chacun a été trouvé en relisant après avoir écrit. Aucun n'a été signalé.
|
|
|
|
## Ce qu'il vérifie
|
|
|
|
Le playbook `playbooks/maintenance/devis-identite.yml` **relève** deux dictionnaires — le
|
|
déclaré et le réel — et les dépose en JSON. `scripts/devis_identite.py` **compare**. La
|
|
séparation est délibérée : la comparaison en Jinja est illisible, et le dépôt a déjà cette
|
|
forme pour les devis réseau (Python raisonne, Ansible relève).
|
|
|
|
- **realm** — politique de mot de passe, anti-force-brute, « mot de passe oublié », relais SMTP
|
|
- **fédération** — mode d'écriture et les six clés de durcissement
|
|
- **mappeurs** — chaque attribut LDAP projeté
|
|
- **annuaire** — l'entrée `pwdPolicy`, attribut par attribut
|
|
- **comptes** — ceux qui n'ont pas d'adresse, pour qui « mot de passe oublié » ne mène nulle part
|
|
|
|
Le déclaré n'est jamais recopié ici : le playbook charge les défauts du rôle et appelle les
|
|
résolveurs. **Un devis qui redéclare ce qu'il vérifie ne vérifie rien.**
|
|
|
|
## Le devis des certificats
|
|
|
|
Il traque un défaut connu et récurrent : un certificat renouvelé **sur disque** mais
|
|
toujours servi **périmé** depuis la mémoire du service, tant que nginx / postfix / dovecot
|
|
/ slapd / step-ca n'ont pas été rechargés.
|
|
|
|
**Ce qu'il a fallu désapprendre pour l'écrire.** Les certificats step-ca vivent 24 h et le
|
|
minuteur les renouvelle toutes les ~14 minutes. Une empreinte servie *différente* de celle
|
|
sur disque est donc l'état **normal** : comparer les empreintes aurait produit un
|
|
vérificateur qui crie en permanence, et qu'on aurait appris à ignorer. Le signal utile est
|
|
l'**échéance de ce qui est réellement servi**, plus l'absence de `client_pki_reload_services`
|
|
— car alors rien ne rafraîchira jamais la copie en mémoire.
|
|
|
|
Il a trouvé quelque chose à sa première exécution : sur `infra-pki-01`, l'autorité
|
|
elle-même, le certificat était **expiré depuis plus de huit heures** et le renouvellement
|
|
échouait toutes les quatorze minutes sur `'step ca renew' requires the '--ca-url' flag`.
|
|
Rien ne le signalait.
|
|
|
|
## Un piège de construction, à connaître avant d'écrire le prochain
|
|
|
|
`include_vars` au niveau du *play* **prime sur les `group_vars`**. Charger les défauts d'un
|
|
rôle tels quels fait donc mentir le devis : il compare au défaut du dépôt au lieu de la
|
|
valeur réellement déclarée par l'inventaire. Le premier jet rapportait
|
|
`client_pki_reload_services: []` sur les quatorze hôtes alors que quatre groupes le
|
|
déclarent — **le devis portait exactement le défaut qu'il est censé traquer.**
|
|
|
|
La forme correcte, reprise dans les deux devis :
|
|
|
|
1. retenir ce que l'inventaire déclare, **avant** tout chargement de défauts ;
|
|
2. charger les défauts du rôle (`include_vars`, sans `name:` — les défauts se citent entre
|
|
eux, et ces références ne se résolvent plus une fois enfermées dans un dictionnaire) ;
|
|
3. réimposer les valeurs de l'inventaire **clé par clé**, en boucle. `set_fact` n'accepte
|
|
pas un dictionnaire entier passé en argument libre : il le prend sans erreur et n'en
|
|
fait rien — c'est ainsi que le premier correctif a paru fonctionner tout en laissant le
|
|
devis mentir.
|
|
|
|
## Ce qu'ils ne font pas
|
|
|
|
Ils ne corrigent pas — c'est `make deployer` qui réconcilie. Ils répondent à l'autre
|
|
question, et sortent en code 1 s'il y a un écart.
|
|
|
|
Ils couvrent l'identité et les certificats. Le courriel, la base de données et les
|
|
expositions web attendent le même traitement ; le patron est là pour être repris.
|