Set-OPS-Public/roles/serveur_ops/templates/sonde-console-ops.sh.j2
Daniel Allaire f9a20b0870 console des runners : le code sur le disque n'est pas le code servi
Les trois consoles portaient le moteur du jour et servaient celui du 16 septembre. Le
clone du genome ne notifiait rien et la tache de service demande state: started, qui ne
fait rien quand le service tourne deja ; inventory_gui.py lit son code au demarrage, et
seulement la. Le role retient desormais si le MOTEUR a avance — designe par
serveur_ops_depot_moteur, jamais un chemin recopie — et redemarre la console dans ce seul
cas : un plan qui avance ne coupe pas les pages ouvertes.

La sonde console-ops criait au vestibule ouvert sur les deux runners de locataire, sur
des consoles fermees. Elle frappait la boucle locale, seul endroit d'ou un 200 est le
resultat attendu en mode oidc, ou nginx est reduit a 127.0.0.1 pour qu'on ne contourne
pas la passerelle SSO. Elle connait maintenant son mode : en locale le verdict ne bouge
pas, en oidc la serrure mesuree est l'adresse d'ecoute. Le refus de la passerelle reste
l'affaire de la sonde passerelle de serveur_oauth2_proxy, sur le meme hote.

Valide : --syntax-check sur playbooks/groupes/serveur_ops.yml, ansible-lint sur le role
(profil production, 0 echec, 0 avertissement, 14 fichiers), le gabarit rendu dans ses deux
modes sans reste Jinja et bash -n propre, le filtre d'ecoute eprouve sur cinq formes
d'adresses, l'expression du when sur cinq cas dont le mode check. Le role est applique aux
trois runners, failed=0, et la sonde rejouee sur chacun rend 0.

Limite : le redemarrage automatique ne s'est pas declenche, les clones etant deja au
niveau de la forge — la tache s'est correctement abstenue. La preuve du declenchement
viendra au prochain genome pousse.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 15:27:50 -04:00

99 lines
4.8 KiB
Django/Jinja

#!/bin/bash
# GENERE par Set-OPS (role serveur_ops). Ne pas editer a la main.
#
# SONDE « console-ops » — la console repond-elle, et son vestibule tient-il ?
#
# PAS UNE ENVELOPPE DE GREFFON, ET C'EST ASSUME. `check_http` sait dire « ce service
# repond ». La verite d'ici est l'inverse : « ce service REFUSE-t-il encore ceux qui
# n'ont pas de quoi entrer ». Aucun greffon standard ne pense a mesurer une serrure.
#
# ═══ LA SERRURE N'EST PAS LA MEME DES DEUX COTES (2026-09-20) ═══
#
# Cette sonde a crie au trou de securite pendant cinq jours sur les deux runners de
# locataire : « LE VESTIBULE EST OUVERT », HTTP 200, sur des consoles qui etaient
# parfaitement fermees. Elle frappait `127.0.0.1:{{ serveur_ops_gui_port_public }}` —
# le seul endroit d'ou, en mode `oidc`, un 200 est le resultat ATTENDU.
#
# `locale` — nginx EST le vestibule. Il ecoute pour l'edge, et c'est lui qui demande
# le mot de passe. Un 200 anonyme est alors une porte ouverte : une panne.
# `oidc` — la passerelle SSO est le vestibule, et nginx est reduit a la boucle
# locale pour qu'on ne puisse pas la contourner. Un 200 depuis la boucle
# locale prouve seulement que le relais fonctionne. Ce qu'il faut mesurer
# ici, c'est que PERSONNE D'AUTRE que cette boucle n'atteint ce port.
#
# L'INSTRUMENT DOIT DIRE D'OU IL MESURE. Une sonde qui ne connait pas le mode du service
# qu'elle surveille fabrique des pannes, et une panne permanente finit par ne plus etre
# lue du tout.
#
# CE QU'ELLE NE MESURE PAS, ET POURQUOI. En `oidc`, le refus de la passerelle elle-meme
# est l'affaire de la sonde « passerelle » de `serveur_oauth2_proxy`, posee sur ce meme
# hote. Deux sondes, deux serrures, aucune qui devine le port de l'autre.
set -uo pipefail
GUI={{ serveur_ops_gui_ecoute }}:{{ serveur_ops_gui_port }}
VESTIBULE=127.0.0.1:{{ serveur_ops_gui_port_public }}
PORT_PUBLIC={{ serveur_ops_gui_port_public }}
NOM='{{ serveur_ops_hostname }}'
MODE='{{ serveur_ops_gui_auth }}'
# 1. LE GUI VIT-IL ? On frappe la boucle locale, la ou il est le seul a pouvoir repondre.
code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 "http://${GUI}/" 2>/dev/null)
if [[ "${code}" != "200" ]]; then
echo "La console ne repond pas sur ${GUI} (HTTP ${code:-aucune reponse}) — plus personne ne pilote cet ecosysteme depuis le navigateur."
exit 2
fi
# 2. LE VESTIBULE. On frappe SANS justificatif, avec le bon `Host` : c'est ainsi que nginx
# choisit son vhost, et sans cet en-tete on mesurerait le serveur par defaut — c'est-a-dire
# autre chose.
vu=$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 \
-H "Host: ${NOM}" "http://${VESTIBULE}/" 2>/dev/null)
if [[ "${MODE}" == "oidc" ]]; then
# EN `oidc`, LA SERRURE EST UNE ADRESSE D'ECOUTE, PAS UN CODE HTTP.
#
# `ss -H -lnt` rend une ligne par ecoute, sans en-tete ; la 4e colonne porte l'adresse.
# Tout ce qui n'est pas la boucle locale est une seconde porte sur la console, ouverte
# a qui atteint l'hote — `deployer`, `creer` et `raser` sans passer par Keycloak.
hors_boucle=$(ss -H -lnt "sport = :${PORT_PUBLIC}" 2>/dev/null \
| awk '{print $4}' | grep -vE '^(127\.0\.0\.1|\[::1\]):' || true)
if [[ -n "${hors_boucle}" ]]; then
echo "LE VESTIBULE ECOUTE HORS DE LA BOUCLE LOCALE (${hors_boucle//$'\n'/, }) : en mode oidc la passerelle SSO est la seule porte, et ce port-la la contourne."
exit 2
fi
if [[ "${vu}" == "200" ]]; then
echo "Console vivante, vestibule (oidc) reduit a la boucle locale : le relais repond ici, et ce port n'est joignable de nulle part ailleurs."
exit 0
fi
echo "Le relais local ne sert plus la console : HTTP ${vu:-aucune reponse} sur ${VESTIBULE}. La passerelle SSO n'a plus rien a relayer."
exit 2
fi
# EN `locale`, NGINX EST LE VESTIBULE : c'est lui qui doit refuser.
#
# UN 200 ICI EST LA PIRE DES REPONSES. Il veut dire que le vestibule laisse entrer : la
# console est alors ouverte a quiconque atteint l'adresse, avec `deployer`, `creer` et
# l'edition du plan. On le traite comme une panne, parce que c'en est une.
case "${vu}" in
401|403)
echo "Console vivante, vestibule (${MODE}) en place : une requete anonyme rend HTTP ${vu}."
exit 0
;;
30[12478])
# Une redirection en mode locale signifie que le `auth_basic` n'est plus applique.
echo "LE VESTIBULE NE DEMANDE PLUS RIEN : en mode locale, une requete anonyme devrait rendre 401, elle rend ${vu}."
exit 2
;;
200)
echo "LE VESTIBULE EST OUVERT : une requete anonyme obtient la console (HTTP 200). Deployer, creer et raser sont a la portee de qui trouve l'adresse."
exit 2
;;
"" | 000)
echo "Le vestibule ne repond pas sur ${VESTIBULE} — la console vit, mais l'edge ne peut plus l'atteindre."
exit 2
;;
*)
echo "Reponse inattendue du vestibule : HTTP ${vu}."
exit 1
;;
esac