Trouve par le passage d'idempotence : « Connection failure: The read operation timed out » sur le telechargement du binaire — 106 Mo deja presents sur la machine. get_url n'avait aucune garde (ni checksum, ni condition d'existence), alors que le chemin de destination PORTE la version : forgejo-10.0.0 ne peut pas designer un autre contenu demain. Chaque deploiement recontactait donc un serveur tiers pour un fichier immuable. Un deploiement de flotte echouait parce qu'un serveur externe etait lent. Sur une plateforme qui se veut souveraine, c'est une dependance de trop sur le chemin critique. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
260 lines
9.6 KiB
YAML
260 lines
9.6 KiB
YAML
---
|
|
|
|
# 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
|
|
# `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
|
|
# alors qu'elle reste ouverte. On refuse plutot que de laisser croire.
|
|
ansible.builtin.assert:
|
|
that:
|
|
- serveur_forgejo_version is version('10.0.0', '>=')
|
|
fail_msg: >-
|
|
serveur_forgejo_connexion_locale=false exige Forgejo >= 10.0.0
|
|
(ici {{ serveur_forgejo_version }}) : le reglage serait ignore sans erreur.
|
|
Mettre a jour, ou assumer la connexion locale en la passant a true.
|
|
when: not (serveur_forgejo_connexion_locale | bool)
|
|
|
|
- name: Exiger les secrets Forgejo (Vault)
|
|
ansible.builtin.assert:
|
|
that:
|
|
- serveur_forgejo_secret_key | length > 0
|
|
- serveur_forgejo_internal_token | length > 0
|
|
- serveur_forgejo_admin_password | length > 0
|
|
fail_msg: "secret_key, internal_token et admin_password sont requis (Ansible Vault)."
|
|
|
|
- name: Resoudre la base de donnees depuis le registre (role partage)
|
|
ansible.builtin.include_role:
|
|
name: resoudre_base
|
|
vars:
|
|
resoudre_base_groupe: "{{ serveur_forgejo_groupe }}"
|
|
|
|
- name: Adopter les facts de base pour Forgejo
|
|
ansible.builtin.set_fact:
|
|
serveur_forgejo_entree: "{{ resoudre_base_entree }}"
|
|
serveur_forgejo_db_password: "{{ resoudre_base_db_password }}"
|
|
serveur_forgejo_db_host: "{{ resoudre_base_db_host }}"
|
|
serveur_forgejo_db_port: "{{ resoudre_base_db_port }}"
|
|
no_log: true
|
|
|
|
- name: Creer l'utilisateur systeme git
|
|
ansible.builtin.user:
|
|
name: "{{ serveur_forgejo_utilisateur }}"
|
|
system: true
|
|
shell: /bin/bash
|
|
home: "/home/{{ serveur_forgejo_utilisateur }}"
|
|
create_home: true
|
|
comment: "Git Version Control"
|
|
|
|
# Le chemin de destination PORTE la version : `forgejo-10.0.0` ne peut pas designer un
|
|
# autre contenu demain. Le binaire est donc immuable une fois pose, et le retelecharger
|
|
# n'a aucun sens — or `get_url` sans garde recontacte le serveur distant a CHAQUE
|
|
# deploiement, pour 106 Mo deja presents.
|
|
#
|
|
# Constate le 2026-08-09 pendant le passage d'idempotence : « Connection failure: The
|
|
# read operation timed out ». Un deploiement de flotte echouait parce qu'un serveur tiers
|
|
# etait lent — sur une plateforme qui se veut souveraine, c'est une dependance de trop
|
|
# sur le chemin critique.
|
|
- name: Le binaire de cette version est-il deja pose ?
|
|
ansible.builtin.stat:
|
|
path: "/usr/local/bin/forgejo-{{ serveur_forgejo_version }}"
|
|
register: serveur_forgejo_binaire_present
|
|
|
|
- name: Telecharger le binaire Forgejo
|
|
when: not serveur_forgejo_binaire_present.stat.exists
|
|
ansible.builtin.get_url:
|
|
url: "{{ serveur_forgejo_url }}"
|
|
dest: "/usr/local/bin/forgejo-{{ serveur_forgejo_version }}"
|
|
owner: root
|
|
group: root
|
|
mode: "0755"
|
|
|
|
- name: Activer la version courante du binaire
|
|
ansible.builtin.file:
|
|
src: "/usr/local/bin/forgejo-{{ serveur_forgejo_version }}"
|
|
dest: "{{ serveur_forgejo_binaire }}"
|
|
state: link
|
|
notify: Redemarrer forgejo
|
|
|
|
- name: Creer les repertoires de donnees
|
|
ansible.builtin.file:
|
|
path: "{{ serveur_forgejo_data }}/{{ item }}"
|
|
state: directory
|
|
owner: "{{ serveur_forgejo_utilisateur }}"
|
|
group: "{{ serveur_forgejo_utilisateur }}"
|
|
mode: "0750"
|
|
loop:
|
|
- ""
|
|
- custom
|
|
- data
|
|
- log
|
|
|
|
- name: Creer le repertoire de configuration
|
|
ansible.builtin.file:
|
|
path: "{{ serveur_forgejo_config_dir }}"
|
|
state: directory
|
|
owner: root
|
|
group: "{{ serveur_forgejo_utilisateur }}"
|
|
mode: "0770"
|
|
|
|
- name: Deployer app.ini
|
|
ansible.builtin.template:
|
|
src: app.ini.j2
|
|
dest: "{{ serveur_forgejo_config }}"
|
|
owner: "{{ serveur_forgejo_utilisateur }}" # Forgejo persiste des secrets générés (oauth2 JWT) → doit pouvoir écrire
|
|
group: "{{ serveur_forgejo_utilisateur }}"
|
|
mode: "0640"
|
|
no_log: true
|
|
notify: Redemarrer forgejo
|
|
|
|
- name: Deployer l'identite visuelle (logo, favicon, accent aurore, accueil)
|
|
ansible.builtin.copy:
|
|
src: custom/
|
|
dest: "{{ serveur_forgejo_data }}/custom/"
|
|
owner: "{{ serveur_forgejo_utilisateur }}"
|
|
group: "{{ serveur_forgejo_utilisateur }}"
|
|
mode: "0644"
|
|
directory_mode: "0755"
|
|
when: serveur_forgejo_branding | bool
|
|
notify: Redemarrer forgejo
|
|
|
|
- name: Deployer l'unite systemd forgejo
|
|
ansible.builtin.template:
|
|
src: forgejo.service.j2
|
|
dest: /etc/systemd/system/forgejo.service
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Redemarrer forgejo
|
|
|
|
- name: Activer et demarrer Forgejo
|
|
when: not ansible_check_mode
|
|
ansible.builtin.systemd:
|
|
name: "{{ serveur_forgejo_service }}"
|
|
enabled: true
|
|
state: started
|
|
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)
|
|
ansible.builtin.shell:
|
|
cmd: >-
|
|
{{ serveur_forgejo_binaire }} admin user create
|
|
--admin --username {{ serveur_forgejo_admin_user }}
|
|
--email {{ serveur_forgejo_admin_email }}
|
|
--password '{{ serveur_forgejo_admin_password }}'
|
|
--config {{ serveur_forgejo_config }}
|
|
&& touch {{ serveur_forgejo_data }}/.admin-created
|
|
creates: "{{ serveur_forgejo_data }}/.admin-created"
|
|
become: true
|
|
become_user: "{{ serveur_forgejo_utilisateur }}"
|
|
environment:
|
|
GITEA_WORK_DIR: "{{ serveur_forgejo_data }}"
|
|
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 }}"
|
|
# Les reglages de GROUPE sont RECONCILIES, pas seulement poses a la creation :
|
|
# `add-oauth` une fois puis plus rien laissait un cablage perime vivre
|
|
# indefiniment — c'est ce qui est arrive a la federation LDAP de Keycloak, qui
|
|
# a pointe un hote inexistant pendant des semaines (2026-08-07).
|
|
OPTS_GROUPE=""
|
|
{% if serveur_forgejo_oidc_groupe_admin | length > 0 %}
|
|
OPTS_GROUPE="--group-claim-name {{ serveur_forgejo_oidc_groupe_claim }} --admin-group {{ serveur_forgejo_oidc_groupe_admin }}"
|
|
{% endif %}
|
|
ID=$($FJ admin auth list 2>/dev/null \
|
|
| awk -v n="{{ serveur_forgejo_oidc_nom }}" '$2 == n {print $1}' | head -1)
|
|
if [ -z "$ID" ]; then
|
|
$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 }}" \
|
|
$OPTS_GROUPE >/dev/null
|
|
echo SETOPS_CHANGED
|
|
else
|
|
# `update-oauth` est idempotent cote Forgejo : il reecrit les memes valeurs
|
|
# sans effet de bord. On ne peut pas comparer avant/apres (la CLI n'expose
|
|
# pas le detail d'une source), donc on ne signale PAS `changed`.
|
|
# `--secret` fait partie de la reconciliation : sans lui, faire tourner
|
|
# `vault_forgejo_oidc` laissait Forgejo sur l'ancienne valeur.
|
|
$FJ admin auth update-oauth --id "$ID" \
|
|
--auto-discover-url "{{ serveur_forgejo_oidc_discovery }}" \
|
|
--secret "$OIDC_SECRET" \
|
|
$OPTS_GROUPE >/dev/null
|
|
echo SETOPS_OK
|
|
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
|
|
|
|
# --- ROTATION DU COMPTE DE SECOURS -------------------------------------------
|
|
# `admin user create` porte `creates: .admin-created` : il ne tourne qu'UNE fois.
|
|
# Changer la voute ne changeait donc rien, et la voute se mettait a mentir. Meme
|
|
# raisonnement que pour Grafana — idempotence par EMPREINTE du secret applique.
|
|
- name: Empreinte du mot de passe admin en place
|
|
ansible.builtin.slurp:
|
|
src: "{{ serveur_forgejo_marqueur_admin }}"
|
|
register: serveur_forgejo_marque
|
|
failed_when: false
|
|
changed_when: false
|
|
no_log: true
|
|
|
|
- name: Appliquer le mot de passe du compte de secours (rotation)
|
|
ansible.builtin.command:
|
|
argv:
|
|
- "{{ serveur_forgejo_binaire }}"
|
|
- "--config"
|
|
- "{{ serveur_forgejo_config }}"
|
|
- admin
|
|
- user
|
|
- change-password
|
|
- "--username"
|
|
- "{{ serveur_forgejo_admin_user }}"
|
|
- "--password"
|
|
- "{{ serveur_forgejo_admin_password }}"
|
|
environment:
|
|
GITEA_WORK_DIR: "{{ serveur_forgejo_data }}"
|
|
become: true
|
|
become_user: "{{ serveur_forgejo_utilisateur }}"
|
|
changed_when: true
|
|
no_log: true
|
|
when: >-
|
|
(serveur_forgejo_marque.content | default('') | b64decode | trim)
|
|
!= (serveur_forgejo_admin_password | hash('sha256'))
|
|
|
|
- name: Retenir l'empreinte appliquee
|
|
ansible.builtin.copy:
|
|
content: "{{ serveur_forgejo_admin_password | hash('sha256') }}\n"
|
|
dest: "{{ serveur_forgejo_marqueur_admin }}"
|
|
owner: root
|
|
group: root
|
|
mode: "0600"
|
|
no_log: true
|