2026-08-07 13:27:00 -04:00
# Accès et habilitations : Set-OPS amorce, le sysadmin gouverne
2026-08-07 13:05:48 -04:00
preuve : P34 — chaque document declare son lecteur (D-74)
La refonte de ce matin posait une convention. Une convention qu'on n'outille
pas tient tant que quelqu'un y pense : c'est le raisonnement de D-70, applique
au corpus documentaire.
Etat de depart mesure : 2 documents sur 34 declaraient leur lecteur. Les 32
autres disaient leur SUJET — ce qui avait enfoui le runbook de reprise le plus
utile du depot au §6 de autorisation.md.
Les 38 le declarent desormais, lecteur determine document par document et non
colle au gabarit : l'exploitant (devis, migration de tenant, cycle de vie,
gabarit d'or), le mainteneur (conceptions, registres, carte), le lecteur
externe (ecosysteme-chezlepro), l'agent IA (MISE-A-JOUR-CODEX-CLAUDE).
Deux exemptions DERIVEES, pas listees — un chemin en dur aurait vieilli a la
premiere page ajoutee : un document qui s'annonce genere, et un fragment sans
titre. Les 13 exemptes verifies un par un ; aucun document ecrit a la main
n'est exempte par accident. La preuve ne lit que l'EN-TETE, ce qui empeche
frontiere-opnsense.md et plan-et-generation.md — qui parlent de generation
dans leur corps — d'etre exemptes a tort.
Eprouvee dans les deux sens. Elle a echoue seule des sa premiere execution en
nommant deux documents que mon inventaire avait manques (docs/audit/). Puis
test negatif delibere : declaration retiree de meta-classe.md -> ECHEC la
nommant ; restauree -> OK.
Ce qu'elle ne teste pas : que le lecteur declare soit le BON. Ca se juge en
revue ; elle garantit qu'on a du y penser.
P01–P34. Comptes perimes corriges au passage (AGENTS.md et devis-services.md
annoncaient encore 30 preuves).
Verifie : prouver.py 0 (34 OK), plan-recette inchange.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 07:53:04 -04:00
> **Pour qui :** l'**exploitant**, le jour de la reprise — le §6 est la partie utile. La doctrine qui précède (§1-5) s'adresse au mainteneur.
2026-08-07 13:05:48 -04:00
> **Directive d'architecture du 2026-08-07.** L'authentification dit *qui tu es*
2026-08-07 13:27:00 -04:00
> (`authentification.md`). Ce document dit *ce que tu peux faire* — et surtout **qui en
> décide**. La réponse n'est pas « le dépôt ».
2026-08-07 13:05:48 -04:00
## 1. Le constat, mesuré
`ou=groups` est créé par `serveur_openldap` depuis le début. Au 2026-08-07, c'est un
**conteneur vide qu'aucun service ne lit** : pas un `memberOf` , pas un filtre de groupe
dans les 29 rôles du catalogue.
2026-08-07 13:27:00 -04:00
Toute personne qui s'authentifie obtient donc le défaut du service — tout, ou rien. Et
aucun humain n'a de compte : `ou=people` est vide aussi. L'écosystème est déployé et
personne ne peut y entrer autrement que par les comptes de secours en voûte.
2026-08-07 13:05:48 -04:00
documentation : la tournee des 74 documents, parce qu un balayage ne lit pas
La revision a commence par un balayage par motifs — chemins morts, cibles make
absentes, comptes derives. Il a trouve une trentaine d ecarts et rate presque
tout le reste : un motif ne voit que ce qui s exprime en motif.
make hote-planifier en est l exemple. La cible EXISTE, donc le controle passait
au vert. C est une cible depreciee qui refuse et sort en 2, recommandee par
AGENTS.md, et qui contredit la REGLE D OR du meme fichier trois ecrans plus
haut. Il fallait lire pour la voir.
74 documents lus un par un. 66 corriges, 8 exacts.
CE QUI ETAIT FRANCHEMENT FAUX
AGENTS.md, la source d autorite, annoncait la flotte pas encore executee contre
des VM reelles. Elle a ete rasee et remontee depuis zero trois fois.
ecosysteme-chezlepro.md, le document montre a un client, portait la meme
phrase : il se sous-vendait gravement.
courriel-conception.md s ouvrait sur aucun role n est encore ecrit, au-dessus de
son propre paragraphe 1 qui les nomme. autorisation.md se terminait sur rien n
est construit alors qu il rapporte des mesures datees du role en fonctionnement.
hebergeur-exploitation.md disait rien n est fait d un depot qui existe.
filiation-emancipation.md se contredisait a deux ecrans de distance.
DES MODELES DECRITS D APRES UN MONDE ANTERIEUR
Le resolveur : cinq documents decrivaient un Unbound par VM en opt-in, trois le
donnaient en exemple d integration FACULTATIVE — il est universel depuis le
2026-08-24. L adressage de nomenclature-vm.md : reseau unique, VLAN 11-15, VMID
a cinq chiffres. Le nommage SDN de sdn-evpn.md contre le code : c est le wiki
qui avait raison.
CE QUI CASSE AU PREMIER ESSAI
Le nom du gabarit dore etait faux a quatre endroits, dont la procedure qui le
FABRIQUE et le critere R2 de l epreuve d operateur independant.
preparer-un-site-hebergeur.md avertissait qu une VM faite a la main serait
detruite : raser derive du plan, il ne la detruira jamais — le risque est l
inverse. Un mot de passe d essai en clair dans un depot public.
DEUX PREUVES ETENDUES, ET UNE QUI SE TROMPAIT ELLE-MEME
P57 couvre les groupes : elle a signale aussitot 29 groupes annonces au-dessus d
un tableau qui en cite 40. P29 confronte le tableau de authentification.md aux
declarations reelles : 12 annonces, 21 reels.
Et P57 imposait un chiffre faux — 56 preuves alors que le depot en porte 57, la
conditionnelle vivant hors de tout comptage. Un garde-fou qui fait respecter une
erreur ajoute l assurance a l erreur.
CE QUI RESTE, ET QU AUCUNE PREUVE NE TIENT
Deux comptes trouves a la main. Et une lacune reelle : rien ne garde les
meta/acces.yml — ni qu un service web-sso en porte un, ni que le groupe qu il
nomme existe. P29 tient les positions d authentification, personne ne tient les
habilitations.
make prouver : CONFORME, 56 OK, 0 echec, 1 saute. 0 lien mort.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-06 16:18:23 -04:00
> **Révisé le 2026-09-05 — le constat ci-dessus est daté, et il a bougé.** `ou=groups`
> n'est plus un conteneur que personne ne lit : `amorcage_acces`, `serveur_keycloak` et
> `serveur_icingaweb2` s'en servent. La mesure du 2026-08-07 est conservée telle quelle
> parce qu'elle explique *pourquoi* ce document existe — mais elle ne décrit plus l'état
> du dépôt. Le §2 et la suite, eux, restent la doctrine en vigueur.
2026-08-07 13:27:00 -04:00
## 2. Deux régimes, et la frontière entre eux
2026-08-07 13:05:48 -04:00
2026-08-07 13:27:00 -04:00
C'est la décision principale, et elle **contredit délibérément la doctrine du dépôt** .
2026-08-07 13:05:48 -04:00
2026-08-07 13:27:00 -04:00
Partout ailleurs, un écart entre le déclaré et le réel est un défaut à corriger : les devis
réconcilient, les applicateurs retirent ce qui n'est plus demandé. **Ici, l'écart est
légitime** — c'est le sysadmin qui fait son travail.
2026-08-07 13:05:48 -04:00
2026-08-07 13:27:00 -04:00
| | Qui décide | Régime |
|---|---|---|
| **Infrastructure** — quel groupe accorde quoi dans un service | Set-OPS | réconcilié à chaque déploiement, comme le reste |
| **Habilitations** — qui appartient à quel groupe | **une personne** | **amorcé une fois, jamais réconcilié** |
Le mapping « le groupe `sysadmin` vaut `Admin` dans Grafana » est de la configuration
d'infrastructure : il se déclare, se dérive et se corrige. Savoir **qui** est dans
`sysadmin` ne l'est pas — c'est une décision d'exploitation, prise par un humain, souvent
en urgence, et le dépôt n'a pas à la défaire au prochain `make deployer` .
Un dépôt qui réconcilierait les appartenances effacerait le compte créé la veille pour un
nouvel employé. C'est exactement le genre de « correction » qu'il ne faut pas faire.
## 3. L'amorçage est un one-shot
Set-OPS crée **un** accès : celui du sysadmin, pour qu'il puisse prendre la main. Rien de
plus.
**Idempotence par non-intervention.** La condition n'est pas la conformité, c'est
l'**existence** : si le compte est là, on n'y touche pas — ni son mot de passe, ni ses
groupes, ni ses attributs. Il a pu être renommé, promu, déplacé ; c'est le droit de celui
qui exploite.
```
compte sysadmin absent → créé, mot de passe généré en voûte, changement forcé
compte sysadmin présent → aucune action, quel que soit son état
```
C'est la différence entre `state: present` et une réconciliation. Le second serait un
défaut ici.
**Le premier mot de passe n'est pas *le* mot de passe de quelqu'un.** Généré, déposé en
voûte, **changement forcé à la première ouverture** : c'est un jeton d'amorçage à usage
unique. Sans ce changement, l'auteur du déploiement connaîtrait le mot de passe du
sysadmin — ce qui contredit « une identité, une personne ».
2026-08-11 20:25:01 -04:00
### 3.1 Raser un hôte d'identité réarme le jeton — et détruit tout le reste
Le « one-shot » tient **tant que l'annuaire vit** . `make raser` sur l'hôte qui porte
`openldap` (dérivé : `applications.openldap.hote` ) détruit `ou=people` et `ou=groups` avec
la VM.
La condition étant l'**existence** et non la conformité, le redéploiement retrouve un compte
absent et le recrée. Mesuré le 2026-08-11, après rasage de `idm-01` :
```
dn: uid=sysadmin,ou=people,dc=chezlepro,dc=internal
pwdReset: TRUE
```
Le jeton d'amorçage de la voûte redevient donc la valeur en vigueur, et le changement forcé
est réarmé. **Le mot de passe que l'exploitant avait choisi n'existe plus.**
**Ce n'est pas la perte principale.** Par le §2, les appartenances ne sont *jamais*
réconciliées : Set-OPS crée **un** compte, et rien d'autre. Tout compte créé depuis, toute
appartenance à un groupe, toute promotion décidée en exploitation sont détruits — et **ne
seront pas recréés**. Le code ne peut pas reconstruire ce qu'il a délibérément choisi de ne
pas posséder. C'est la contrepartie exacte du régime qui protège ces décisions du prochain
`make deployer` .
sauvegarde : le catalogue derive du groupe, et P36 le prouve
Correction : l'entree precedente attribuait le defaut a la reconstruction
from-zero. C'est faux — le commit fondateur 7476a54 (2026-07-03) disait lui-meme
« Reste : jobs Tier 1 (pg_dump/slapcat/vmail/forgejo) ». Ils n'ont jamais ete
ecrits, et infra-pki-01 a ensuite perdu son integration client_backup.
Le role qui POSSEDE la donnee dit comment la sortir : client_backup_jobs est
l'intersection du catalogue et des group_names du noeud. Un tenant qui deplace un
service emporte sa sauvegarde avec lui. On sauvegarde l'etat NON REGENERABLE :
ni zones PowerDNS ni tableaux Grafana, ils se redeploient.
L'unite qui ment est RETIREE, pas rendue bloquante : refuser le deploiement aurait
casse infra-edge-01, infra-dns-01 et mon-01, qui ne detiennent legitimement rien.
Le defaut etait le timer qui echouait chaque nuit en donnant l'apparence d'une
sauvegarde.
P36 (D-75) lit les groupes detenteurs dans le catalogue : ajouter un role au
catalogue etend la preuve du meme geste. Elle a attrape infra-pki-01 — les cles
de l'AC — corrige au plan.
Mesure hors-noeud : collab-01 64,0 MiB/272, edge-mta-01 4,4 MiB/139,
data-sql-01 1,0 MiB (pg_dumpall complet), forge-01 26,4 KiB/68,
infra-pki-01 20,1 KiB/21, idm-01 2,3 KiB/5 (slapcat). 9 hotes, 9 success.
Reste : rien ne surveille l'unite — c'est ce silence qui a laisse le defaut
vivre un mois.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 20:50:46 -04:00
> **Il y a désormais quelque chose à restaurer — depuis le 2026-08-11 seulement.** Jusque-là
> `client_backup_jobs` valait `[]` et rien ne le surchargeait : onze hôtes lançaient chaque
> nuit un timer qui échouait sur `Fatal: nothing to backup`, et `infra-pki-01` — les clés de
> l'AC — n'avait aucune sauvegarde. `client_backup` dérive maintenant ses jeux de
> l'appartenance aux groupes, et **P36** prouve statiquement que tout détenteur d'état porte
> `client_backup`. L'annuaire de `idm-01` est sorti par `slapcat` à chaque exécution.
La sauvegarde ne dispense pas de la vérifier avant un geste destructeur. Sortir l'annuaire à
la main reste le réflexe juste avant de raser :
2026-08-11 20:25:01 -04:00
```
ansible < hote_openldap > -m shell -a "slapcat -b dc=< domaine > > /tmp/annuaire.ldif" -b
ansible < hote_openldap > -m fetch -a "src=/tmp/annuaire.ldif dest=./ flat=yes" -b
```
2026-08-07 13:27:00 -04:00
## 4. Où vivent les habilitations : LDAP, pas Keycloak
2026-08-07 13:05:48 -04:00
2026-08-07 13:27:00 -04:00
**Les groupes LDAP sont la source unique.** Keycloak les *projette* en rôles pour le web ;
les services non-OIDC les lisent directement.
2026-08-07 13:05:48 -04:00
2026-08-07 13:27:00 -04:00
L'argument est mécanique, pas doctrinal : **Dovecot et Postfix ne savent pas lire un rôle
Keycloak.** L'y loger rendrait la moitié courriel du système aveugle, et imposerait au
sysadmin de tenir deux modèles de permissions — donc de les voir diverger.
2026-08-07 13:05:48 -04:00
2026-08-07 13:27:00 -04:00
Conséquence pratique pour l'exploitant : **un seul endroit à administrer** . Ajouter
quelqu'un au groupe `sysadmin` dans LDAP lui ouvre Grafana, Forgejo, Icinga et le courriel,
sans toucher à un seul service.
2026-08-07 13:05:48 -04:00
2026-08-07 13:27:00 -04:00
## 5. Un service nomme un groupe, jamais une personne
2026-08-07 13:05:48 -04:00
2026-08-07 13:27:00 -04:00
C'est la règle qui rend la reprise possible. Chaque rôle déclare le **groupe** qu'il
reconnaît, dans son `meta/acces.yml` :
2026-08-07 13:05:48 -04:00
```yaml
acces:
2026-08-07 13:27:00 -04:00
- groupe: sysadmin # groupe LDAP
2026-08-07 13:05:48 -04:00
accorde: Admin # ce que ça vaut DANS ce service
raison: >-
2026-08-07 13:27:00 -04:00
Administration : sources de données, tableaux de bord, utilisateurs.
2026-08-07 13:05:48 -04:00
```
2026-08-07 13:27:00 -04:00
Le champ `accorde` est **le vocabulaire du service** , pas un niveau abstrait : `Admin` chez
Grafana, `owner` chez Forgejo. Inventer une échelle commune obligerait à la traduire
partout, et la traduction est exactement l'endroit où une habilitation se perd.
2026-08-07 13:05:48 -04:00
2026-08-07 13:27:00 -04:00
Un service qui nommerait une personne créerait une dette qu'on découvre le jour du
départ, service par service — et il faudrait un déploiement pour révoquer quelqu'un.
2026-08-07 13:05:48 -04:00
2026-08-07 13:27:00 -04:00
## 6. Prendre la main — le runbook
2026-08-07 13:05:48 -04:00
2026-08-07 13:27:00 -04:00
Ce qui suit est destiné au sysadmin le jour de la livraison. **C'est la partie utile de ce
document.**
2026-08-07 13:05:48 -04:00
docs : tisser les devis dans les points d'entree, et corriger trois faits perimes
Pas de refonte : 54 roles / 54 README, 34 documents, une carte, un registre de
decisions. Le retard etait ailleurs — le travail du jour vivait dans son coin,
les cinq devis n'existant que dans deux fichiers. Donc decouvrables seulement
par qui connait le Makefile, ce qui contredit « exploitable sans IA ».
Tisses dans les quatre points d'entree : ligne « Conformite du deploye » dans
la carte, section « Ecrire, puis relire (D-68) » dans AGENTS.md, §6.0 du
runbook (le premier reflexe), vue Reconstruction de la GUI.
Le tissage a fait tomber trois affirmations perimees :
- la carte annoncait 28 decisions, il y en a 66 en vigueur (D-01 -> D-69) ;
- elle disait les acces « decides, non construits, ou=people et ou=groups
restent vides » — mesure : un compte, un groupe, chaine exercee de bout en
bout sur Icinga Web 2 le jour meme ;
- la GUI parlait des « deux » devis d'infrastructure ; il y en a quatre.
Formation et wiki differes : la reconstruction from-zero est le test de cette
documentation, et enseigner une procedure que personne n'a executee serait
enseigner une hypothese.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 12:33:35 -04:00
### 6.0 D'abord : demander au système où il en est
Avant de suivre quoi que ce soit, cinq commandes disent l'état réel. Elles ne modifient
rien et sortent en erreur s'il y a un écart entre ce qui tourne et ce que le plan décrit.
```
make identite-plan # realm, fédération LDAP, mappeurs, politique de mot de passe, comptes
make certificats-plan # certificats sur disque contre certificats réellement servis
make expositions-plan # chaque service publié répond-il — depuis l'edge et depuis ton poste
make postgresql-plan # chiffrement imposé, et à quels réseaux
make courriel-plan # Postfix → LDAP → LMTP → Dovecot → IMAP, file d'attente comprise
```
C'est le premier réflexe à prendre, et pas seulement le jour de la livraison : après tout
changement, après une panne, avant d'appeler quelqu'un. `make deployer` **répare** ;
ces devis **constatent** . Le détail de ce qu'ils vérifient et de ce qu'ils ne vérifient
pas est dans `devis-services.md` .
Un exemple de ce qu'ils voient et que rien d'autre ne voyait : le 2026-08-08, le
certificat de l'autorité elle-même était expiré depuis plus de huit heures, le
renouvellement échouant toutes les quatorze minutes. Aucun écran ne le disait.
2026-08-07 13:27:00 -04:00
### 6.1 Récupérer le mot de passe d'amorçage
2026-08-07 13:05:48 -04:00
2026-08-07 13:27:00 -04:00
```
ansible-vault view instance/inventories/principal/group_vars/all/vault.yml \
| grep vault_sysadmin_amorcage
```
2026-08-07 13:05:48 -04:00
2026-08-07 13:52:00 -04:00
**Tu ne pourras rien faire d'autre avant de l'avoir changé** : l'annuaire refuse toute
opération sauf le changement lui-même (§3). C'est le premier geste de la reprise, et il
n'est pas optionnel.
2026-08-07 13:05:48 -04:00
documentation : la tournee des 74 documents, parce qu un balayage ne lit pas
La revision a commence par un balayage par motifs — chemins morts, cibles make
absentes, comptes derives. Il a trouve une trentaine d ecarts et rate presque
tout le reste : un motif ne voit que ce qui s exprime en motif.
make hote-planifier en est l exemple. La cible EXISTE, donc le controle passait
au vert. C est une cible depreciee qui refuse et sort en 2, recommandee par
AGENTS.md, et qui contredit la REGLE D OR du meme fichier trois ecrans plus
haut. Il fallait lire pour la voir.
74 documents lus un par un. 66 corriges, 8 exacts.
CE QUI ETAIT FRANCHEMENT FAUX
AGENTS.md, la source d autorite, annoncait la flotte pas encore executee contre
des VM reelles. Elle a ete rasee et remontee depuis zero trois fois.
ecosysteme-chezlepro.md, le document montre a un client, portait la meme
phrase : il se sous-vendait gravement.
courriel-conception.md s ouvrait sur aucun role n est encore ecrit, au-dessus de
son propre paragraphe 1 qui les nomme. autorisation.md se terminait sur rien n
est construit alors qu il rapporte des mesures datees du role en fonctionnement.
hebergeur-exploitation.md disait rien n est fait d un depot qui existe.
filiation-emancipation.md se contredisait a deux ecrans de distance.
DES MODELES DECRITS D APRES UN MONDE ANTERIEUR
Le resolveur : cinq documents decrivaient un Unbound par VM en opt-in, trois le
donnaient en exemple d integration FACULTATIVE — il est universel depuis le
2026-08-24. L adressage de nomenclature-vm.md : reseau unique, VLAN 11-15, VMID
a cinq chiffres. Le nommage SDN de sdn-evpn.md contre le code : c est le wiki
qui avait raison.
CE QUI CASSE AU PREMIER ESSAI
Le nom du gabarit dore etait faux a quatre endroits, dont la procedure qui le
FABRIQUE et le critere R2 de l epreuve d operateur independant.
preparer-un-site-hebergeur.md avertissait qu une VM faite a la main serait
detruite : raser derive du plan, il ne la detruira jamais — le risque est l
inverse. Un mot de passe d essai en clair dans un depot public.
DEUX PREUVES ETENDUES, ET UNE QUI SE TROMPAIT ELLE-MEME
P57 couvre les groupes : elle a signale aussitot 29 groupes annonces au-dessus d
un tableau qui en cite 40. P29 confronte le tableau de authentification.md aux
declarations reelles : 12 annonces, 21 reels.
Et P57 imposait un chiffre faux — 56 preuves alors que le depot en porte 57, la
conditionnelle vivant hors de tout comptage. Un garde-fou qui fait respecter une
erreur ajoute l assurance a l erreur.
CE QUI RESTE, ET QU AUCUNE PREUVE NE TIENT
Deux comptes trouves a la main. Et une lacune reelle : rien ne garde les
meta/acces.yml — ni qu un service web-sso en porte un, ni que le groupe qu il
nomme existe. P29 tient les positions d authentification, personne ne tient les
habilitations.
make prouver : CONFORME, 56 OK, 0 echec, 1 saute. 0 lien mort.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-06 16:18:23 -04:00
> **La clé de la voûte — une voûte, une clé (depuis le 2026-08-28).** Ce paragraphe a
> longtemps désigné un `ANSIBLE_VAULT_PASSWORD_FILE` unique, `~/.config/setops-vault-pass`.
> Ce n'est plus le mécanisme : un seul mot de passe ouvrait alors *toutes* les voûtes de la
> flotte, celle de l'hébergeur comprise — compromettre le plus petit locataire, c'était
> obtenir les secrets de tous. Chaque dépôt a désormais **sa** clé, nommée d'après lui :
> `~/.config/setops-vault-<dépôt-en-minuscules>`. Le `Makefile` les rassemble tout seul dans
> `ANSIBLE_VAULT_IDENTITY_LIST` (`scripts/voutes.py`), et Ansible les essaie toutes — il n'y
> a **rien à exporter**. Pour voir ce que cette machine peut ouvrir :
>
> ```
> python3 scripts/voutes.py etat
> ```
>
> **Sans la clé de ton écosystème, rien de ce qui suit n'est possible** — c'est la clé de
> voûte au sens propre, et la première chose à sortir de la machine
> (`make cles-exporter`, cf. [`sortir-les-cles-du-poste.md`](sortir-les-cles-du-poste.md)).
2026-08-07 13:05:48 -04:00
2026-08-07 17:48:43 -04:00
### 6.2 Deux consoles Keycloak, et la racine mène à la mauvaise
**C'est le piège de la première connexion.** `https://auth.<domaine>/` redirige vers `/admin/`
— la console d'administration du realm ** `master` **. Le compte `sysadmin` n'y existe pas : il
vit dans le realm applicatif. Keycloak répond alors *« invalid username or password »* , ce qui
est exact et parfaitement trompeur — le mot de passe est bon, c'est la porte qui ne l'est pas.
| Ce que tu veux faire | URL | Compte |
|---|---|---|
| **ton compte, tes accès** | `https://auth.<domaine>/realms/<realm>/account/` | `sysadmin` + jeton d'amorçage |
2026-08-07 19:47:10 -04:00
| **administrer ton realm** | `https://auth.<domaine>/admin/<realm>/console/` | `sysadmin` — par le groupe |
| Keycloak lui-même (secours) | `https://auth.<domaine>/admin/` | `admin` + `vault_keycloak_admin` |
2026-08-07 17:48:43 -04:00
La première ligne est celle de la reprise. C'est là que le changement de mot de passe te sera
imposé, et c'est cette connexion qui **importe ton groupe dans le realm** — sans elle, les
habilitations sur Grafana, Forgejo et Nextcloud restent câblées mais jamais exercées.
2026-08-07 19:47:10 -04:00
**Administrer ton realm passe désormais par le groupe** : `realm-admin` (rôle du client
`realm-management` ) est attaché à `sysadmin` . La console est celle du realm —
`/admin/<realm>/console/` — pas la racine `/admin/` , qui est celle de `master` .
La dernière ligne reste le **compte de secours** (D-40). Sa portée est `master` , hors d'atteinte
du groupe : c'est voulu. Un accès de secours qui dépendrait des habilitations qu'il doit pouvoir
réparer ne serait pas un accès de secours.
2026-08-07 17:48:43 -04:00
### 6.3 Faire confiance à l'AC interne
2026-08-07 17:26:35 -04:00
Les interfaces web portent des certificats de l'autorité interne, qu'aucun navigateur ne
connaît. Récupérer la racine et **son empreinte** :
```
make ca-racine # écrit ./root_ca.crt, affiche sujet, validité, empreinte
make ca-empreinte # la même empreinte, lue SUR l'AC — le témoin de comparaison
```
**Compare les deux avant d'installer.** Ce n'est pas une formalité : installer une AC, c'est
lui donner le droit de signer *n'importe quel nom* pour ton navigateur. La comparaison est ce
qui distingue ta racine d'une racine interceptée.
```
sudo cp root_ca.crt /usr/local/share/ca-certificates/setops-root.crt
sudo update-ca-certificates
```
Firefox a son propre magasin : *Paramètres → Certificats → Autorités → Importer* .
La racine est un certificat **public** — elle n'a rien à faire dans la voûte, et tout à faire
dans le magasin de confiance de qui administre.
2026-08-07 17:48:43 -04:00
### 6.4 Où administrer quoi
2026-08-07 13:05:48 -04:00
2026-08-07 13:27:00 -04:00
| Ce que tu veux faire | Où | Comment |
|---|---|---|
| créer/désactiver une personne | **LDAP** (`ou=people`) | via Keycloak, ou `ldapmodify` |
| donner ou retirer un accès | **LDAP** (`ou=groups`) | ajouter/retirer du groupe |
| changer ce qu'un groupe *vaut* dans un service | **le plan** (`meta/acces.yml`) | c'est de l'infrastructure : éditer, redéployer |
2026-08-07 13:05:48 -04:00
2026-08-07 13:27:00 -04:00
La troisième ligne est la seule qui passe par Set-OPS. Les deux premières t'appartiennent
et **le dépôt ne les touchera plus jamais** .
2026-08-07 13:05:48 -04:00
2026-08-07 17:48:43 -04:00
### 6.5 Si tu te fermes dehors
2026-08-07 13:05:48 -04:00
2026-08-07 13:27:00 -04:00
Les comptes locaux de chaque service existent, sont en voûte, et leur formulaire n'est pas
annoncé (`authentification.md` §3). L'accès de secours passe par `sudo` sur l'hôte :
```
grafana-cli admin reset-admin-password
forgejo admin user change-password
occ user:resetpassword
```
Et si LDAP lui-même est en panne, ces trois commandes fonctionnent quand même : elles ne
dépendent ni de Keycloak, ni de l'annuaire. C'est le sens de D-40.
2026-08-08 06:08:06 -04:00
### 6.6 « Mot de passe oublié » — pour ne pas dépendre de toi
Un utilisateur qui oublie son mot de passe ne doit pas avoir à t'appeler : la seule issue
serait alors que tu manipules le mot de passe de quelqu'un d'autre, ce que « une identité,
une personne » cherche précisément à écarter. L'écran de connexion du realm porte donc le
lien **Forgot password?** , et Keycloak envoie lui-même le courriel.
Deux conditions, toutes deux tenues par le code :
- **Un relais SMTP.** `serveur_keycloak` le dérive du plan (`applications.postfix.hote`) —
aucun nom de machine n'est écrit nulle part. Le MTA accepte les hôtes du supernet sans
authentification (`mynetworks`) et `idm-01` porte déjà `client_smtp` , donc le flux existe.
Si le plan ne déclare pas de MTA, le rôle **refuse** plutôt que d'afficher un écran qui
promet un courriel que personne n'enverrait.
- **Une adresse sur le compte.** C'est la seule valeur de l'amorçage qui ne se dérive pas :
elle désigne une personne, donc quelque chose d'extérieur au système qu'on amorce.
`amorcage_acces_courriel` doit être déclarée, et le rôle refuse de créer le compte sans
elle. Une adresse `sysadmin@<domaine_interne>` serait un piège : elle est servie par une
boîte que tu ne peux pas encore lire.
**Attention à un reliquat.** Tant que la fédération LDAP était en `READ_ONLY` , une adresse
saisie dans la console de compte restait dans la base de Keycloak sans jamais atteindre
l'annuaire. Les deux côtés pouvaient donc afficher des adresses différentes sans que rien
ne le signale. En `WRITABLE` (le mode actuel) le problème ne se reproduit pas, mais les
comptes créés avant peuvent encore porter l'écart. Pour le voir et le réduire :
```
ldapsearch -x -H ldapi:/// -D "cn=admin,$BASE" -W -b "uid=< uid > ,ou=people,$BASE" mail
# puis, si les deux diffèrent : corriger dans LDAP (source de vérité), et resynchroniser
# Identity providers → LDAP → Sync all users
```
identite : une declaration de politique de mot de passe, deux executants
Quatre defauts mesures dans l'integration Keycloak/LDAP, meme famille : une
valeur declaree d'un cote, consommee de l'autre, rien qui verifie la jonction.
1. Aucune regle ne s'appliquait sur le chemin d'un vrai utilisateur. Sonde :
« abcd » refuse par l'operation etendue LDAP, accepte par Keycloak (204),
puis actif pour l'authentification. Keycloak ecrivait userPassword en
direct (ppolicy aveugle) et le realm n'avait aucune passwordPolicy.
2. ldap_entry ne fait que CREER : la politique etait figee a sa creation. Le
depot disait pwdMustChange TRUE, le serveur FALSE — une reconstruction
from-zero aurait ressuscite la boucle du 2026-08-07. ldap_attrs state=exact
reconcilie la politique et l'overlay (DN lu, pas devine).
3. syncRegistrations absent : un compte cree dans Keycloak n'atteignait jamais
ou=people — acces web, aucune boite, invisible du modele de groupes.
4. Le prenom pointait sur cn (nom complet) : « Administrateur systeme systeme ».
Ajoute roles/resoudre_politique_mdp : LA declaration, traduite en pwdPolicy,
passwordPolicy et anti-force-brute. Les deux roles la consomment sans la
redeclarer.
usePasswordModifyExtendedOp ET validatePasswordPolicy : la seconde est
porteuse, Keycloak se liant en rootDN et slapd n'appliquant pas ses controles
de qualite au rootDN. La premiere seule aurait paru juste sans tenir.
Verification : abcd -> 400 « minimum length 12 » et absent de LDAP ; mot de
passe conforme -> 204 puis ldapwhoami accepte ; POST users -> 201 ET present
dans ou=people. Second passage des deux playbooks : changed=0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 06:28:10 -04:00
### 6.7 La politique de mot de passe : une déclaration, deux exécutants
Les règles sont déclarées **une seule fois** , dans `resoudre_politique_mdp` , en termes
neutres. Le rôle les traduit dans les deux dialectes qui doivent les appliquer :
`pwdPolicy` pour OpenLDAP, `passwordPolicy` pour le realm. Changer la longueur minimale
se fait à un seul endroit.
Il a fallu en arriver là parce que les deux se renvoyaient la balle. LDAP portait bien les
règles — mais il ne sait que **refuser** , sans jamais dire pourquoi à l'écran. Keycloak,
lui, ne validait rien, et écrivait `userPassword` directement : l'overlay `ppolicy` ne
voyait même pas passer le changement. Mesuré le 2026-08-08 avec un compte sonde, le même
`abcd` était refusé par l'opération étendue LDAP et **accepté** par Keycloak, puis actif
pour l'authentification.
Deux réglages de la fédération tiennent la correction, et il faut les deux :
| Réglage | Ce qu'il répare |
|---|---|
| `usePasswordModifyExtendedOp` | slapd voit le changement passer, donc `ppolicy` s'applique |
| `validatePasswordPolicy` | Keycloak valide la politique du realm **avant** d'écrire |
Le second est le contrôle porteur : Keycloak se lie en rootDN, et slapd n'applique pas ses
contrôles de qualité au rootDN. L'utilisateur voit désormais *« Invalid password: minimum
length 12 »* au lieu d'un refus muet.
**`pwdMustChange` reste délibérément à `FALSE` .** Il signifie « quand un *administrateur*
pose un mot de passe, l'utilisateur devra le changer ». Comme Keycloak se lie en rootDN,
tout changement qui passe par lui *est* un changement administrateur : l'utilisateur
choisissait un nouveau mot de passe, `ppolicy` reposait aussitôt `pwdReset` , et l'écran
redemandait un changement — sans fin. Le changement forcé à la première connexion est
obtenu par l'action requise `UPDATE_PASSWORD` de Keycloak, qui, elle, se consomme.
### 6.8 Ce que tu dois changer en priorité
2026-08-07 13:27:00 -04:00
2026-08-07 13:52:00 -04:00
1. **Le mot de passe d'amorçage** — imposé dès la première connexion, tu n'as pas le
choix (§3).
2026-08-07 13:27:00 -04:00
2. **Les comptes de secours** générés au déploiement : ils sont en voûte, et l'auteur du
déploiement y a eu accès. Les régénérer transfère réellement le contrôle.
3. **Le mot de passe de la voûte** elle-même, si le dépôt change de mains.
2026-08-07 13:05:48 -04:00
identite : une declaration de politique de mot de passe, deux executants
Quatre defauts mesures dans l'integration Keycloak/LDAP, meme famille : une
valeur declaree d'un cote, consommee de l'autre, rien qui verifie la jonction.
1. Aucune regle ne s'appliquait sur le chemin d'un vrai utilisateur. Sonde :
« abcd » refuse par l'operation etendue LDAP, accepte par Keycloak (204),
puis actif pour l'authentification. Keycloak ecrivait userPassword en
direct (ppolicy aveugle) et le realm n'avait aucune passwordPolicy.
2. ldap_entry ne fait que CREER : la politique etait figee a sa creation. Le
depot disait pwdMustChange TRUE, le serveur FALSE — une reconstruction
from-zero aurait ressuscite la boucle du 2026-08-07. ldap_attrs state=exact
reconcilie la politique et l'overlay (DN lu, pas devine).
3. syncRegistrations absent : un compte cree dans Keycloak n'atteignait jamais
ou=people — acces web, aucune boite, invisible du modele de groupes.
4. Le prenom pointait sur cn (nom complet) : « Administrateur systeme systeme ».
Ajoute roles/resoudre_politique_mdp : LA declaration, traduite en pwdPolicy,
passwordPolicy et anti-force-brute. Les deux roles la consomment sans la
redeclarer.
usePasswordModifyExtendedOp ET validatePasswordPolicy : la seconde est
porteuse, Keycloak se liant en rootDN et slapd n'appliquant pas ses controles
de qualite au rootDN. La premiere seule aurait paru juste sans tenir.
Verification : abcd -> 400 « minimum length 12 » et absent de LDAP ; mot de
passe conforme -> 204 puis ldapwhoami accepte ; POST users -> 201 ET present
dans ou=people. Second passage des deux playbooks : changed=0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 06:28:10 -04:00
### 6.9 Faire tourner un secret : l'ordre n'est pas indifférent
2026-08-07 21:16:51 -04:00
**Les comptes de secours des services** (`vault_grafana_admin`, `vault_forgejo_admin` ,
`vault_nextcloud_admin` ) se régénèrent en voûte, puis un redéploiement les applique. Les rôles
savent désormais *changer* un mot de passe existant, pas seulement le créer.
**Le compte d'administration de Keycloak est différent : il est le moyen de se changer
lui-même.** Régénérer la voûte d'abord le rendrait inapplicable — plus rien ne pourrait
s'authentifier pour poser la nouvelle valeur. L'ordre est donc inversé :
```
1. s'authentifier avec la valeur ACTUELLE
2. poser la nouvelle valeur dans Keycloak
3. vérifier que la nouvelle fonctionne
4. seulement alors, écrire la voûte
```
C'est une **procédure** , pas un redéploiement. La même contrainte vaut pour tout secret qui
2026-08-08 05:46:38 -04:00
est aussi la clé de son propre changement.
**`vault_openldap_admin` en est un, avec deux pièges de plus.** Il ne se change pas par
`ldappasswd` : `cn=admin` n'est pas une entrée de la base mais le **rootDN** déclaré dans
`cn=config` . Le mot de passe vit dans `olcRootPW` , et se modifie par un bind `EXTERNAL` :
```
slappasswd -h '{SSHA}' -s < nouveau > puis ldapmodify -Y EXTERNAL sur olcDatabase={1}mdb
```
Et il a **quatre consommateurs** : Keycloak (dans sa base), Dovecot, Postfix, Icinga Web 2
(fichiers de configuration). Les trois derniers suivent au redéploiement ; Keycloak stocke le
mot de passe de liaison et le masque — sa réconciliation passe donc par une empreinte, comme
les comptes de secours.
2026-08-07 21:16:51 -04:00
**Et une vérification n'est pas un message de succès.** `grafana-cli` annonçait
« Admin password changed successfully » en écrivant dans une base qui n'était pas celle du
serveur. Ce qui l'a démasqué est l'**état** — le champ `updated` du compte, inchangé. Après
toute rotation, s'authentifier réellement avec la nouvelle valeur.
2026-08-07 13:05:48 -04:00
## 7. Ce que ce document ne couvre pas
2026-08-07 13:27:00 -04:00
**La gestion courante des comptes.** Créer, suspendre, réaffecter : c'est le travail du
sysadmin, pas du dépôt. Set-OPS ne fournit ni registre de personnes, ni synchronisation —
volontairement. Un registre versionné mettrait des données personnelles dans l'historique
git, de façon permanente et difficile à retirer.
2026-08-07 13:05:48 -04:00
**L'autorisation machine-à-machine.** Les comptes de service (PostgreSQL, Redis, jetons
d'API) ne sont pas des humains et ne passent pas par LDAP — ils vivent en voûte, liés à un
2026-08-07 13:27:00 -04:00
service et non à une personne.
2026-08-07 13:05:48 -04:00
2026-08-07 13:27:00 -04:00
**Les permissions internes à un service.** Qui peut écrire dans quel dépôt Forgejo, qui voit
quel dossier Nextcloud : ça se règle dans le service, à partir du groupe qu'il a reçu.
Set-OPS accorde l'entrée et le niveau ; il ne réimplémente pas le modèle de chaque
2026-08-07 13:05:48 -04:00
application.
documentation : la tournee des 74 documents, parce qu un balayage ne lit pas
La revision a commence par un balayage par motifs — chemins morts, cibles make
absentes, comptes derives. Il a trouve une trentaine d ecarts et rate presque
tout le reste : un motif ne voit que ce qui s exprime en motif.
make hote-planifier en est l exemple. La cible EXISTE, donc le controle passait
au vert. C est une cible depreciee qui refuse et sort en 2, recommandee par
AGENTS.md, et qui contredit la REGLE D OR du meme fichier trois ecrans plus
haut. Il fallait lire pour la voir.
74 documents lus un par un. 66 corriges, 8 exacts.
CE QUI ETAIT FRANCHEMENT FAUX
AGENTS.md, la source d autorite, annoncait la flotte pas encore executee contre
des VM reelles. Elle a ete rasee et remontee depuis zero trois fois.
ecosysteme-chezlepro.md, le document montre a un client, portait la meme
phrase : il se sous-vendait gravement.
courriel-conception.md s ouvrait sur aucun role n est encore ecrit, au-dessus de
son propre paragraphe 1 qui les nomme. autorisation.md se terminait sur rien n
est construit alors qu il rapporte des mesures datees du role en fonctionnement.
hebergeur-exploitation.md disait rien n est fait d un depot qui existe.
filiation-emancipation.md se contredisait a deux ecrans de distance.
DES MODELES DECRITS D APRES UN MONDE ANTERIEUR
Le resolveur : cinq documents decrivaient un Unbound par VM en opt-in, trois le
donnaient en exemple d integration FACULTATIVE — il est universel depuis le
2026-08-24. L adressage de nomenclature-vm.md : reseau unique, VLAN 11-15, VMID
a cinq chiffres. Le nommage SDN de sdn-evpn.md contre le code : c est le wiki
qui avait raison.
CE QUI CASSE AU PREMIER ESSAI
Le nom du gabarit dore etait faux a quatre endroits, dont la procedure qui le
FABRIQUE et le critere R2 de l epreuve d operateur independant.
preparer-un-site-hebergeur.md avertissait qu une VM faite a la main serait
detruite : raser derive du plan, il ne la detruira jamais — le risque est l
inverse. Un mot de passe d essai en clair dans un depot public.
DEUX PREUVES ETENDUES, ET UNE QUI SE TROMPAIT ELLE-MEME
P57 couvre les groupes : elle a signale aussitot 29 groupes annonces au-dessus d
un tableau qui en cite 40. P29 confronte le tableau de authentification.md aux
declarations reelles : 12 annonces, 21 reels.
Et P57 imposait un chiffre faux — 56 preuves alors que le depot en porte 57, la
conditionnelle vivant hors de tout comptage. Un garde-fou qui fait respecter une
erreur ajoute l assurance a l erreur.
CE QUI RESTE, ET QU AUCUNE PREUVE NE TIENT
Deux comptes trouves a la main. Et une lacune reelle : rien ne garde les
meta/acces.yml — ni qu un service web-sso en porte un, ni que le groupe qu il
nomme existe. P29 tient les positions d authentification, personne ne tient les
habilitations.
make prouver : CONFORME, 56 OK, 0 echec, 1 saute. 0 lien mort.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-06 16:18:23 -04:00
**Ce qui est construit, et ce qui ne l'est pas — mesuré le 2026-09-06.** Ce document s'est
terminé jusqu'à cette date sur « **Rien n'est construit** [...] le rôle d'amorçage, les
`meta/acces.yml` et la preuve restent à écrire ». C'était devenu faux au point de contredire
le §3 du même document, qui rapporte des mesures **datées du 2026-08-11** prises sur le rôle
en fonctionnement. L'état réel :
| | État |
|---|---|
| Le rôle d'amorçage | ** `roles/amorcage_acces/` ** — écrit, déployé, et c'est lui qui crée l'unique compte `sysadmin` du §3 |
| Les `meta/acces.yml` | **écrits pour les cinq services `web-sso`** : `serveur_forgejo` , `serveur_grafana` , `serveur_icingaweb2` , `serveur_keycloak` , `serveur_nextcloud` |
| La preuve | **toujours à écrire** — c'est la seule ligne d'origine qui tient |
La lacune restante mérite d'être nommée pour ce qu'elle est. Le §5 de
[`authentification.md` ](authentification.md ) le formule ainsi : *une directive qu'aucune
garde ne vérifie finit par ne plus être vraie*. `meta/acces.yml` est exactement dans ce cas
— rien ne vérifie qu'un service `web-sso` en porte un, ni que le groupe qu'il nomme existe
dans l'annuaire. **P29** garde les *positions* d'authentification ; personne ne garde encore
les *habilitations* .