la console d exploitation devient un service, et elle n avait aucune serrure
Le GUI n a aucune authentification : GET / sert la page a qui la demande, jeton
ecrit dedans, et POST /api exige ce jeton que la page vient de donner. Le jeton
garde contre le CSRF, pas contre un visiteur — la seule serrure est
--hote 127.0.0.1. Ce qu il offre a qui entre : deployer, creer, raser, editer le
plan. La fabric entiere.
Le service reste donc sur la boucle locale. Ce qui est publie est un nginx local
qui authentifie d abord : oidc par defaut (oauth2-proxy, donc un groupe
d annuaire qu on revoque sans deploiement), locale en repli pour un ecosysteme
sans annuaire. L authentification est posee au niveau du server, pas d un
location.
La sonde console-ops mesure une SERRURE : une requete anonyme doit etre REFUSEE.
Un 200 y est la pire des reponses, et il ne fait echouer personne.
P54 a attrape une contrainte ratee : serveur_ops est insemine par le SITE, qui
ne detient pas la voute du locataire. Le role ne nomme donc aucune voute — il
declare un parametre, et la couche qui detient le secret le remplit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-14 19:27:28 -04:00
#!/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.
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
#
# ═══ 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.
la console d exploitation devient un service, et elle n avait aucune serrure
Le GUI n a aucune authentification : GET / sert la page a qui la demande, jeton
ecrit dedans, et POST /api exige ce jeton que la page vient de donner. Le jeton
garde contre le CSRF, pas contre un visiteur — la seule serrure est
--hote 127.0.0.1. Ce qu il offre a qui entre : deployer, creer, raser, editer le
plan. La fabric entiere.
Le service reste donc sur la boucle locale. Ce qui est publie est un nginx local
qui authentifie d abord : oidc par defaut (oauth2-proxy, donc un groupe
d annuaire qu on revoque sans deploiement), locale en repli pour un ecosysteme
sans annuaire. L authentification est posee au niveau du server, pas d un
location.
La sonde console-ops mesure une SERRURE : une requete anonyme doit etre REFUSEE.
Un 200 y est la pire des reponses, et il ne fait echouer personne.
P54 a attrape une contrainte ratee : serveur_ops est insemine par le SITE, qui
ne detient pas la voute du locataire. Le role ne nomme donc aucune voute — il
declare un parametre, et la couche qui detient le secret le remplit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-14 19:27:28 -04:00
set -uo pipefail
GUI={{ serveur_ops_gui_ecoute }}:{{ serveur_ops_gui_port }}
VESTIBULE=127.0.0.1:{{ serveur_ops_gui_port_public }}
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
PORT_PUBLIC={{ serveur_ops_gui_port_public }}
la console d exploitation devient un service, et elle n avait aucune serrure
Le GUI n a aucune authentification : GET / sert la page a qui la demande, jeton
ecrit dedans, et POST /api exige ce jeton que la page vient de donner. Le jeton
garde contre le CSRF, pas contre un visiteur — la seule serrure est
--hote 127.0.0.1. Ce qu il offre a qui entre : deployer, creer, raser, editer le
plan. La fabric entiere.
Le service reste donc sur la boucle locale. Ce qui est publie est un nginx local
qui authentifie d abord : oidc par defaut (oauth2-proxy, donc un groupe
d annuaire qu on revoque sans deploiement), locale en repli pour un ecosysteme
sans annuaire. L authentification est posee au niveau du server, pas d un
location.
La sonde console-ops mesure une SERRURE : une requete anonyme doit etre REFUSEE.
Un 200 y est la pire des reponses, et il ne fait echouer personne.
P54 a attrape une contrainte ratee : serveur_ops est insemine par le SITE, qui
ne detient pas la voute du locataire. Le role ne nomme donc aucune voute — il
declare un parametre, et la couche qui detient le secret le remplit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-14 19:27:28 -04:00
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
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
# 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.
la console d exploitation devient un service, et elle n avait aucune serrure
Le GUI n a aucune authentification : GET / sert la page a qui la demande, jeton
ecrit dedans, et POST /api exige ce jeton que la page vient de donner. Le jeton
garde contre le CSRF, pas contre un visiteur — la seule serrure est
--hote 127.0.0.1. Ce qu il offre a qui entre : deployer, creer, raser, editer le
plan. La fabric entiere.
Le service reste donc sur la boucle locale. Ce qui est publie est un nginx local
qui authentifie d abord : oidc par defaut (oauth2-proxy, donc un groupe
d annuaire qu on revoque sans deploiement), locale en repli pour un ecosysteme
sans annuaire. L authentification est posee au niveau du server, pas d un
location.
La sonde console-ops mesure une SERRURE : une requete anonyme doit etre REFUSEE.
Un 200 y est la pire des reponses, et il ne fait echouer personne.
P54 a attrape une contrainte ratee : serveur_ops est insemine par le SITE, qui
ne detient pas la voute du locataire. Le role ne nomme donc aucune voute — il
declare un parametre, et la couche qui detient le secret le remplit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-14 19:27:28 -04:00
#
# 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])
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
# Une redirection en mode locale signifie que le `auth_basic` n'est plus applique.
la console d exploitation devient un service, et elle n avait aucune serrure
Le GUI n a aucune authentification : GET / sert la page a qui la demande, jeton
ecrit dedans, et POST /api exige ce jeton que la page vient de donner. Le jeton
garde contre le CSRF, pas contre un visiteur — la seule serrure est
--hote 127.0.0.1. Ce qu il offre a qui entre : deployer, creer, raser, editer le
plan. La fabric entiere.
Le service reste donc sur la boucle locale. Ce qui est publie est un nginx local
qui authentifie d abord : oidc par defaut (oauth2-proxy, donc un groupe
d annuaire qu on revoque sans deploiement), locale en repli pour un ecosysteme
sans annuaire. L authentification est posee au niveau du server, pas d un
location.
La sonde console-ops mesure une SERRURE : une requete anonyme doit etre REFUSEE.
Un 200 y est la pire des reponses, et il ne fait echouer personne.
P54 a attrape une contrainte ratee : serveur_ops est insemine par le SITE, qui
ne detient pas la voute du locataire. Le role ne nomme donc aucune voute — il
declare un parametre, et la couche qui detient le secret le remplit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-14 19:27:28 -04:00
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