Forgejo au SSO OIDC (2e app SSO) — éprouvé sur forge-01
serveur_forgejo (10.0.0) : PostgreSQL via registre, exposé par l'edge (auto-dérivé), source OAuth2 vers Keycloak (add-oauth idempotent), client OIDC forgejo via serveur_keycloak_clients, auto-enregistrement OIDC (identités depuis l'annuaire seulement). Prouvé : testmail (LDAP) → « Se connecter avec Chezlepro » → compte auto-créé, connecté au tableau de bord. 5 bugs de 1er déploiement corrigés : dépendance sendmail→postfix ; app.ini propriété git (persiste secrets) ; ordre admin/migrations (flush_handlers + wait_for) ; HTTP_ADDR 0.0.0.0 (edge distant) ; auto-enregistrement OIDC. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
fa945de5a7
commit
e214dcf4ed
5 changed files with 83 additions and 4 deletions
13
CHANGELOG.md
13
CHANGELOG.md
|
|
@ -3,6 +3,19 @@
|
||||||
## 2026-07-03
|
## 2026-07-03
|
||||||
|
|
||||||
### Ajouté
|
### Ajouté
|
||||||
|
- **Forgejo branché au SSO OIDC (« Se connecter avec Chezlepro ») — 2e app SSO, prouvée.**
|
||||||
|
`serveur_forgejo` (10.0.0) éprouvé sur `forge-01` (🔧→⭐), adossé à PostgreSQL (base `forgejo`
|
||||||
|
auto-provisionnée depuis le registre), exposé par l'edge (vhost + cert SAN + A PowerDNS + alias
|
||||||
|
plancher, tout auto-dérivé de `expose`). **Source OAuth2** vers Keycloak
|
||||||
|
(`forgejo admin auth add-oauth`, idempotent, realm `chezlepro`), client OIDC `forgejo`
|
||||||
|
enregistré via `serveur_keycloak_clients`. Auto-enregistrement OIDC (`[oauth2_client]
|
||||||
|
ENABLE_AUTO_REGISTRATION` + `ALLOW_ONLY_EXTERNAL_REGISTRATION` : identités depuis l'annuaire
|
||||||
|
seulement). **Prouvé** (flux authorization code headless) : `testmail` (LDAP) se connecte,
|
||||||
|
**compte auto-créé** (`testmail@lab.chezlepro.internal`), atterrit sur le tableau de bord.
|
||||||
|
**5 bugs de 1er déploiement corrigés** : dépendance périmée `serveur_sendmail`→`serveur_postfix`
|
||||||
|
(le vrai MTA) ; `app.ini` doit appartenir au user `git` (Forgejo persiste des secrets générés) ;
|
||||||
|
ordre admin/migrations (`flush_handlers` + `wait_for` avant `admin user create`) ; `HTTP_ADDR`
|
||||||
|
`127.0.0.1`→`0.0.0.0` (l'edge nginx est sur un autre hôte, 502 sinon) ; auto-enregistrement OIDC.
|
||||||
- **PowerDNS — A d'exposition auto-dérivés (le DNS de la Phase 3 des bindings).** `serveur_powerdns`
|
- **PowerDNS — A d'exposition auto-dérivés (le DNS de la Phase 3 des bindings).** `serveur_powerdns`
|
||||||
génère désormais, dans la zone interne, un enregistrement A pour chaque FQDN d'exposition (champ
|
génère désormais, dans la zone interne, un enregistrement A pour chaque FQDN d'exposition (champ
|
||||||
`expose` des applications) vers l'**edge qui le sert** (`domaines.edge`) — via
|
`expose` des applications) vers l'**edge qui le sert** (`domaines.edge`) — via
|
||||||
|
|
|
||||||
|
|
@ -66,8 +66,8 @@ groupes:
|
||||||
requiert_groupes_actifs:
|
requiert_groupes_actifs:
|
||||||
- serveur_postgresql
|
- serveur_postgresql
|
||||||
- serveur_nginx
|
- serveur_nginx
|
||||||
- serveur_sendmail
|
- serveur_postfix
|
||||||
raison: "Forgejo depend d'une base, d'une publication HTTP(S) et du courriel."
|
raison: "Forgejo depend d'une base, d'une publication HTTP(S) et d'un relais courriel (MTA Postfix)."
|
||||||
surveillance: "Verifier HTTP(S), base, files Git et envoi courriel."
|
surveillance: "Verifier HTTP(S), base, files Git et envoi courriel."
|
||||||
|
|
||||||
serveur_nextcloud:
|
serveur_nextcloud:
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,9 @@ serveur_forgejo_service: "forgejo"
|
||||||
|
|
||||||
# Publication (derriere serveur_nginx, TLS a l'edge).
|
# Publication (derriere serveur_nginx, TLS a l'edge).
|
||||||
serveur_forgejo_hostname: "forge.{{ domaine_interne }}"
|
serveur_forgejo_hostname: "forge.{{ domaine_interne }}"
|
||||||
|
# Adresse d'ecoute : 0.0.0.0 car l'edge nginx est sur un AUTRE hote (le distant doit
|
||||||
|
# joindre le backend). A restreindre par pare-feu (nftables) au reseau interne / a l'edge.
|
||||||
|
serveur_forgejo_http_addr: "0.0.0.0"
|
||||||
serveur_forgejo_http_port: 3000
|
serveur_forgejo_http_port: 3000
|
||||||
|
|
||||||
# Base de donnees : resolue depuis le registre par le groupe consommateur.
|
# Base de donnees : resolue depuis le registre par le groupe consommateur.
|
||||||
|
|
@ -28,3 +31,13 @@ serveur_forgejo_internal_token: "{{ vault_forgejo_internal_token | default('') }
|
||||||
serveur_forgejo_admin_user: "forgejo-admin"
|
serveur_forgejo_admin_user: "forgejo-admin"
|
||||||
serveur_forgejo_admin_email: "admin@{{ domaine_interne }}"
|
serveur_forgejo_admin_email: "admin@{{ domaine_interne }}"
|
||||||
serveur_forgejo_admin_password: "{{ vault_forgejo_admin | default('') }}" # rempli depuis la voute (vault_forgejo_admin)
|
serveur_forgejo_admin_password: "{{ vault_forgejo_admin | default('') }}" # rempli depuis la voute (vault_forgejo_admin)
|
||||||
|
|
||||||
|
# --- SSO OIDC (Keycloak, realm applicatif) — « Se connecter avec Chezlepro » ---
|
||||||
|
# Requiert client_pki (confiance step_ca) + résolution de l'hôte Keycloak (plancher/DNS).
|
||||||
|
# Le client OIDC doit exister dans le realm (serveur_keycloak_clients).
|
||||||
|
serveur_forgejo_oidc_actif: true
|
||||||
|
serveur_forgejo_oidc_nom: "chezlepro" # slug du callback : /user/oauth2/<nom>/callback
|
||||||
|
serveur_forgejo_oidc_client_id: "forgejo"
|
||||||
|
serveur_forgejo_oidc_client_secret: "{{ vault_forgejo_oidc | default('') }}" # voûte (vault_forgejo_oidc)
|
||||||
|
serveur_forgejo_oidc_realm: "chezlepro"
|
||||||
|
serveur_forgejo_oidc_discovery: "https://keycloak.{{ domaine_interne }}/realms/{{ serveur_forgejo_oidc_realm }}/.well-known/openid-configuration"
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: app.ini.j2
|
src: app.ini.j2
|
||||||
dest: "{{ serveur_forgejo_config }}"
|
dest: "{{ serveur_forgejo_config }}"
|
||||||
owner: root
|
owner: "{{ serveur_forgejo_utilisateur }}" # Forgejo persiste des secrets générés (oauth2 JWT) → doit pouvoir écrire
|
||||||
group: "{{ serveur_forgejo_utilisateur }}"
|
group: "{{ serveur_forgejo_utilisateur }}"
|
||||||
mode: "0640"
|
mode: "0640"
|
||||||
no_log: true
|
no_log: true
|
||||||
|
|
@ -111,6 +111,16 @@
|
||||||
state: started
|
state: started
|
||||||
daemon_reload: true
|
daemon_reload: true
|
||||||
|
|
||||||
|
- name: Appliquer les redemarrages Forgejo (config a jour) avant admin/OAuth2
|
||||||
|
ansible.builtin.meta: flush_handlers
|
||||||
|
|
||||||
|
- name: Attendre que Forgejo reponde (migrations terminees)
|
||||||
|
ansible.builtin.wait_for:
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: "{{ serveur_forgejo_http_port }}"
|
||||||
|
timeout: 120
|
||||||
|
when: not ansible_check_mode
|
||||||
|
|
||||||
- name: Creer le compte administrateur (une fois)
|
- name: Creer le compte administrateur (une fois)
|
||||||
ansible.builtin.shell:
|
ansible.builtin.shell:
|
||||||
cmd: >-
|
cmd: >-
|
||||||
|
|
@ -126,3 +136,32 @@
|
||||||
environment:
|
environment:
|
||||||
GITEA_WORK_DIR: "{{ serveur_forgejo_data }}"
|
GITEA_WORK_DIR: "{{ serveur_forgejo_data }}"
|
||||||
no_log: true
|
no_log: true
|
||||||
|
|
||||||
|
- name: Configurer la source OAuth2 SSO (Keycloak, idempotent)
|
||||||
|
ansible.builtin.shell:
|
||||||
|
executable: /bin/bash
|
||||||
|
cmd: |
|
||||||
|
set -euo pipefail
|
||||||
|
FJ="{{ serveur_forgejo_binaire }} --config {{ serveur_forgejo_config }}"
|
||||||
|
if $FJ admin auth list 2>/dev/null | grep -qw "{{ serveur_forgejo_oidc_nom }}"; then
|
||||||
|
echo SETOPS_OK
|
||||||
|
else
|
||||||
|
$FJ admin auth add-oauth \
|
||||||
|
--name "{{ serveur_forgejo_oidc_nom }}" \
|
||||||
|
--provider openidConnect \
|
||||||
|
--key "{{ serveur_forgejo_oidc_client_id }}" \
|
||||||
|
--secret "$OIDC_SECRET" \
|
||||||
|
--auto-discover-url "{{ serveur_forgejo_oidc_discovery }}" >/dev/null
|
||||||
|
echo SETOPS_CHANGED
|
||||||
|
fi
|
||||||
|
environment:
|
||||||
|
OIDC_SECRET: "{{ serveur_forgejo_oidc_client_secret }}"
|
||||||
|
GITEA_WORK_DIR: "{{ serveur_forgejo_data }}"
|
||||||
|
become: true
|
||||||
|
become_user: "{{ serveur_forgejo_utilisateur }}"
|
||||||
|
register: serveur_forgejo_oauth
|
||||||
|
changed_when: "'SETOPS_CHANGED' in serveur_forgejo_oauth.stdout"
|
||||||
|
no_log: true
|
||||||
|
when:
|
||||||
|
- serveur_forgejo_oidc_actif | bool
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ WORK_PATH = {{ serveur_forgejo_data }}
|
||||||
[server]
|
[server]
|
||||||
DOMAIN = {{ serveur_forgejo_hostname }}
|
DOMAIN = {{ serveur_forgejo_hostname }}
|
||||||
ROOT_URL = https://{{ serveur_forgejo_hostname }}/
|
ROOT_URL = https://{{ serveur_forgejo_hostname }}/
|
||||||
HTTP_ADDR = 127.0.0.1
|
HTTP_ADDR = {{ serveur_forgejo_http_addr }}
|
||||||
HTTP_PORT = {{ serveur_forgejo_http_port }}
|
HTTP_PORT = {{ serveur_forgejo_http_port }}
|
||||||
SSH_DOMAIN = {{ serveur_forgejo_hostname }}
|
SSH_DOMAIN = {{ serveur_forgejo_hostname }}
|
||||||
|
|
||||||
|
|
@ -24,7 +24,21 @@ SECRET_KEY = {{ serveur_forgejo_secret_key }}
|
||||||
INTERNAL_TOKEN = {{ serveur_forgejo_internal_token }}
|
INTERNAL_TOKEN = {{ serveur_forgejo_internal_token }}
|
||||||
|
|
||||||
[service]
|
[service]
|
||||||
|
{% if serveur_forgejo_oidc_actif %}
|
||||||
|
; Identites depuis l'annuaire via le SSO uniquement : pas d'inscription locale,
|
||||||
|
; mais auto-enregistrement des utilisateurs OIDC.
|
||||||
|
DISABLE_REGISTRATION = false
|
||||||
|
ALLOW_ONLY_EXTERNAL_REGISTRATION = true
|
||||||
|
{% else %}
|
||||||
DISABLE_REGISTRATION = true
|
DISABLE_REGISTRATION = true
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if serveur_forgejo_oidc_actif %}
|
||||||
|
[oauth2_client]
|
||||||
|
ENABLE_AUTO_REGISTRATION = true
|
||||||
|
USERNAME = preferred_username
|
||||||
|
ACCOUNT_LINKING = auto
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
[mailer]
|
[mailer]
|
||||||
ENABLED = true
|
ENABLED = true
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue