Set-OPS-Public/docs/authentification.md
Daniel Allaire 5bc3bceac1
Some checks failed
verifier / verifier (push) Has been cancelled
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

7.5 KiB

Authentification : Keycloak devant, LDAP dessous, sudo en secours

Pour qui : le mainteneur — la doctrine d'authentification, avant de brancher un service.

Directive d'architecture du 2026-08-03. Trois règles, décidées ensemble et volontairement indissociables — chacune crée le problème que la suivante résout.

  1. Toute authentification web passe par Keycloak. Aucun service ne tient son propre répertoire de comptes.
  2. LDAP est la source de vérité unique des comptes d'un tenant. Keycloak le fédère ; les protocoles qui ne parlent pas OIDC s'y lient directement.
  3. Chaque service garde un accès de secours, atteint par sudo sur l'hôte.

1. Pourquoi les trois vont ensemble

La chaîne est en série : service → Keycloak → LDAP. C'est ce qui donne « une identité, un mot de passe » — et c'est aussi trois points de panne alignés. Si LDAP tombe, plus personne n'entre nulle part, y compris pour réparer.

La règle 3 n'est donc pas une entorse au SSO : c'est ce qui rend les deux premières tenables. Sans elle, la panne d'un maillon devient une exclusion totale.

2. Portée : le web, et seulement le web

Protocole Chemin d'authentification Pourquoi
HTTP(S) — interfaces humaines Keycloak (OIDC natif, ou oauth2-proxy devant) c'est là que la directive s'applique
IMAP / SMTP LDAP direct (Dovecot, Postfix) ces protocoles ne parlent pas OIDC ; le flux est prouvé de bout en bout
SSH clé publique (PasswordAuthentication no) pas de mot de passe du tout, donc rien à fédérer
PostgreSQL, Redis secrets applicatifs en voûte comptes de service, pas d'humains

Ce qui est interdit partout, c'est qu'un service tienne son propre répertoire d'humains. Le chemin varie ; la source ne varie pas.

3. La posture retenue : la porte de secours n'est pas annoncée

Le compte local de chaque service existe — il ne peut pas dépendre de Keycloak, sinon il tomberait avec lui. Mais son formulaire de connexion n'est pas proposé au repos.

Ce n'est pas une préférence esthétique. Un formulaire local ouvert en permanence :

  • contourne la politique de mot de passe et le MFA ;
  • contourne surtout la révocation centrale — désactiver quelqu'un dans LDAP laisserait son compte local valide, sans que rien ne le signale ;
  • offre une cible de bourrage d'identifiants sur admin, en continu, pour un usage qui devrait être exceptionnel.

Le coût de la fermeture est nul depuis qu'on a tranché que sudo suffit : sudo est le mécanisme de réouverture.

Ce que chaque service sait vraiment faire

Vérifié auprès de l'amont le 2026-08-03, puis par rendu réel des gabarits dans les deux postures — pas seulement lu.

Service Réglage Effet réel Secours par sudo
Grafana GF_AUTH_DISABLE_LOGIN_FORM=true ferme le formulaire grafana-cli admin reset-admin-password
Forgejo ≥ 10 ENABLE_INTERNAL_SIGNIN=false + ENABLE_BASIC_AUTHENTICATION=false ferme la connexion interne et l'API en Basic forgejo admin user change-password
Nextcloud hide_login_form=true masque seulement occ user:resetpassword, ou …/login?direct=1

Nextcloud est une exception, et il faut la dire. hide_login_form masque le formulaire ; …/login?direct=1 y accède encore, et l'amont le documente comme voulu — c'est ainsi qu'un administrateur entre. La protection est donc de ne plus l'annoncer, pas de l'interdire. Le présenter comme équivalent à Grafana ou Forgejo donnerait un faux confort.

Le réglage s'appelle <rôle>_connexion_locale dans les trois rôles, et vaut false par défaut. Le passer à true rouvre la porte, explicitement.

