ajoute le proxy console Go et le relais WebSocket vers nova-serialproxy
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
30e14b1c54
commit
8742bde807
30 changed files with 1103 additions and 6 deletions
90
docs/runbooks/proxy-console.md
Normal file
90
docs/runbooks/proxy-console.md
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
# Runbook — Proxy console série (étape 9)
|
||||
|
||||
## Comment ça marche
|
||||
|
||||
```
|
||||
Navigateur élève/prof → WebSocket /console/{jeton} → Nginx → proxy-console (Go, 127.0.0.1:8081)
|
||||
│
|
||||
lit "console:{jeton}" dans Redis
|
||||
(posé par Laravel, 5 min)
|
||||
│
|
||||
une seule connexion WebSocket
|
||||
▼
|
||||
nova-serialproxy (10.10.0.1:6083)
|
||||
```
|
||||
|
||||
1. Laravel (`App\Services\OpenStack\NovaClient`) s'authentifie auprès de Keystone avec l'*application credential* `portail-syllabus` (étape 6), demande à Nova l'adresse de la console série d'une instance (`POST /servers/{id}/remote-consoles`).
|
||||
2. `App\Services\OpenStack\JetonConsole` range cette adresse dans Redis sous un jeton aléatoire (`console:{jeton}`, 5 minutes), avec le rôle (`ecriture` pour l'élève, `lecture` pour le prof). Le navigateur ne reçoit que le jeton.
|
||||
3. Le navigateur ouvre `ws://.../console/{jeton}`, relayé par Nginx vers le proxy Go.
|
||||
4. Le proxy lit Redis, ouvre (ou réutilise) **une seule** connexion amont vers nova-serialproxy par VM (`instance_id`), et duplique ce qu'il en reçoit vers tous les navigateurs abonnés à cette VM. Seul l'abonné en rôle `ecriture` peut faire passer ses frappes ; les autres sont ignorées côté serveur (pas seulement côté interface).
|
||||
|
||||
Le code source : `proxy-console/` (Go), `portail/app/Services/OpenStack/`, `portail/app/Http/Controllers/ConsoleController.php`.
|
||||
|
||||
## Déployer
|
||||
|
||||
```
|
||||
cd ansible && script -q /dev/null ansible-playbook proxy-console.yml -i inventory.ini
|
||||
```
|
||||
|
||||
Rejoue aussi le rôle `portail` (le vhost Nginx avec `location /console/` et les
|
||||
variables `OS_*` de `.env` vivent dans ce rôle).
|
||||
|
||||
## Où sont les choses
|
||||
|
||||
| Élément | Emplacement |
|
||||
|---|---|
|
||||
| Code source Go | `/opt/proxy-console/` (utilisateur système dédié `proxyconsole`, sans shell) |
|
||||
| Binaire compilé | `/opt/proxy-console/proxy-console` (compilé sur le serveur, jamais committé) |
|
||||
| Secrets du service (mot de passe Redis) | `/etc/proxy-console.env`, `640 root:proxyconsole` |
|
||||
| Service | `systemd proxy-console.service`, durci (`ProtectSystem=strict`, `NoNewPrivileges`) |
|
||||
| Scripts de test manuel | `/opt/construction-image/test-manuel.py`, `test-duplication.py` |
|
||||
|
||||
Le proxy utilise **le même Redis que l'app** (port 6380) : pas de deuxième
|
||||
instance à gérer, juste un espace de clés différent (`console:*`).
|
||||
|
||||
## Tester manuellement (sans navigateur)
|
||||
|
||||
Récupérer un jeton (en tant qu'élève connecté, via l'API du portail), puis :
|
||||
|
||||
```
|
||||
ssh paillasse
|
||||
sudo /usr/bin/python3 /opt/construction-image/test-manuel.py 'ws://127.0.0.1:8081/console/<jeton>' 'ip a'
|
||||
```
|
||||
|
||||
Pour vérifier la duplication (élève + prof simultanés, le prof ne peut rien taper) :
|
||||
|
||||
```
|
||||
sudo /usr/bin/python3 /opt/construction-image/test-duplication.py \
|
||||
'ws://127.0.0.1:8081/console/<jeton-ecriture>' 'ws://127.0.0.1:8081/console/<jeton-lecture>'
|
||||
```
|
||||
|
||||
## Piège vécu le 2026-10-08
|
||||
|
||||
**La VM de test avait été créée dans le mauvais projet OpenStack** (`admin`
|
||||
au lieu de `paillasse`) : l'*application credential* du portail, scopée au
|
||||
projet `paillasse`, recevait `404 Instance could not be found` de Nova —
|
||||
pas un problème d'authentification, juste un projet qui ne voit pas les
|
||||
ressources d'un autre. Toujours créer les VM avec le cloud `paillasse-service`
|
||||
(étape 6), jamais `kolla-admin` seul.
|
||||
|
||||
**`go build` avec `$HOME` pointé sur le dossier de l'app** crée `.cache/` et
|
||||
`go/pkg/mod/` appartenant à `root`, que le `rsync` suivant (qui tourne sans
|
||||
`become`) ne peut plus supprimer. Ces deux dossiers sont exclus du `rsync`.
|
||||
|
||||
**Le sous-protocole WebSocket `binary`**, nécessaire pour parler directement
|
||||
à `nova-serialproxy` (étape 6), n'a pas sa place pour parler à *notre*
|
||||
proxy : le vrai navigateur ne le demande pas, et un client qui le demande
|
||||
sans que le proxy le lui renvoie peut rejeter le handshake (« Invalid
|
||||
WebSocket Header »). Les scripts de test ne le demandent plus.
|
||||
|
||||
## Vérifié le 2026-10-08
|
||||
|
||||
- Une élève fictive a tapé `ip a` dans sa VM et vu le résultat (deux
|
||||
interfaces, `lo` et `ens3`), via le chemin complet navigateur → Nginx →
|
||||
proxy Go → nova-serialproxy.
|
||||
- Un autre élève a tenté d'obtenir un jeton pour cette même session de lab :
|
||||
`403 Forbidden`.
|
||||
- Prof et élève connectés en même temps sur la même VM : le prof a vu une
|
||||
commande tapée par l'élève (duplication du flux), et sa propre tentative
|
||||
d'écrire n'a eu aucun effet (vérifié qu'elle n'apparaît jamais dans le
|
||||
flux renvoyé par la VM).
|
||||
Loading…
Add table
Add a link
Reference in a new issue