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
This commit is contained in:
parent
5ecce4282a
commit
627ef00ec7
12 changed files with 542 additions and 10 deletions
76
CHANGELOG.md
76
CHANGELOG.md
|
|
@ -1,5 +1,81 @@
|
|||
# CHANGELOG — Set-OPS
|
||||
|
||||
## 2026-09-14 (20) — La console d'exploitation devient un service, et elle n'avait aucune serrure
|
||||
|
||||
Decision de l'exploitant : *« j'aimerais que chaque runner expose le GUI de Set-OPS a
|
||||
travers ce edge »*. La construire a d'abord demande de mesurer ce qu'on allait publier.
|
||||
|
||||
### CE QUI A CHANGE LA FORME DE TOUT LE RESTE
|
||||
|
||||
**Le GUI n'a AUCUNE authentification.** Mesure du 2026-09-14, dans son propre code :
|
||||
|
||||
GET / sert la page A QUI LA DEMANDE, avec le jeton ECRIT DEDANS
|
||||
POST /api/* exige ce jeton — que la page vient de donner a tout le monde
|
||||
|
||||
Le jeton est une garde **CSRF**, pas une serrure. La seule serrure est
|
||||
`--hote 127.0.0.1` : le GUI est sur parce qu'il n'est joignable que de sa propre machine.
|
||||
|
||||
Ce qu'il offre a qui entre : `deployer`, `creer`, `instance-creer`, `instance-utiliser`,
|
||||
`pousser`, et l'edition du plan. **La fabric entiere.** L'exposer tel quel aurait livre
|
||||
un ecosysteme a quiconque trouve l'URL.
|
||||
|
||||
### Ce qui est construit
|
||||
|
||||
Le service reste sur la boucle locale, **toujours**. Ce qui est publie est un nginx local
|
||||
qui authentifie D'ABORD et relaie ensuite.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| `setops-gui.service` | le GUI, `127.0.0.1:8765`, sous le compte `setops`, dans le venv du runner |
|
||||
| nginx local | port declare au plan, `server_name` derive du plan (P67) |
|
||||
| vestibule | `oidc` (oauth2-proxy -> Keycloak) **par defaut**, `locale` (HTTP Basic) en repli |
|
||||
| flux | `ingress [edge, admin]` — la meme paire que Grafana et la vigie, pour la meme raison |
|
||||
| sonde | `console-ops` |
|
||||
|
||||
`oidc` PAR DEFAUT, ET CE N'EST PAS UNE PREFERENCE. Cette console peut raser un
|
||||
ecosysteme. Un mot de passe partage devant ce pouvoir est un accident qui attend ; un
|
||||
groupe d'annuaire se revoque sans deploiement (D-66). `locale` existe pour un ecosysteme
|
||||
qui n'a pas d'annuaire — un SITE — et c'est ecrit comme un repli, pas comme un choix.
|
||||
|
||||
L'AUTHENTIFICATION EST POSEE AU NIVEAU DU `server`, pas d'un `location` : placee dans le
|
||||
seul bloc de relais, elle laisserait passer tout chemin qu'un `location` plus specifique
|
||||
attraperait en premier. Meme geste que pour la vigie, et meme raison.
|
||||
|
||||
DEUX REGLAGES QUI NE SONT PAS DU CONFORT. `proxy_read_timeout` a 3600 s : un deploiement
|
||||
dure, et le delai par defaut de nginx couperait la reponse au milieu — la page dirait
|
||||
echec pendant que la fabric continue. `proxy_buffering off` : sans lui, la sortie d'un
|
||||
deploiement arriverait d'un bloc a la fin, et la console resterait muette des minutes.
|
||||
|
||||
### La sonde mesure une SERRURE, ce qu'aucun greffon ne pense a faire
|
||||
|
||||
`console-ops` verifie deux choses, et la seconde est l'inverse d'une sonde ordinaire :
|
||||
|
||||
1. le GUI repond sur la boucle locale -> sinon plus personne ne pilote
|
||||
2. une requete ANONYME au vestibule est REFUSEE (401/403, ou renvoi en `oidc`)
|
||||
|
||||
**Un 200 y est la pire des reponses.** Il veut dire que le vestibule laisse entrer. Ca ne
|
||||
fait echouer personne — c'est exactement pourquoi il faut le mesurer.
|
||||
|
||||
### P54 a attrape une contrainte que j'avais ratee
|
||||
|
||||
`serveur_ops` est un role **d'insemination** : le SITE le pose sur le runner d'un
|
||||
locataire qui vient de naitre, **sans detenir la voute de ce locataire**. Citer
|
||||
`vault_setops_gui_admin` dans ce role rendait l'insemination impossible — le site
|
||||
reclamait un secret qu'il n'a pas, et par construction ne doit pas avoir.
|
||||
|
||||
serveur_ops cite vault_setops_gui_admin dans roles/serveur_ops/defaults/main.yml :
|
||||
le SITE ne detient pas cette voute
|
||||
|
||||
Le role declare donc un PARAMETRE vide, et la couche qui detient le secret est la seule a
|
||||
le nommer. La garde a tenu deux fois : la seconde, le nom ne survivait plus que dans le
|
||||
TEXTE d'un message d'erreur — ou il etait de toute facon faux, puisque la voute varie
|
||||
selon qui publie.
|
||||
|
||||
### Ce qui reste
|
||||
|
||||
Le site n'a pas d'edge. L'exploitant vient d'en autoriser un : il publiera l'observatoire,
|
||||
la vigie et la console du site sous leurs noms, en TLS. C'est la suite.
|
||||
|
||||
## 2026-09-14 (19) — Une console pour le site, et un repli qui ouvrait vers l'Internet
|
||||
|
||||
Le site calculait 88 verdicts que personne ne pouvait lire. Il a desormais sa console —
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
| # | Preuve | Affirmations | Statut | Detail |
|
||||
|---|---|---|---|---|
|
||||
| P01 | Lint (ansible-lint) | AFF-006 | ✅ OK | [0m[0m |
|
||||
| P01 | Lint (ansible-lint) | AFF-006 | ✅ OK | } \| to_nice_json }}`. |
|
||||
| P02 | Tests unitaires (inventaire, raser, ecritures du plan, rendu du GUI) | — | ✅ OK | OK |
|
||||
| P03 | Diff-vide du plan — TOUTES les instances | AFF-001, AFF-004, AFF-030, AFF-031, AFF-032 | ✅ OK | 5 instance(s) verifiee(s) — instance-ci-1646753, OPS-Chezlepro-lab, OPS-Chezlepro, OPS-Technolibre, OPS-Patient0 : plan et inventaire applique coincident. |
|
||||
| P04 | Groupes <-> playbooks homonymes | AFF-008 | ✅ OK | |
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
| P06 | Validateurs de registres (serveurs/apps/bases/domaines) | AFF-003 | ✅ OK | Registre des domaines valide. |
|
||||
| P07 | GUI (node --check) | AFF-033 | ✅ OK | JS du GUI : syntaxe valide (node --check), 1 nom(s) surveille(s) sans reference orpheline. |
|
||||
| P08 | Orchestration (couches + graphe) | AFF-070 | ✅ OK | Orchestration coherente : 41 groupes classes, aucun cycle, aucune arete en arriere. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 39 rôles, 110 flux, schéma + matrice OK. |
|
||||
| P09 | Flux reseau (schema + matrice) | AFF-071 | ✅ OK | Flux coherents : 39 rôles, 111 flux, schéma + matrice OK. |
|
||||
| P10 | Handlers <-> notify | AFF-034, AFF-035 | ✅ OK | Tout notify pointe vers un handler du meme role (49 roles). |
|
||||
| P11 | Syntaxe des playbooks (--syntax-check) | AFF-083 | ✅ OK | serveur_resolveur_site |
|
||||
| P12 | Existence des runbooks cites | AFF-010, AFF-011, AFF-012, AFF-083 | ✅ OK | 17/17 runbooks/registres cites presents. |
|
||||
|
|
@ -37,7 +37,7 @@
|
|||
| P22 | Plan de recette a jour (genere du wiki) | AFF-002 | ✅ OK | Plan de recette à jour (22 sections). |
|
||||
| P23 | Underlay sans collision avec la plage tenant | AFF-103 | ✅ OK | Underlay conforme : 13 reseau(x), aucune collision avec la plage tenant. |
|
||||
| P24 | Frontiere nord/sud : acces d'administration declare | AFF-104 | ✅ OK | note : serveur_powerdns declare un port `derive` que le plan du site ne resout pas — aucune regle emise. |
|
||||
| P25 | Pare-feu Proxmox : est-ouest intra-tenant derive | AFF-107 | ✅ OK | CONFORME : pare-feu Proxmox, 4 tenant(s), 49 groupe(s), 90 regle(s). |
|
||||
| P25 | Pare-feu Proxmox : est-ouest intra-tenant derive | AFF-107 | ✅ OK | CONFORME : pare-feu Proxmox, 4 tenant(s), 52 groupe(s), 96 regle(s). |
|
||||
| P26 | Integrations universelles : aucun hote laisse de cote | AFF-108 | ✅ OK | 13 hote(s) x 6 integration(s) universelle(s) : aucune lacune, aucune recopie (0 exemption(s) derivee(s) du service rendu). |
|
||||
| P27 | Propriete des intrants : hebergeur et tenant separes | AFF-109 | ✅ OK | 8 cle(s) de cluster chez l'hebergeur, aucune recopiee dans les group_vars du tenant. |
|
||||
| P28 | Pools Proxmox : un par tenant, sans collision | AFF-110 | ✅ OK | CONFORME : 5 pool(s) Proxmox, 42 VM placee(s), aucun nom ni VMID en collision. |
|
||||
|
|
@ -45,7 +45,7 @@
|
|||
| P30 | SDN EVPN : zones, VNets et sous-reseaux derives | AFF-112 | ✅ OK | CONFORME : SDN EVPN, 3 zone(s), 15 VNet(s), 15 sous-reseau(x), aucune collision. |
|
||||
| P31 | Documentation : tout ce que le depot FAIT est nomme | — | ✅ OK | 67 scripts expliques et atteignables, 123 cibles make documentees, 68 roles avec README. |
|
||||
| P32 | Intrants exiges par les roles : tous fournis | — | ✅ OK | CONFORME : 36 exigence(s) de role, toutes satisfaites (142 cle(s) declaree(s) par l'instance). |
|
||||
| P33 | Aucune collision de port entre roles co-localises | — | ✅ OK | CONFORME : 35 revendication(s) de port, aucune collision entre roles co-localises (35 groupes). |
|
||||
| P33 | Aucune collision de port entre roles co-localises | — | ✅ OK | CONFORME : 36 revendication(s) de port, aucune collision entre roles co-localises (35 groupes). |
|
||||
| P34 | Chaque document declare son lecteur | — | ✅ OK | 46 document(s) declarent leur lecteur (40 genere(s) exempte(s)). |
|
||||
| P35 | Toute application exigeant une base en a une au plan | — | ✅ OK | 4 application(s) exigeant une base l'ont toutes (3 entree(s) au registre). |
|
||||
| P36 | Tout detenteur d'etat porte une sauvegarde | — | ✅ OK | 8 hote(s) de l'ecosysteme et 3 du site detiennent de l'etat, tous porteurs de `client_backup` (9 groupe(s) au catalogue). |
|
||||
|
|
@ -55,14 +55,14 @@
|
|||
| P40 | Parente : l'ecosysteme sait de quoi il descend | — | ✅ OK | Parente coherente : 4 depot(s), tous retrouves, tous porteurs d'un remote. |
|
||||
| P41 | Resolution d'instance : une seule, partagee | — | ✅ OK | Resolution unique : 63 script(s) passent par `inventory_rules`, 3 exemption(s) nommee(s). |
|
||||
| P42 | L'edge porte les noms qu'il publie | — | ✅ OK | 5 edge(s) emettent un certificat portant les noms publies (instance-ci-1646753/production, OPS-Chezlepro-lab/principal, OPS-Chezlepro/principal, OPS-Technolibre |
|
||||
| P43 | Frontiere : le devis voit les machines du site | — | ✅ OK | Devis de la frontiere : 7 machine(s) du plan retrouvees, 153 regle(s) du site. |
|
||||
| P43 | Frontiere : le devis voit les machines du site | — | ✅ OK | Devis de la frontiere : 7 machine(s) du plan retrouvees, 154 regle(s) du site. |
|
||||
| P44 | Integrations : le serveur avant ses clients | — | ✅ OK | 5 integration(s) appliquent leur serveur avant leurs clients. |
|
||||
| P45 | Pare-feu Proxmox : arme sur les VNet SDN, jamais ailleurs | — | ✅ OK | Le pare-feu Proxmox ne s'arme que sur un VNet SDN (4 cas evalues, dont un qui doit rendre VRAI). |
|
||||
| P46 | Plancher /etc/hosts : un seul role en decide | — | ✅ OK | Un seul maitre du plancher — roles/hosts_statiques/tasks/main.yml : manage_etc_hosts: false ; et le gabarit maitre est pose (roles/hosts_statiques/templates/hos |
|
||||
| P47 | Zones inverses : couvrir l'occupe, et rien de plus | — | ✅ OK | Les zones inverses couvrent l'occupe et rien de plus (5 cas evalues, dont un site a quatre zones et un tenant a une). |
|
||||
| P48 | La carte d'orientation designe ce qui existe, et compte juste | — | ✅ OK | La carte designe 92 chemin(s) qui existent, et ses 7 chiffres correspondent a la mesure. |
|
||||
| P49 | Registre des flux : la matrice d'audit est a jour | — | ✅ OK | Le registre des flux reproduit exactement ce que les `meta/flux.yml` declarent (128 lignes). |
|
||||
| P50 | Silences : un refus muet est declare, place en dernier, et motive | — | ✅ OK | 2 silence(s) declare(s), tous en sequence > 1 (la plus haute des 240 regles `pass`), tous non consignes et tous motives. |
|
||||
| P49 | Registre des flux : la matrice d'audit est a jour | — | ✅ OK | Le registre des flux reproduit exactement ce que les `meta/flux.yml` declarent (129 lignes). |
|
||||
| P50 | Silences : un refus muet est declare, place en dernier, et motive | — | ✅ OK | 2 silence(s) declare(s), tous en sequence > 1 (la plus haute des 241 regles `pass`), tous non consignes et tous motives. |
|
||||
| P51 | Collections : toutes declarees, toutes epinglees | — | ✅ OK | 3 collection(s) et 2 bibliotheque(s) Python declarees et epinglees : ansible.posix==1.6.2, community.general==10.3.0, community.postgresql==3.10.2 |
|
||||
| P52 | Materialiser n'exige pas d'entrer dans le tenant | — | ✅ OK | `creer-vm` confirme par l'agent invite (API des hyperviseurs, deja utilisee pour creer), sans exiger d'entrer dans le tenant. |
|
||||
| P53 | L'interne refuse a voix haute, la bordure se tait | — | ✅ OK | L'interne parle, la bordure se tait — 13 ruleset(s) nftables refusent a voix haute ; pare-feu est-ouest en REJECT, source unique ; frontiere muette (actions : b |
|
||||
|
|
@ -76,7 +76,7 @@
|
|||
| P61 | Schema du plan : il decrit tout ce que les plans contiennent | AFF-033 | ✅ OK | Le schema decrit 47 champ(s) sur 6 registres ; il couvre tout ce que les plans reels contiennent, et la FORME de chaque champ (scalaire / objet / table) corresp |
|
||||
| P62 | Schema du plan : il decrit tout ce que le MOTEUR accepte | AFF-033 | ✅ OK | Les 4 validateurs n'acceptent aucun champ que le schema ignore (applications:8, bases_donnees:4, domaines_publics:5, serveurs:3 champ(s) lus par validateur). |
|
||||
| P63 | cloud-init nait avec la VM et ne lui survit pas | — | ✅ OK | cloud-init est au gabarit (la premiere seconde), absent du socle (pas de va-et-vient), et retire par le durcissement — avec la garde qui verifie que le reseau s |
|
||||
| P64 | Sondes de supervision : declarees ET deposees | — | ✅ OK | 39 sonde(s) declaree(s) ET deposee(s), chacune avec sa raison et son `ttl` : client_journal/journaux, client_metrique/metriques, client_pki/certificat, serveur_ |
|
||||
| P64 | Sondes de supervision : declarees ET deposees | — | ✅ OK | 40 sonde(s) declaree(s) ET deposee(s), chacune avec sa raison et son `ttl` : client_journal/journaux, client_metrique/metriques, client_pki/certificat, serveur_ |
|
||||
| P65 | Depots tiers : demandes au cache, jamais en HTTPS direct | — | ✅ OK | 4 depot(s) tiers relaye(s) par le cache, aucun role ne les vise en https:// ecrit en dur. |
|
||||
| P66 | Clients OIDC : chaque URI vise un nom que le plan expose | — | ✅ OK | 3 client(s) OIDC, toutes leurs URI visent un FQDN que le plan expose (5 exposition(s)). |
|
||||
| P67 | Nom public : le service porte celui du plan, pas celui du role | — | ✅ OK | 11 service(s) expose(s) portent le nom du plan, sur 2 inventaire(s) : instance, SITE. |
|
||||
|
|
@ -88,7 +88,7 @@
|
|||
| P73 | Le locataire designe les services de son site REEL | — | ✅ OK | CONFORME : 6 intrant(s) du locataire concordent avec ce que le site expose (1 non declare(s), donc derive(s) ou non utilise(s)). |
|
||||
| P74 | Gabarit dore : une seule declaration, au plan du site | — | ✅ OK | Gabarit declare une seule fois : VMID 9006 « modeleSetOPS-minimal », precedent 99998. |
|
||||
| P75 | Les parametres de clonage traversent les trois maillons | — | ✅ OK | 14 parametre(s) de clonage, tous emis par l'inventaire. |
|
||||
| P76 | Tout gabarit de role se rend vraiment | — | ✅ OK | 152 gabarits de role : tous se rendent. |
|
||||
| P76 | Tout gabarit de role se rend vraiment | — | ✅ OK | 155 gabarits de role : tous se rendent. |
|
||||
| P77 | Panneaux declares : assemblables, et gradues | — | ✅ OK | 8 panneau(x) declare(s) dans 2 role(s), tous avec titre, expression, raison et une unite que la table sait traduire. |
|
||||
| P78 | Un consommateur de base suit le verrou TLS de son serveur | — | ✅ OK | 3 consommateur(s) suivent la posture de leur serveur ; 2 sans reglage TLS (serveur_icingaweb2, serveur_nextcloud). |
|
||||
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@
|
|||
| `serveur_oauth2_proxy` | egress | 8080 | tcp | localhost | clair | Relais vers l'application co-localisée protégée (upstream en localhost). |
|
||||
| `serveur_openldap` | ingress | 389 | tcp | flotte | starttls | LDAP + STARTTLS pour les clients internes qui préfèrent la mise à niveau TLS sur 389. |
|
||||
| `serveur_openldap` | ingress | 636 | tcp | serveur_keycloak, serveur_dovecot, serveur_icingaweb2, serveur_postfix | tls-requis | LDAPS : fédération (Keycloak), userdb courriel (Dovecot), auth web (Icinga Web 2), tables virtuelles (Postfix). |
|
||||
| `serveur_ops` | ingress | 8090 | tcp | edge, admin | clair | Console d'exploitation servie par l'edge (TLS terminé à l'edge), et joignable depuis le plan d'administration là où il n'y a pas d'edge. Le GUI lui-même reste sur la boucle locale : c'est nginx qui authentifie devant. |
|
||||
| `serveur_ops` | egress | 22 | tcp | flotte | ssh | Piloter la flotte — c'est la raison d'être du poste. |
|
||||
| `serveur_ops` | egress | 443 | tcp | edge | tls-requis | Cloner et resynchroniser le génome depuis la forge de l'écosystème. |
|
||||
| `serveur_ops` | egress | 443 | tcp | voisins_site | tls-requis | Cloner le genome depuis la forge du site, quand cet ecosysteme n'heberge pas la sienne. |
|
||||
|
|
@ -119,7 +120,7 @@
|
|||
|
||||
## Synthèse chiffrement
|
||||
|
||||
- **clair** : 40 flux
|
||||
- **clair** : 41 flux
|
||||
- **n-a** : 8 flux
|
||||
- **ssh** : 8 flux
|
||||
- **starttls** : 6 flux
|
||||
|
|
|
|||
|
|
@ -49,6 +49,15 @@ vault_redis: ""
|
|||
# Inutile en mode `ldap` ou `external` : le rôle ne l'exige que dans le mode qui s'en sert.
|
||||
vault_icingaweb2_admin: ""
|
||||
|
||||
# LA CONSOLE D'EXPLOITATION, QUAND ELLE S'AUTHENTIFIE SEULE.
|
||||
#
|
||||
# `serveur_ops_gui_auth: locale` — le repli d'un ecosysteme SANS annuaire (un SITE).
|
||||
# Un ecosysteme qui a Keycloak reste en `oidc` et laisse cette cle VIDE : la console
|
||||
# lance des deploiements et peut raser, un mot de passe partage devant ce pouvoir est un
|
||||
# accident qui attend.
|
||||
vault_setops_gui_admin: ""
|
||||
|
||||
|
||||
# LE COMPTE DE METRIQUES DE POSTGRESQL — lecture seule, role `pg_monitor`.
|
||||
#
|
||||
# Il ne lit que les vues de statistiques : pas une ligne de donnee applicative. Faire
|
||||
|
|
|
|||
|
|
@ -201,3 +201,78 @@ serveur_ops_sonde_branches: >-
|
|||
{{ dict(serveur_ops_depots | map(attribute='dest')
|
||||
| zip(serveur_ops_depots
|
||||
| map(attribute='branche', default=serveur_ops_sonde_branche))) }}
|
||||
|
||||
# --- LA CONSOLE D'EXPLOITATION, SERVIE PAR LE RUNNER (2026-09-14) ---------------------
|
||||
#
|
||||
# CE QU'ELLE ETAIT : une cible `make inventaire-ui` lancee a la main, qui ecoute sur la
|
||||
# boucle locale du POSTE. Pour la voir, il fallait etre assis devant le controleur.
|
||||
#
|
||||
# CE QU'ELLE DEVIENT : un service du runner, publie par l'edge de son ecosysteme sous le
|
||||
# nom que le plan lui donne. Chaque ecosysteme a sa console, chez lui, et c'est lui qui
|
||||
# la gouverne.
|
||||
#
|
||||
# ═══ CE QUI NE PEUT PAS ETRE SAUTE ═══
|
||||
#
|
||||
# LE GUI N'A AUCUNE AUTHENTIFICATION. Mesure du 2026-09-14, dans son code :
|
||||
#
|
||||
# `GET /` sert la page A QUI LA DEMANDE, avec le jeton ECRIT DEDANS.
|
||||
# `POST /api/*` exige ce jeton — que la page vient de donner a tout le monde.
|
||||
#
|
||||
# Le jeton est une garde CSRF, pas une serrure. La seule serrure est
|
||||
# `--hote 127.0.0.1` : le GUI est sur parce qu'il n'est joignable que de sa propre
|
||||
# machine. L'exposer sans vestibule livrerait `deployer`, `creer`, `instance-utiliser` et
|
||||
# l'edition du plan — la fabric entiere — a quiconque atteint l'URL.
|
||||
#
|
||||
# LE SERVICE RESTE DONC SUR LA BOUCLE LOCALE, TOUJOURS. Ce qui est publie, c'est un
|
||||
# nginx local qui authentifie D'ABORD et relaie ensuite. Aucun chemin ne contourne le
|
||||
# vestibule : il est pose au niveau du `server`, pas d'un `location`.
|
||||
# LE DOSSIER DU MOTEUR, DERIVE DE `serveur_ops_depots` ET PAS ECRIT DEUX FOIS. C'est la
|
||||
# meme liste qui decide ou le genome est clone ; la console doit demarrer LA, pas dans un
|
||||
# chemin qu'on aurait recopie et qui prendrait du retard le jour ou le dossier change.
|
||||
serveur_ops_depot_moteur: >-
|
||||
{{ (serveur_ops_depots | selectattr('role', 'equalto', 'moteur')
|
||||
| map(attribute='dest') | first) | default('Set-OPS-public', true) }}
|
||||
|
||||
serveur_ops_gui_actif: false
|
||||
serveur_ops_gui_ecoute: "127.0.0.1"
|
||||
serveur_ops_gui_port: 8765
|
||||
|
||||
# LE PORT QUE L'EDGE VA CHERCHER. C'est nginx qui l'ouvre, jamais le GUI lui-meme.
|
||||
serveur_ops_gui_port_public: 8090
|
||||
|
||||
# DEUX VESTIBULES, ET LE PREMIER EST LE BON.
|
||||
#
|
||||
# `oidc` — `serveur_oauth2_proxy` devant, donc Keycloak, donc un GROUPE d'annuaire.
|
||||
# Revoquer quelqu'un ne demande alors aucun deploiement (D-66).
|
||||
# `locale` — HTTP Basic, pour un ecosysteme SANS annuaire (un SITE). Un mot de passe
|
||||
# vaut alors pour toute la fabric : c'est un repli, pas un choix.
|
||||
#
|
||||
# `oidc` PAR DEFAUT, et ce n'est pas une preference : la console peut RASER un
|
||||
# ecosysteme. Un seul mot de passe partage devant ce pouvoir est un accident qui attend.
|
||||
serveur_ops_gui_auth: "oidc"
|
||||
serveur_ops_gui_admin_utilisateur: "setops-admin"
|
||||
# LE MOT DE PASSE N'EST PAS NOMME ICI, ET P54 A RAISON DE L'EXIGER.
|
||||
#
|
||||
# `serveur_ops` est un role D'INSEMINATION : le SITE le pose sur le runner d'un locataire
|
||||
# qui vient de naitre, SANS detenir la voute de ce locataire. Citer `vault_...` dans ce
|
||||
# role rendrait l'insemination impossible — le site reclamerait un secret qu'il n'a pas,
|
||||
# et par construction ne doit pas avoir.
|
||||
#
|
||||
# Le role declare donc un PARAMETRE VIDE. Qui le remplit est l'affaire de l'inventaire :
|
||||
# `site_inventaire.py` y met la cle de la voute du SITE, un tenant la sienne dans ses
|
||||
# `group_vars`. La couche qui detient le secret est la seule a le nommer.
|
||||
#
|
||||
# L'assertion en tete des taches refuse de deployer un vestibule `locale` sans mot de
|
||||
# passe : le parametre peut rester vide, il ne peut pas rester vide ET actif.
|
||||
serveur_ops_gui_admin_motdepasse: ""
|
||||
serveur_ops_gui_htpasswd: "/etc/nginx/.setops-gui.htpasswd"
|
||||
serveur_ops_gui_realm: "Console Set-OPS"
|
||||
|
||||
# LE NOM SOUS LEQUEL L'EDGE LE SERT — derive du plan par `instancier` et par
|
||||
# `site_inventaire`, comme tout nom public (P67). Le defaut n'est qu'un repli.
|
||||
serveur_ops_hostname: "console.{{ domaine_interne }}"
|
||||
|
||||
# Le vestibule et de quoi hacher son mot de passe.
|
||||
serveur_ops_gui_paquets:
|
||||
- nginx
|
||||
- apache2-utils
|
||||
|
|
|
|||
17
roles/serveur_ops/handlers/main.yml
Normal file
17
roles/serveur_ops/handlers/main.yml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
- name: Redemarrer la console Set-OPS
|
||||
ansible.builtin.systemd:
|
||||
name: setops-gui.service
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
when: not ansible_check_mode
|
||||
|
||||
# `reload`, PAS `restart` : le vestibule est un vhost, pas une ecoute nouvelle. Le jour ou
|
||||
# `serveur_ops_gui_port_public` change, c'est une autre affaire — nginx ne reapplique pas
|
||||
# un `listen` sur un `reload`, et le service resterait vert sur l'ancien port.
|
||||
- name: Recharger le vestibule de la console
|
||||
ansible.builtin.systemd:
|
||||
name: nginx
|
||||
state: reloaded
|
||||
failed_when: false
|
||||
when: not ansible_check_mode
|
||||
|
|
@ -4,6 +4,26 @@
|
|||
# Le poste est presque tout en sortie : il va chercher son génome, puis il pilote. Rien
|
||||
# ne s'y connecte, sinon l'administration elle-même.
|
||||
flux:
|
||||
# LA CONSOLE D'EXPLOITATION, PUBLIEE PAR L'EDGE DE L'ECOSYSTEME (2026-09-14).
|
||||
#
|
||||
# `[edge, admin]` — la meme paire que `serveur_grafana` et `serveur_icingaweb2`, et
|
||||
# pour la meme raison : sans edge, un ecosysteme n'aurait plus de console du tout.
|
||||
#
|
||||
# CE PORT N'EST PAS CELUI DU GUI. Le GUI ecoute `127.0.0.1:8765` et n'est joignable de
|
||||
# nulle part ailleurs — c'est sa seule serrure, il sert sa page a qui la demande. Ce
|
||||
# qui s'ouvre ici est le NGINX qui authentifie devant.
|
||||
#
|
||||
# LA REGLE N'EST EMISE QUE SI LE PLAN L'EXPOSE : un runner qui ne publie pas sa console
|
||||
# n'a pas ce port ouvert, et `serveur_ops_gui_actif` reste faux.
|
||||
- sens: ingress
|
||||
port: 8090
|
||||
protocole: tcp
|
||||
pair: [edge, admin]
|
||||
chiffrement: clair
|
||||
raison: >-
|
||||
Console d'exploitation servie par l'edge (TLS terminé à l'edge), et joignable
|
||||
depuis le plan d'administration là où il n'y a pas d'edge. Le GUI lui-même reste
|
||||
sur la boucle locale : c'est nginx qui authentifie devant.
|
||||
- sens: egress
|
||||
port: 443
|
||||
protocole: tcp
|
||||
|
|
|
|||
|
|
@ -6,3 +6,19 @@ sondes:
|
|||
raison: 'Le poste d''exploitation peut-il encore agir ? Deux choses : ses depots sont-ils PROPRES
|
||||
et sur leur branche (une modification locale fait diverger le runner du depot que tout le monde
|
||||
lit), et Ansible repond-il dans son venv.'
|
||||
|
||||
# LA CONSOLE, SURVEILLEE PAR CELUI QUI LA SERT (2026-09-14).
|
||||
#
|
||||
# DEUX PROCESSUS, UNE SEULE CAUSE D'ACTION : aller voir ce runner. Le GUI et son
|
||||
# vestibule tombent separement, et le second cas est le plus retors — nginx repond
|
||||
# parfaitement, et relaie vers un service mort.
|
||||
#
|
||||
# CE QUI COMPTE VRAIMENT : que le vestibule TIENNE. Un nginx qui laisserait passer sans
|
||||
# authentifier ne fait echouer personne — il ouvre la fabric. La sonde exige donc un
|
||||
# REFUS sur une requete anonyme : c'est la seule facon de mesurer une serrure.
|
||||
- nom: console-ops
|
||||
ttl: 5400
|
||||
raison: 'La console d''exploitation repond-elle, et son vestibule tient-il ? Un GUI
|
||||
mort derriere un nginx vivant rend une page d''erreur a qui sait deja entrer ; un
|
||||
vestibule tombe rend la fabric entiere a qui trouve l''URL. Le second ne fait
|
||||
echouer personne — c''est pourquoi il faut le mesurer.'
|
||||
|
|
|
|||
|
|
@ -549,3 +549,166 @@
|
|||
owner: root
|
||||
group: root
|
||||
mode: "0750"
|
||||
|
||||
# --- LA CONSOLE D'EXPLOITATION, SERVIE PAR CE RUNNER (2026-09-14) ---------------------
|
||||
#
|
||||
# CE QU'ELLE ETAIT : `make inventaire-ui`, lancee a la main, ecoutant la boucle locale du
|
||||
# POSTE. Pour la voir, il fallait etre assis devant le controleur.
|
||||
#
|
||||
# CE QU'ELLE DEVIENT : un service du runner, publie par l'edge de son ecosysteme sous le
|
||||
# nom que le plan lui donne. Chaque ecosysteme a sa console, chez lui.
|
||||
#
|
||||
# ELLE RESTE SUR LA BOUCLE LOCALE. Voir `serveur_ops_gui_actif` dans les defauts : le GUI
|
||||
# sert sa page a qui la demande, jeton compris — ce jeton garde contre le CSRF, pas
|
||||
# contre un visiteur. Ce qui est publie, c'est le nginx qui authentifie devant.
|
||||
- name: Exiger un vestibule devant la console
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- (serveur_ops_gui_auth != 'locale') or (serveur_ops_gui_admin_motdepasse | length > 0)
|
||||
fail_msg: >-
|
||||
`serveur_ops_gui_auth: locale` mais `serveur_ops_gui_admin_motdepasse` est vide.
|
||||
Cette console lance des deploiements et peut raser un ecosysteme : elle ne s'ouvre
|
||||
pas avec un mot de passe devine. Le renseigner depuis la voute de l'ECOSYSTEME QUI
|
||||
LA PUBLIE — ce role n'en nomme aucune, il est insemine par un site qui ne la
|
||||
detient pas — ou passer en `oidc`.
|
||||
when: serveur_ops_gui_actif | bool
|
||||
|
||||
- name: Installer le vestibule de la console
|
||||
ansible.builtin.apt:
|
||||
name: "{{ serveur_ops_gui_paquets }}"
|
||||
state: present
|
||||
when: serveur_ops_gui_actif | bool
|
||||
|
||||
- name: Déployer le service de la console
|
||||
ansible.builtin.template:
|
||||
src: setops-gui.service.j2
|
||||
dest: /etc/systemd/system/setops-gui.service
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
when: serveur_ops_gui_actif | bool
|
||||
notify: Redemarrer la console Set-OPS
|
||||
|
||||
# LE MOT DE PASSE DU VESTIBULE, QUAND IL N'Y A PAS D'ANNUAIRE.
|
||||
#
|
||||
# `htpasswd -v` VERIFIE sans rien reecrire : c'est lui qui rend la pose idempotente. Une
|
||||
# comparaison de fichiers ne le pourrait pas — bcrypt sale a chaque hachage, et deux
|
||||
# fichiers justes different toujours.
|
||||
- name: Le mot de passe deja pose est-il le bon ?
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- htpasswd
|
||||
- -vb
|
||||
- "{{ serveur_ops_gui_htpasswd }}"
|
||||
- "{{ serveur_ops_gui_admin_utilisateur }}"
|
||||
- "{{ serveur_ops_gui_admin_motdepasse }}"
|
||||
register: serveur_ops_gui_verif
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
no_log: true
|
||||
when:
|
||||
- serveur_ops_gui_actif | bool
|
||||
- serveur_ops_gui_auth == 'locale'
|
||||
|
||||
- name: Poser le mot de passe du vestibule
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- htpasswd
|
||||
- -cbB
|
||||
- "{{ serveur_ops_gui_htpasswd }}"
|
||||
- "{{ serveur_ops_gui_admin_utilisateur }}"
|
||||
- "{{ serveur_ops_gui_admin_motdepasse }}"
|
||||
no_log: true
|
||||
# Elle ne tourne que si la verification a echoue : quand elle tourne, elle change
|
||||
# vraiment quelque chose. L'idempotence vit dans le `when`, pas dans un test de sortie.
|
||||
changed_when: true
|
||||
when:
|
||||
- serveur_ops_gui_actif | bool
|
||||
- serveur_ops_gui_auth == 'locale'
|
||||
- (serveur_ops_gui_verif.rc | default(1)) != 0
|
||||
notify: Recharger le vestibule de la console
|
||||
|
||||
- name: Refermer le fichier de mots de passe sur nginx
|
||||
ansible.builtin.file:
|
||||
path: "{{ serveur_ops_gui_htpasswd }}"
|
||||
owner: root
|
||||
group: www-data
|
||||
mode: "0640"
|
||||
when:
|
||||
- serveur_ops_gui_actif | bool
|
||||
- serveur_ops_gui_auth == 'locale'
|
||||
|
||||
- name: Déployer le vestibule de la console
|
||||
ansible.builtin.template:
|
||||
src: nginx-gui.conf.j2
|
||||
dest: /etc/nginx/sites-available/setops-gui.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
when: serveur_ops_gui_actif | bool
|
||||
notify: Recharger le vestibule de la console
|
||||
|
||||
- name: Activer le vestibule de la console
|
||||
ansible.builtin.file:
|
||||
src: /etc/nginx/sites-available/setops-gui.conf
|
||||
dest: /etc/nginx/sites-enabled/setops-gui.conf
|
||||
state: link
|
||||
when: serveur_ops_gui_actif | bool
|
||||
notify: Recharger le vestibule de la console
|
||||
|
||||
# LE SITE PAR DEFAUT SERT TOUT CE QU'ON NE LUI A PAS DEMANDE. Le laisser en place
|
||||
# donnerait une seconde porte, sans vestibule, sur le meme port.
|
||||
- name: Retirer le site nginx par defaut
|
||||
ansible.builtin.file:
|
||||
path: /etc/nginx/sites-enabled/default
|
||||
state: absent
|
||||
when: serveur_ops_gui_actif | bool
|
||||
notify: Recharger le vestibule de la console
|
||||
|
||||
- name: Activer et demarrer la console
|
||||
ansible.builtin.systemd:
|
||||
name: setops-gui.service
|
||||
enabled: true
|
||||
state: started
|
||||
daemon_reload: true
|
||||
when:
|
||||
- serveur_ops_gui_actif | bool
|
||||
- not ansible_check_mode
|
||||
|
||||
# UN RUNNER QUI CESSE DE PUBLIER SA CONSOLE DOIT L'ETEINDRE. Sans ce retrait, retirer
|
||||
# l'exposition du plan laisserait le service tourner et le vestibule ouvert — la console
|
||||
# resterait joignable alors que plus rien ne la declare.
|
||||
- name: Éteindre la console quand ce runner ne la publie plus
|
||||
ansible.builtin.systemd:
|
||||
name: setops-gui.service
|
||||
enabled: false
|
||||
state: stopped
|
||||
failed_when: false
|
||||
when:
|
||||
- not (serveur_ops_gui_actif | bool)
|
||||
- not ansible_check_mode
|
||||
|
||||
- name: Retirer le vestibule quand ce runner ne publie plus
|
||||
ansible.builtin.file:
|
||||
path: /etc/nginx/sites-enabled/setops-gui.conf
|
||||
state: absent
|
||||
when: not (serveur_ops_gui_actif | bool)
|
||||
notify: Recharger le vestibule de la console
|
||||
|
||||
- name: Deposer la sonde « console-ops »
|
||||
ansible.builtin.template:
|
||||
src: sonde-console-ops.sh.j2
|
||||
dest: /usr/local/lib/setops/sondes/console-ops.sh
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0750"
|
||||
when: serveur_ops_gui_actif | bool
|
||||
|
||||
# LA SONDE SUIT LA DECLARATION, DANS LES DEUX SENS. Un runner qui cesse de publier sa
|
||||
# console garderait sinon une sonde orpheline : `client_sante` continuerait de la jouer
|
||||
# et de pousser un resultat pour un service qu'Icinga ne definit plus.
|
||||
- name: Retirer la sonde « console-ops » quand ce runner ne publie plus
|
||||
ansible.builtin.file:
|
||||
path: /usr/local/lib/setops/sondes/console-ops.sh
|
||||
state: absent
|
||||
when: not (serveur_ops_gui_actif | bool)
|
||||
|
|
|
|||
48
roles/serveur_ops/templates/nginx-gui.conf.j2
Normal file
48
roles/serveur_ops/templates/nginx-gui.conf.j2
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# GENERE par Set-OPS (role serveur_ops). Ne pas editer a la main.
|
||||
#
|
||||
# LE VESTIBULE DE LA CONSOLE. Le GUI, lui, n'ecoute que la boucle locale.
|
||||
#
|
||||
# TOUT CE QUI EST PUBLIE PASSE PAR ICI, et l'authentification est posee au niveau du
|
||||
# `server` — pas d'un `location`. Placee dans le seul bloc de relais, elle laisserait
|
||||
# passer tout chemin qu'un `location` plus specifique attraperait en premier. Une
|
||||
# serrure qu'on contourne par la porte d'a cote n'est pas une serrure.
|
||||
server {
|
||||
listen {{ serveur_ops_gui_port_public }};
|
||||
listen [::]:{{ serveur_ops_gui_port_public }};
|
||||
server_name {{ serveur_ops_hostname }};
|
||||
{% if serveur_ops_gui_auth == 'locale' %}
|
||||
|
||||
# SANS ANNUAIRE : un mot de passe, et il vaut pour toute la fabric. C'est un repli
|
||||
# assume, pas un choix — voir `serveur_ops_gui_auth` dans les defauts du role.
|
||||
auth_basic "{{ serveur_ops_gui_realm }}";
|
||||
auth_basic_user_file {{ serveur_ops_gui_htpasswd }};
|
||||
{% else %}
|
||||
|
||||
# AVEC ANNUAIRE : c'est `serveur_oauth2_proxy` qui se tient devant, et lui seul doit
|
||||
# pouvoir frapper ce port. Le pare-feu de l'hote le dit deja (`meta/flux.yml`), et on
|
||||
# ne le redit pas ici : deux couches qui declarent la meme politique finissent par
|
||||
# en declarer deux differentes.
|
||||
{% endif %}
|
||||
|
||||
# LA CONSOLE DEPLOIE, ET UN DEPLOIEMENT EST LONG. Les delais par defaut de nginx
|
||||
# (60 s) couperaient la reponse au milieu d'un `deployer`, et la page afficherait une
|
||||
# erreur reseau pour une operation qui, elle, continue sur la machine. La pire forme
|
||||
# de mensonge : l'ecran dit echec, la fabric dit succes.
|
||||
proxy_read_timeout {{ serveur_ops_gui_delai | default(3600) }}s;
|
||||
proxy_send_timeout {{ serveur_ops_gui_delai | default(3600) }}s;
|
||||
|
||||
# LA SORTIE D'UN DEPLOIEMENT ARRIVE AU FIL DE L'EAU. Sans ces deux lignes, nginx la
|
||||
# retiendrait pour l'envoyer d'un bloc : la console resterait muette pendant des
|
||||
# minutes, puis tout apparaitrait d'un coup.
|
||||
proxy_buffering off;
|
||||
gzip off;
|
||||
|
||||
location / {
|
||||
proxy_pass http://{{ serveur_ops_gui_ecoute }}:{{ serveur_ops_gui_port }};
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
47
roles/serveur_ops/templates/setops-gui.service.j2
Normal file
47
roles/serveur_ops/templates/setops-gui.service.j2
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# GENERE par Set-OPS (role serveur_ops). Ne pas editer a la main.
|
||||
#
|
||||
# LA CONSOLE D'EXPLOITATION DE CET ECOSYSTEME.
|
||||
#
|
||||
# ELLE N'ECOUTE QUE SA PROPRE MACHINE, et ce n'est pas un reglage : c'est SA SEULE
|
||||
# SERRURE. Le GUI sert sa page a qui la demande, avec son jeton ecrit dedans — le jeton
|
||||
# garde contre le CSRF, pas contre un visiteur. Ce qui protege la fabric, c'est que
|
||||
# personne d'autre que `127.0.0.1` ne peut frapper.
|
||||
#
|
||||
# Ce qui est publie est le nginx local, qui authentifie d'abord.
|
||||
[Unit]
|
||||
Description=Console d'exploitation Set-OPS ({{ setops_instance | default('instance') }})
|
||||
Documentation=file://{{ serveur_ops_racine }}/{{ serveur_ops_depot_moteur }}/docs/carte-set-ops.md
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User={{ serveur_ops_utilisateur }}
|
||||
Group={{ serveur_ops_utilisateur }}
|
||||
WorkingDirectory={{ serveur_ops_racine }}/{{ serveur_ops_depot_moteur }}
|
||||
|
||||
# LE PYTHON DE L'ENVIRONNEMENT ISOLE, pas celui du systeme : le GUI importe `yaml` et
|
||||
# les modules du moteur, et c'est cet environnement-la qui les porte.
|
||||
ExecStart={{ serveur_ops_venv }}/bin/python3 scripts/inventory_gui.py \
|
||||
--hote {{ serveur_ops_gui_ecoute }} --port {{ serveur_ops_gui_port }}
|
||||
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
# CE QU'ELLE PEUT FAIRE, ET CE QU'ON LUI RETIRE. La console LANCE des deploiements : on
|
||||
# ne peut donc pas la priver d'executables ni de reseau. Ce qu'on peut lui retirer sans
|
||||
# la casser, on le retire — un service qui deploie merite plus de soin que les autres,
|
||||
# pas moins.
|
||||
NoNewPrivileges=yes
|
||||
PrivateTmp=yes
|
||||
ProtectHome=read-only
|
||||
ProtectSystem=full
|
||||
ProtectKernelTunables=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectControlGroups=yes
|
||||
RestrictSUIDSGID=yes
|
||||
RemoveIPC=yes
|
||||
LockPersonality=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
60
roles/serveur_ops/templates/sonde-console-ops.sh.j2
Normal file
60
roles/serveur_ops/templates/sonde-console-ops.sh.j2
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
#!/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.
|
||||
set -uo pipefail
|
||||
|
||||
GUI={{ serveur_ops_gui_ecoute }}:{{ serveur_ops_gui_port }}
|
||||
VESTIBULE=127.0.0.1:{{ 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 REFUSE-T-IL ? 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.
|
||||
#
|
||||
# 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.
|
||||
vu=$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 \
|
||||
-H "Host: ${NOM}" "http://${VESTIBULE}/" 2>/dev/null)
|
||||
case "${vu}" in
|
||||
401|403)
|
||||
echo "Console vivante, vestibule (${MODE}) en place : une requete anonyme rend HTTP ${vu}."
|
||||
exit 0
|
||||
;;
|
||||
30[12478])
|
||||
# Une passerelle SSO renvoie vers son fournisseur d'identite : c'est un refus, dit
|
||||
# autrement. On l'accepte seulement quand c'est le mode declare — en `locale`, une
|
||||
# redirection signifierait que le `auth_basic` n'est plus applique.
|
||||
if [[ "${MODE}" != "locale" ]]; then
|
||||
echo "Console vivante, vestibule (${MODE}) en place : une requete anonyme est renvoyee (HTTP ${vu})."
|
||||
exit 0
|
||||
fi
|
||||
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
|
||||
Loading…
Reference in a new issue