resoudre_idp : le nom invente etait dans QUATRE roles
`forge-01` a echoue sur une URL de decouverte pointant `https://keycloak.<domaine>` — le nom que rien ne publie. J'avais corrige exactement ca dans `serveur_oauth2_proxy` quelques heures plus tot, en croyant regler un cas isole. Il etait dans quatre roles : forgejo, grafana, nextcloud, oauth2-proxy. Chacun fabriquait le meme nom par la meme convention. Une cinquieme correction a la main aurait diverge comme les quatre autres. `roles/resoudre_idp` lit l'exposition declaree au plan et rend hote, base et discovery. Les roles n'en gardent qu'un REPLI nomme, jamais la valeur de travail. Verifie en base sur forge-01 : discovery = auth.chezlepro.internal, GroupClaimName = groups, AdminGroup = sysadmin. Meme lecon que `resoudre_annuaire` hier : corriger la valeur la ou elle echoue ne corrige que la. Ce sont les copies silencieuses qui coutent la journee suivante. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
a08c61bb4e
commit
6699935dc7
10 changed files with 139 additions and 3 deletions
29
CHANGELOG.md
29
CHANGELOG.md
|
|
@ -2,6 +2,35 @@
|
||||||
|
|
||||||
## 2026-08-06 — le chemin nord-sud devient dérivable
|
## 2026-08-06 — le chemin nord-sud devient dérivable
|
||||||
|
|
||||||
|
### `resoudre_idp` — le nom inventé était dans **quatre** rôles
|
||||||
|
|
||||||
|
`forge-01` a échoué sur `serveur_forgejo_oidc_discovery`, qui pointait
|
||||||
|
`https://keycloak.<domaine>` — le nom que rien ne publie. J'avais corrigé exactement ça dans
|
||||||
|
`serveur_oauth2_proxy` quelques heures plus tôt, en croyant régler un cas isolé.
|
||||||
|
|
||||||
|
Il était en fait dans **quatre** rôles : forgejo, grafana, nextcloud, oauth2-proxy. Chacun
|
||||||
|
fabriquait le même nom par la même convention, et chacun aurait échoué au même endroit —
|
||||||
|
`oauth2-proxy` l'a fait le premier parce qu'il a été déployé le premier.
|
||||||
|
|
||||||
|
Une cinquième copie corrigée à la main aurait divergé comme les quatre autres. D'où
|
||||||
|
`roles/resoudre_idp` : il lit l'exposition déclarée au plan et rend `resoudre_idp_hote`,
|
||||||
|
`resoudre_idp_base`, `resoudre_idp_discovery`. Les trois rôles restants l'appellent ; leur
|
||||||
|
défaut ne garde plus qu'un **repli** nommé, jamais la valeur de travail.
|
||||||
|
|
||||||
|
Vérifié en base sur `forge-01` :
|
||||||
|
|
||||||
|
```
|
||||||
|
OpenIDConnectAutoDiscoveryURL : https://auth.chezlepro.internal/realms/chezlepro/…
|
||||||
|
GroupClaimName : 'groups' AdminGroup : 'sysadmin'
|
||||||
|
```
|
||||||
|
|
||||||
|
Le câblage par groupe et l'URL correcte, sur un service déployé pour la première fois.
|
||||||
|
|
||||||
|
**La leçon est la même que pour `resoudre_annuaire` hier**, et elle mérite d'être dite deux
|
||||||
|
fois : corriger la valeur là où elle échoue ne corrige que là. Ce sont les autres copies,
|
||||||
|
silencieuses, qui coûtent la journée suivante.
|
||||||
|
|
||||||
|
|
||||||
### Forgejo et Nextcloud câblés — avant d'être déployés
|
### Forgejo et Nextcloud câblés — avant d'être déployés
|
||||||
|
|
||||||
Les deux services n'existaient pas encore : les câbler maintenant vaut mieux que les corriger
|
Les deux services n'existaient pas encore : les câbler maintenant vaut mieux que les corriger
|
||||||
|
|
|
||||||
23
roles/resoudre_idp/README.md
Normal file
23
roles/resoudre_idp/README.md
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
# resoudre_idp — l'URL publique du fournisseur d'identite
|
||||||
|
|
||||||
|
Resout `auth.<domaine>` depuis l'exposition que le plan declare, au lieu de fabriquer
|
||||||
|
`keycloak.<domaine>` par convention.
|
||||||
|
|
||||||
|
## Pourquoi
|
||||||
|
|
||||||
|
Quatre roles construisaient le nom a la main. Aucun ne le publiait : le DNS resout
|
||||||
|
`auth`, nginx sert `auth`, et Keycloak s'annonce sous `auth`. Un client qui interroge
|
||||||
|
`keycloak.<domaine>` echoue — ou recoit un issuer qui ne correspond pas au sien
|
||||||
|
(« issuer did not match the issuer returned by provider »).
|
||||||
|
|
||||||
|
Corrige une premiere fois dans `serveur_oauth2_proxy`, puis retrouve a l'identique
|
||||||
|
dans trois autres roles. Une convention de nommage se contredit ; une declaration se
|
||||||
|
corrige.
|
||||||
|
|
||||||
|
## Sorties
|
||||||
|
|
||||||
|
| Fait | Exemple |
|
||||||
|
|---|---|
|
||||||
|
| `resoudre_idp_hote` | `auth.chezlepro.internal` |
|
||||||
|
| `resoudre_idp_base` | `https://auth.chezlepro.internal/realms/chezlepro` |
|
||||||
|
| `resoudre_idp_discovery` | `…/.well-known/openid-configuration` |
|
||||||
5
roles/resoudre_idp/defaults/main.yml
Normal file
5
roles/resoudre_idp/defaults/main.yml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
# Application du plan qui EST le fournisseur d'identite.
|
||||||
|
resoudre_idp_app: "keycloak"
|
||||||
|
# Realm applicatif — le meme pour tous les consommateurs d'une instance.
|
||||||
|
resoudre_idp_realm: "{{ identite_realm | default('chezlepro') }}"
|
||||||
42
roles/resoudre_idp/tasks/main.yml
Normal file
42
roles/resoudre_idp/tasks/main.yml
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
---
|
||||||
|
# Role utilitaire (pas un groupe deployable) : resout l'URL PUBLIQUE du fournisseur
|
||||||
|
# d'identite depuis l'exposition que le plan declare.
|
||||||
|
#
|
||||||
|
# Sorties (facts) :
|
||||||
|
# resoudre_idp_hote — auth.<domaine> (le FQDN publie, pas un nom fabrique)
|
||||||
|
# resoudre_idp_base — https://<hote>/realms/<realm>
|
||||||
|
# resoudre_idp_discovery — .../.well-known/openid-configuration
|
||||||
|
#
|
||||||
|
# POURQUOI CE ROLE. Quatre roles fabriquaient `https://keycloak.<domaine>` par
|
||||||
|
# convention de nommage — un nom que RIEN ne publie : le plan expose Keycloak sous
|
||||||
|
# `auth.<domaine>`, et c'est ce nom que le DNS resout, que nginx sert et que
|
||||||
|
# Keycloak annonce dans son issuer. Un client OIDC qui interroge l'autre nom
|
||||||
|
# echoue, ou recoit un issuer qui ne correspond pas au sien.
|
||||||
|
#
|
||||||
|
# Corrige une premiere fois dans `serveur_oauth2_proxy` le 2026-08-07, puis
|
||||||
|
# retrouve a l'identique dans forgejo, grafana et nextcloud. Une quatrieme copie
|
||||||
|
# aurait diverge comme les trois premieres : d'ou ce role.
|
||||||
|
- name: Charger les registres applications et domaines (expositions)
|
||||||
|
ansible.builtin.include_vars:
|
||||||
|
file: "{{ item }}"
|
||||||
|
loop:
|
||||||
|
- "{{ setops_plan_dir }}/applications.yml"
|
||||||
|
- "{{ setops_plan_dir }}/domaines.yml"
|
||||||
|
|
||||||
|
- name: Exiger que l'IdP declare une exposition
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- ((applications | default({})).get(resoudre_idp_app, {}).get('expose', []) | length) > 0
|
||||||
|
fail_msg: >-
|
||||||
|
L'application `{{ resoudre_idp_app }}` n'expose aucun FQDN dans
|
||||||
|
instance/plan/applications.yml. Un client OIDC ne peut pas joindre un
|
||||||
|
fournisseur qui n'est publie nulle part.
|
||||||
|
|
||||||
|
- name: Deriver l'URL publique du fournisseur d'identite
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
resoudre_idp_hote: >-
|
||||||
|
{{ (applications[resoudre_idp_app].expose | first) }}
|
||||||
|
resoudre_idp_base: >-
|
||||||
|
https://{{ (applications[resoudre_idp_app].expose | first) }}/realms/{{ resoudre_idp_realm }}
|
||||||
|
resoudre_idp_discovery: >-
|
||||||
|
https://{{ (applications[resoudre_idp_app].expose | first) }}/realms/{{ resoudre_idp_realm }}/.well-known/openid-configuration
|
||||||
|
|
@ -60,7 +60,12 @@ serveur_forgejo_oidc_nom: "{{ organisation | default('organisation') | lower | r
|
||||||
serveur_forgejo_oidc_client_id: "forgejo"
|
serveur_forgejo_oidc_client_id: "forgejo"
|
||||||
serveur_forgejo_oidc_client_secret: "{{ vault_forgejo_oidc | default('') }}" # voûte (vault_forgejo_oidc)
|
serveur_forgejo_oidc_client_secret: "{{ vault_forgejo_oidc | default('') }}" # voûte (vault_forgejo_oidc)
|
||||||
serveur_forgejo_oidc_realm: "{{ identite_realm | default('chezlepro') }}"
|
serveur_forgejo_oidc_realm: "{{ identite_realm | default('chezlepro') }}"
|
||||||
serveur_forgejo_oidc_discovery: "https://keycloak.{{ domaine_interne }}/realms/{{ serveur_forgejo_oidc_realm }}/.well-known/openid-configuration"
|
# DERIVE de l'exposition declaree au plan (role `resoudre_idp`). La valeur
|
||||||
|
# ci-dessous n'est qu'un REPLI : `keycloak.<domaine>` n'est publie nulle part.
|
||||||
|
serveur_forgejo_oidc_repli: >-
|
||||||
|
https://keycloak.{{ domaine_interne }}/realms/{{ serveur_forgejo_oidc_realm }}/.well-known/openid-configuration
|
||||||
|
serveur_forgejo_oidc_discovery: >-
|
||||||
|
{{ resoudre_idp_discovery | default(serveur_forgejo_oidc_repli) }}
|
||||||
|
|
||||||
# TLS vers PostgreSQL (zero-confiance). 'disable' = compat ; 'verify-full' = chiffre + verifie
|
# TLS vers PostgreSQL (zero-confiance). 'disable' = compat ; 'verify-full' = chiffre + verifie
|
||||||
# le cert serveur contre le root_ca step-ca (via PGSSLROOTCERT dans l'unite systemd).
|
# le cert serveur contre le root_ca step-ca (via PGSSLROOTCERT dans l'unite systemd).
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,12 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
|
# L'URL PUBLIQUE de l'IdP se derive de l'exposition declaree au plan, elle ne se
|
||||||
|
# fabrique pas : `keycloak.<domaine>` n'est publie nulle part (voir resoudre_idp).
|
||||||
|
- name: Resoudre le fournisseur d'identite (role partage)
|
||||||
|
ansible.builtin.include_role:
|
||||||
|
name: resoudre_idp
|
||||||
|
vars:
|
||||||
|
resoudre_idp_realm: "{{ serveur_forgejo_oidc_realm }}"
|
||||||
- name: Refuser de fermer la connexion locale sur un Forgejo anterieur a la v10
|
- name: Refuser de fermer la connexion locale sur un Forgejo anterieur a la v10
|
||||||
# `ENABLE_INTERNAL_SIGNIN` a ete ajoute en v10 (suivi amont, issue 7476). Sur une
|
# `ENABLE_INTERNAL_SIGNIN` a ete ajoute en v10 (suivi amont, issue 7476). Sur une
|
||||||
# version anterieure il est ignore EN SILENCE : le role croirait avoir ferme la porte
|
# version anterieure il est ignore EN SILENCE : le role croirait avoir ferme la porte
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,11 @@ serveur_grafana_oidc_nom: "{{ organisation | default('Organisation') }}"
|
||||||
serveur_grafana_oidc_client_id: "grafana"
|
serveur_grafana_oidc_client_id: "grafana"
|
||||||
serveur_grafana_oidc_client_secret: "{{ vault_grafana_oidc | default('') }}" # voûte (vault_grafana_oidc)
|
serveur_grafana_oidc_client_secret: "{{ vault_grafana_oidc | default('') }}" # voûte (vault_grafana_oidc)
|
||||||
serveur_grafana_oidc_realm: "{{ identite_realm | default('chezlepro') }}"
|
serveur_grafana_oidc_realm: "{{ identite_realm | default('chezlepro') }}"
|
||||||
serveur_grafana_oidc_base: "https://keycloak.{{ domaine_interne }}/realms/{{ serveur_grafana_oidc_realm }}/protocol/openid-connect"
|
# DERIVE de l'exposition declaree au plan (role `resoudre_idp`) ; repli seulement.
|
||||||
|
serveur_grafana_oidc_repli: >-
|
||||||
|
https://keycloak.{{ domaine_interne }}/realms/{{ serveur_grafana_oidc_realm }}
|
||||||
|
serveur_grafana_oidc_base: >-
|
||||||
|
{{ (resoudre_idp_base | default(serveur_grafana_oidc_repli)) }}/protocol/openid-connect
|
||||||
serveur_grafana_oidc_scopes: "openid email profile"
|
serveur_grafana_oidc_scopes: "openid email profile"
|
||||||
# RBAC via SSO : mappe le claim 'roles' (rôles de realm Keycloak) vers le niveau Grafana.
|
# RBAC via SSO : mappe le claim 'roles' (rôles de realm Keycloak) vers le niveau Grafana.
|
||||||
# Défaut : grafana-admin→Admin, grafana-editor→Editor, sinon Viewer. Vider pour désactiver.
|
# Défaut : grafana-admin→Admin, grafana-editor→Editor, sinon Viewer. Vider pour désactiver.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,12 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
|
# L'URL PUBLIQUE de l'IdP se derive de l'exposition declaree au plan, elle ne se
|
||||||
|
# fabrique pas : `keycloak.<domaine>` n'est publie nulle part (voir resoudre_idp).
|
||||||
|
- name: Resoudre le fournisseur d'identite (role partage)
|
||||||
|
ansible.builtin.include_role:
|
||||||
|
name: resoudre_idp
|
||||||
|
vars:
|
||||||
|
resoudre_idp_realm: "{{ serveur_grafana_oidc_realm }}"
|
||||||
- name: Exiger le mot de passe admin Grafana (Vault)
|
- name: Exiger le mot de passe admin Grafana (Vault)
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,11 @@ serveur_nextcloud_oidc_nom: "{{ organisation | default('Organisation') }}"
|
||||||
serveur_nextcloud_oidc_client_id: "nextcloud"
|
serveur_nextcloud_oidc_client_id: "nextcloud"
|
||||||
serveur_nextcloud_oidc_client_secret: "{{ vault_nextcloud_oidc | default('') }}"
|
serveur_nextcloud_oidc_client_secret: "{{ vault_nextcloud_oidc | default('') }}"
|
||||||
serveur_nextcloud_oidc_realm: "{{ identite_realm | default('chezlepro') }}"
|
serveur_nextcloud_oidc_realm: "{{ identite_realm | default('chezlepro') }}"
|
||||||
serveur_nextcloud_oidc_discovery: "https://keycloak.{{ domaine_interne }}/realms/{{ serveur_nextcloud_oidc_realm }}/.well-known/openid-configuration"
|
# DERIVE de l'exposition declaree au plan (role `resoudre_idp`) ; repli seulement.
|
||||||
|
serveur_nextcloud_oidc_repli: >-
|
||||||
|
https://keycloak.{{ domaine_interne }}/realms/{{ serveur_nextcloud_oidc_realm }}/.well-known/openid-configuration
|
||||||
|
serveur_nextcloud_oidc_discovery: >-
|
||||||
|
{{ resoudre_idp_discovery | default(serveur_nextcloud_oidc_repli) }}
|
||||||
|
|
||||||
# --- Collabora Online (WOPI) — app richdocuments ---
|
# --- Collabora Online (WOPI) — app richdocuments ---
|
||||||
serveur_nextcloud_office_actif: true
|
serveur_nextcloud_office_actif: true
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,12 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
|
# L'URL PUBLIQUE de l'IdP se derive de l'exposition declaree au plan, elle ne se
|
||||||
|
# fabrique pas : `keycloak.<domaine>` n'est publie nulle part (voir resoudre_idp).
|
||||||
|
- name: Resoudre le fournisseur d'identite (role partage)
|
||||||
|
ansible.builtin.include_role:
|
||||||
|
name: resoudre_idp
|
||||||
|
vars:
|
||||||
|
resoudre_idp_realm: "{{ serveur_nextcloud_oidc_realm }}"
|
||||||
- name: Exiger les secrets Nextcloud (Vault)
|
- name: Exiger les secrets Nextcloud (Vault)
|
||||||
ansible.builtin.assert:
|
ansible.builtin.assert:
|
||||||
that:
|
that:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue