Set-OPS-Public/roles/serveur_oauth2_proxy/tasks/main.yml
Daniel Allaire 16675037f5 identité : Keycloak 26.7.5, oauth2-proxy v7.15.5 ; les rôles savent monter
- serveur_keycloak : une montée remplace la distribution (arrêt, retrait,
  extraction) au lieu de construire les anciens fichiers sous le nouveau repère
- serveur_oauth2_proxy : repère = version du binaire ; empreinte SHA-256
  épinglée, archive refusée et retirée du cache si elle diffère
- CHANGELOG 67

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 23:32:10 -04:00

247 lines
10 KiB
YAML

---
# L'ISSUER SE DERIVE DE L'EXPOSITION DECLAREE, il ne s'invente pas. Le defaut fabriquait
# `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-la que PowerDNS resout et
# que nginx sert. Resultat le 2026-08-07 : `lookup keycloak.chezlepro.internal : no such
# host`, et oauth2-proxy redemarrait en boucle.
#
# Le champ `expose` est deja la source pour PowerDNS, nginx et /etc/hosts. C'est la
# quatrieme fois qu'on le lit : une convention de nommage se contredit, une declaration
# se corrige.
- 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"
when: serveur_oauth2_proxy_issuer_derive | bool
- name: Dériver l'issuer depuis l'exposition de Keycloak
ansible.builtin.set_fact:
serveur_oauth2_proxy_issuer: >-
https://{{ (applications | default({})).get(serveur_oauth2_proxy_app_idp, {}).get('expose', []) | first }}/realms/{{ serveur_oauth2_proxy_realm }}
when:
- serveur_oauth2_proxy_issuer_derive | bool
- ((applications | default({})).get(serveur_oauth2_proxy_app_idp, {}).get('expose', []) | length) > 0
- name: Exiger la configuration OIDC (client, secret, redirect, upstream, cookie)
ansible.builtin.assert:
that:
- serveur_oauth2_proxy_client_id | length > 0
- serveur_oauth2_proxy_client_secret | length > 0
- serveur_oauth2_proxy_redirect_url | length > 0
- serveur_oauth2_proxy_upstream | length > 0
- serveur_oauth2_proxy_cookie_secret | length > 0
fail_msg: "client_id, client_secret (voûte), redirect_url, upstream et cookie_secret (voûte) requis."
# Garde d'ENCODAGE, distincte de la garde de présence : un secret présent mais en base64
# standard fait redémarrer oauth2-proxy en boucle, avec un message qui parle de longueur
# et jamais d'encodage. Mieux vaut refuser ici, où la cause est nommable.
- name: Refuser un cookie_secret qui n'est pas du base64 url-safe
ansible.builtin.assert:
that:
- "'+' not in serveur_oauth2_proxy_cookie_secret"
- "'/' not in serveur_oauth2_proxy_cookie_secret"
fail_msg: >-
`vault_oauth2_cookie` contient `+` ou `/` : c'est du base64 STANDARD. oauth2-proxy
décode en base64 URL-SAFE et retombera sur la chaîne brute, dont la longueur sera
refusée. Régénérer : openssl rand -base64 32 | tr '+/' '-_'
- name: Créer l'utilisateur système oauth2-proxy
ansible.builtin.user:
name: "{{ serveur_oauth2_proxy_utilisateur }}"
system: true
shell: /usr/sbin/nologin
create_home: false
# Le chemin de destination PORTE la version : cet artefact est immuable une fois pose.
# Sans garde, `get_url` recontacte le serveur distant a CHAQUE deploiement — et un
# serveur tiers lent suffit alors a faire tomber un deploiement de flotte. Constate le
# 2026-08-09 sur le binaire Forgejo, meme motif ici.
#
# LE REPERE EST LE BINAIRE INSTALLE, PAS L'ARCHIVE (2026-09-28). On regardait l'archive
# dans /tmp : /tmp se vide, et elle repartait du controleur a chaque deploiement (`changed`
# sans raison). Le binaire est exactement ce que l'extraction attend (`creates`) : present,
# il n'y a rien a apporter. Meme correction que `serveur_keycloak`.
#
# LA VERSION, PAS LA SEULE PRESENCE (2026-10-03). Le repere etait l'existence du binaire :
# changer `serveur_oauth2_proxy_version` sur une machine deja installee ne faisait RIEN —
# ni telechargement, ni extraction. On demande donc au binaire sa version.
- name: Quelle version d'oauth2-proxy est en place ?
ansible.builtin.command:
argv: ["{{ serveur_oauth2_proxy_binaire }}", "--version"]
register: serveur_oauth2_proxy_version_posee
changed_when: false
failed_when: false
check_mode: false
- name: L artefact de cette version est-il deja pose ?
ansible.builtin.set_fact:
serveur_oauth2_proxy_a_jour: >-
{{ (serveur_oauth2_proxy_version_posee.rc | default(1) == 0)
and ((serveur_oauth2_proxy_version ~ ' ') in (serveur_oauth2_proxy_version_posee.stdout | default('') ~ ' ')) }}
# Le controleur telecharge, puis pousse par SSH — la cible ne tire jamais d'Internet.
# Motif explique en detail dans `roles/serveur_nextcloud/tasks/installer.yml`.
- name: Cache d artefacts du contrôleur
ansible.builtin.file:
path: "{{ serveur_oauth2_proxy_cache_local }}"
state: directory
mode: "0700"
delegate_to: localhost
become: false
when: not (serveur_oauth2_proxy_a_jour | bool)
# --- LE DEPOT DU SITE D'ABORD, L'INTERNET ENSUITE (2026-09-12) ----------------
#
# Le site tient ces artefacts dans son cache (`serveur_artefacts`, LocalDirs). Un runner
# qui les y trouve ne sort pas du tout : c'est le meme principe que le cache apt, applique
# aux publications qui ne vivent dans aucun depot apt.
#
# PLACEE AVANT LE `stat` DU CACHE DU CONTROLEUR, ET C'EST TOUT LE MONTAGE : si le depot
# sert le fichier, le `stat` qui suit le voit, et la tache de telechargement amont se
# saute d'elle-meme. Aucune tache existante n'a change.
#
# `failed_when: false` — le depot est une COMMODITE. Site sans depot, depot qui n'a pas
# encore cette version, cache du site tout neuf : on retombe sur l'amont, comme avant.
- name: Prendre l'archive oauth2-proxy au dépôt du site, avant de sortir sur Internet
ansible.builtin.get_url:
url: "{{ setops_depot_binaires }}/oauth2-proxy-{{ serveur_oauth2_proxy_version }}.tar.gz"
dest: "{{ serveur_oauth2_proxy_cache_local }}/oauth2-proxy-{{ serveur_oauth2_proxy_version }}.tar.gz"
mode: "0644"
timeout: 30
delegate_to: localhost
become: false
failed_when: false
when:
- setops_depot_binaires | default('') | length > 0
- not (serveur_oauth2_proxy_a_jour | bool)
- name: L artefact est-il déjà dans le cache du contrôleur ?
ansible.builtin.stat:
path: "{{ serveur_oauth2_proxy_cache_local }}/oauth2-proxy-{{ serveur_oauth2_proxy_version }}.tar.gz"
register: serveur_oauth2_proxy_cache_present
delegate_to: localhost
become: false
when: not (serveur_oauth2_proxy_a_jour | bool)
- name: Télécharger oauth2-proxy dans le cache du contrôleur (une seule fois)
ansible.builtin.get_url:
url: "{{ serveur_oauth2_proxy_url }}"
dest: "{{ serveur_oauth2_proxy_cache_local }}/oauth2-proxy-{{ serveur_oauth2_proxy_version }}.tar.gz"
mode: "0644"
delegate_to: localhost
become: false
when:
- not (serveur_oauth2_proxy_a_jour | bool)
- not (serveur_oauth2_proxy_cache_present.stat.exists | default(false))
# QUELLE QUE SOIT LA PROVENANCE — depot du site ou Internet — l'archive du cache doit avoir
# l'empreinte relue (defaults/main.yml). Une archive qui ne l'a pas est retiree du cache,
# pour ne pas la reproposer au passage suivant.
- name: Mesurer l'archive oauth2-proxy du cache
ansible.builtin.stat:
path: "{{ serveur_oauth2_proxy_cache_local }}/oauth2-proxy-{{ serveur_oauth2_proxy_version }}.tar.gz"
checksum_algorithm: sha256
delegate_to: localhost
become: false
register: serveur_oauth2_proxy_archive_mesuree
when: not (serveur_oauth2_proxy_a_jour | bool)
- name: Retirer du cache une archive dont l'empreinte ne correspond pas
ansible.builtin.file:
path: "{{ serveur_oauth2_proxy_cache_local }}/oauth2-proxy-{{ serveur_oauth2_proxy_version }}.tar.gz"
state: absent
delegate_to: localhost
become: false
when:
- not (serveur_oauth2_proxy_a_jour | bool)
- serveur_oauth2_proxy_archive_mesuree.stat.exists | default(false)
- serveur_oauth2_proxy_archive_mesuree.stat.checksum | default('') != serveur_oauth2_proxy_sha256
- name: Refuser une archive oauth2-proxy dont l'empreinte ne correspond pas
ansible.builtin.assert:
that:
- serveur_oauth2_proxy_archive_mesuree.stat.checksum | default('') == serveur_oauth2_proxy_sha256
fail_msg: >-
L'archive oauth2-proxy {{ serveur_oauth2_proxy_version }} n'a pas l'empreinte relue
({{ serveur_oauth2_proxy_sha256[:12] }}…) : obtenue
{{ (serveur_oauth2_proxy_archive_mesuree.stat.checksum | default('absente'))[:12] }}.
Elle a ete retiree du cache. Ne pas contourner : relire la publication avant de
changer l'empreinte.
when: not (serveur_oauth2_proxy_a_jour | bool)
- name: Déposer oauth2-proxy sur l hôte depuis le cache
ansible.builtin.copy:
src: "{{ serveur_oauth2_proxy_cache_local }}/oauth2-proxy-{{ serveur_oauth2_proxy_version }}.tar.gz"
dest: "/tmp/oauth2-proxy-{{ serveur_oauth2_proxy_version }}.tar.gz"
mode: "0644"
when: not (serveur_oauth2_proxy_a_jour | bool)
- name: Extraire le binaire oauth2-proxy
ansible.builtin.unarchive:
src: "/tmp/oauth2-proxy-{{ serveur_oauth2_proxy_version }}.tar.gz"
dest: /usr/local/bin
remote_src: true
extra_opts: ["--strip-components=1", "--wildcards", "*/oauth2-proxy"]
owner: root
group: root
mode: "0755"
# En simulation, l'archive n'a pas ete copiee dans /tmp : rien a extraire.
when:
- not (serveur_oauth2_proxy_a_jour | bool)
- not ansible_check_mode
notify: Redémarrer oauth2-proxy
- name: Créer le répertoire de configuration
ansible.builtin.file:
path: "{{ serveur_oauth2_proxy_config | dirname }}"
state: directory
owner: root
group: "{{ serveur_oauth2_proxy_utilisateur }}"
mode: "0750"
- name: Déployer la configuration oauth2-proxy
ansible.builtin.template:
src: oauth2-proxy.cfg.j2
dest: "{{ serveur_oauth2_proxy_config }}"
owner: root
group: "{{ serveur_oauth2_proxy_utilisateur }}"
mode: "0640"
no_log: true
notify: Redémarrer oauth2-proxy
- name: Déployer l'unité systemd oauth2-proxy
ansible.builtin.template:
src: oauth2-proxy.service.j2
dest: /etc/systemd/system/oauth2-proxy.service
owner: root
group: root
mode: "0644"
notify: Redémarrer oauth2-proxy
- name: Activer et démarrer oauth2-proxy
when: not ansible_check_mode
ansible.builtin.systemd:
name: "{{ serveur_oauth2_proxy_service }}"
enabled: true
state: started
daemon_reload: true
# LE ROLE QUI POSSEDE LA VERITE DEPOSE SA PROPRE SONDE. `client_sante` la fait tourner et
# pousse le resultat ; les greffons standard, eux, sont poses par le porteur.
- name: Assurer le repertoire des sondes de supervision
ansible.builtin.file:
path: /usr/local/lib/setops/sondes
state: directory
owner: root
group: root
mode: "0755"
- name: Deposer la sonde « passerelle »
ansible.builtin.template:
src: sonde-passerelle.sh.j2
dest: /usr/local/lib/setops/sondes/passerelle.sh
owner: root
group: root
mode: "0750"