Garde de version. ENABLE_INTERNAL_SIGNIN n'existe que depuis Forgejo v10 (suivi amont, ticket 7476 : « This option was added to Forgejo v10 »). Sur une version antérieure il serait ignoré sans erreur — le rôle croirait avoir fermé la porte. Un assert refuse donc connexion_locale: false sous 10.0.0 plutôt que de laisser croire.

4. La chaîne de secours

Trois maillons, aucun ne dépendant d'une porte web ouverte :

1. SSO            Keycloak → LDAP                     ← la voie normale
2. sudo via SSH   occ / grafana-cli / forgejo admin   ← Keycloak ou LDAP en panne
3. console Proxmox de la VM                           ← SSH inaccessible

Un accès de secours qu'on ne sait pas emprunter n'existe pas : les commandes exactes sont dans le tableau ci-dessus, et les mots de passe locaux vivent dans la voûte unique de l'instance.

5. La garde : chaque rôle déclare sa position

Une directive qu'aucune garde ne vérifie finit par ne plus être vraie. Chaque rôle serveur_* porte donc un meta/authentification.yml, et P29 le confronte à son code.

Portée Sens Rôles
web-sso authentification humaine web → Keycloak (mecanisme: natif ou oauth2-proxy) 5
socle-identite est la chaîne d'identité, ne peut pas se déléguer à elle-même keycloak, openldap
ldap-direct protocole non-OIDC lié à LDAP dovecot, postfix
interne-sans-auth interface joignable dans le tenant sans authentification, non exposée prometheus, loki
sans-auth-humaine aucun point d'authentification humaine 21

La preuve refuse l'oubli et le mensonge : un rôle sans déclaration, une portée inventée, un web-sso sans accès de secours ou sans posture de formulaire, un web-sso natif sans réglage <rôle>_connexion_locale défini dans defaults, et une déclaration que le code contredit.

Depuis le 2026-09-06, elle refuse aussi que ce tableau mente. Les chiffres et les listes de la troisième colonne sont confrontés aux déclarations réelles. Ils avaient cessé d'être vrais sans que rien ne le signale : la ligne sans-auth-humaine annonçait 12 rôles, il y en avait 21 — la preuve lisait les déclarations depuis le début, mais ne regardait pas ce que le document en disait.

Les indices doivent être nommés. Une première version cherchait les mots « ldap » et « oidc » dans le rôle. Le mot LDAP, présent dans un commentaire de serveur_grafana, suffisait alors à valider une déclaration ldap-direct mensongère. La preuve exige désormais une variable du namespace du rôle (<rôle>_oidc, <rôle>_ldap) ou une URI ldap:// — ce qu'une phrase en prose ne produit pas.

Une valeur mérite d'être expliquée : formulaire_local: aucun. Elle distingue un service qui n'a pas de compte local du tout — oauth2-proxy est une passerelle — de celui qui en a un et l'a fermé. Sans elle, on écrirait « fermé », ce qui laisserait croire qu'une porte a été close alors qu'il n'y en a jamais eu.

Les lacunes sont comptées, pas masquées

interne-sans-auth ne fait pas échouer la preuve : c'est une lacune assumée et nommée. Mais elle est comptée à chaque exécution, donc visible dans chaque rapport. La refuser bloquerait le harnais sur une décision déjà prise ; la taire la ferait oublier.

Prometheus et Loki sont dans ce cas. À noter, contre une idée reçue de la veille : ils ne sont pas exposés publiquement — aucun expose au plan. Seuls six groupes le sont (collabora, forgejo, grafana, keycloak, nextcloud, oauth2-proxy). Le risque est donc intra-tenant, pas frontalier — plus petit qu'annoncé, réel quand même. oauth2-proxy est déjà éprouvé devant Icinga Web 2 : le patron existe, il reste à l'appliquer.