Paillasse/docs/prompt-deploiement-paillasse.md
Adam Belkacemi ca4669156a Prompt final
2026-10-07 01:12:34 +02:00

327 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Prompt Claude Code : déploiement complet de Syllabus
> À coller dans Claude Code, lancé depuis la racine du dépôt `Paillasse` sur le Mac d'Adam.
---
## 0. Contexte
**Nom du produit : Syllabus** (ancien nom de travail : Paillasse).
- Tout ce que voient les utilisateurs (pages, titres, e-mails, mentions légales) dit **Syllabus**, avec les logos `design/images/logo-syllabus.png` et `design/images/logo-syllabus-icone.png`.
- Les noms techniques internes restent tels quels : le dépôt `Paillasse`, le dossier `paillasse-contenus/`, l'image `paillasse-debian`, le projet OpenStack `paillasse`. Ne les renomme pas.
- Si tu trouves « Paillasse » dans un texte visible, remplace-le par « Syllabus ».
Tu vas déployer **Syllabus** de bout en bout sur un serveur bare metal, jusqu'à une version utilisable par une vraie classe.
Lis d'abord `CLAUDE.md` et `paillasse-contenus/README.md`. Ils contiennent le contexte complet du projet, la stack et les règles.
**Syllabus** : une plateforme de labs Linux et réseau pour les écoles. Chaque élève a une vraie VM Debian, avec une console dans le navigateur et une correction automatique par scripts. Pas d'IA dans le produit.
**Le serveur**
- Scaleway Dedibox **Start-2-L**, datacenter DC2 (France).
- Xeon D-1531 (6 cœurs / 12 threads), **32 Go de RAM**, 2 × 250 Go SSD en RAID 1 (`/` fait 226 Go).
- **Une seule IP publique** : `163.172.4.195`.
- Debian `12`, installé par Adam depuis la console Dedibox.
- Accès SSH : `ssh adam@163.172.4.195`, avec la clé dédiée `~/.ssh/id_ed25519_claude`. Crée l'alias `paillasse` dans `~/.ssh/config`, avec `IdentitiesOnly yes` pour n'utiliser que cette clé.
- `sudo` fonctionne sans mot de passe pour `adam` (fichier `/etc/sudoers.d/90-adam`).
- Sauvegardes : **Dedibackup 100 Go** (FTP, joignable uniquement depuis le serveur). Adam a déposé les identifiants dans `/root/.dedibackup` (`chmod 600`). Ne les affiche jamais.
**Image et labs** : suis la section « Image commune » de `paillasse-contenus/README.md`, et crée ce que demandent les labs 04 et 07 (étape `preparation`, `/opt/labs/07/lycee.sql`, `/opt/paquets/`).
**La forge** : `ssh://git@forge.adam.belkacemi.me:2222/adam/Paillasse.git`. Tu n'y fais que `git push` : aucune connexion SSH à la machine qui l'héberge.
**Le domaine** : pas encore acheté. Fais les étapes 1 à 11 sans domaine (accès par IP ou tunnel SSH). Avant l'étape 12, arrête-toi et demande-moi le domaine.
---
## 1. Règles non négociables
1. **Ne jamais toucher à `tripoli-cloud`** ni à aucune autre machine que le serveur Syllabus et le Mac d'Adam.
2. **Aucun secret** dans le code, dans Git, dans le journal ou dans la conversation.
- Les secrets vivent dans des fichiers `chmod 600` sur le serveur, ou dans un `.env` ignoré par Git.
- Les secrets Ansible passent par **Ansible Vault**. Le mot de passe du vault reste hors du dépôt.
- Kolla : `passwords.yml` n'est jamais commité en clair.
3. **Commandes à risque : explique, montre la commande, vérifie la sauvegarde, puis attends le « OK » d'Adam.** Sont concernés :
- suppressions (`rm -rf`, `DROP`, `kolla-ansible destroy`…) ;
- disques, partitions, RAID ;
- toute modification du **réseau**, du **pare-feu** ou de **SSH** ;
- redémarrage du serveur.
4. **Anti-coupure SSH** : avant toute modification du réseau, du pare-feu ou de SSH, programme un retour automatique à l'ancienne configuration dans 5 minutes (par exemple avec `systemd-run --on-active=5min`), puis annule-le une fois l'accès vérifié depuis une **nouvelle** session SSH.
5. **Tu ne commandes et ne paies jamais rien.**
6. **Élèves mineurs** : minimisation des données.
- Un élève = un identifiant `prénom.initiale` et un code secret. Pas d'e-mail. Le nom complet est facultatif et visible uniquement du prof.
- Suppression automatique 90 jours après l'archivage d'une classe.
7. **Tout est du code** :
- chaque configuration passe par un rôle Ansible idempotent dans `ansible/` ;
- une commande tapée à la main pour explorer doit ensuite être codifiée ;
- relancer un playbook ne doit rien casser.
8. **Vérifie les versions dans la documentation officielle**, jamais de mémoire : Kolla-Ansible, OS supportés, Laravel, PHP, Go. Utilise Context7 ou le web.
9. **Documentation en français** (docs, commentaires, messages de commit).
---
## 2. Méthode de travail
### Pour chaque étape
1. Annonce l'étape et son critère de réussite.
2. Fais le travail.
3. **Vérifie le critère par un test réel** et montre la sortie.
4. Écris dans `docs/journal.md` :
- ce qui a été fait ;
- les commandes clés ;
- les problèmes rencontrés et leur solution ;
- un paragraphe **« Comprendre »** de 3 à 6 phrases, qui explique simplement le pourquoi à Adam.
5. `git commit`, puis `git push`.
6. Passe à l'étape suivante sans attendre, sauf si une règle de la section 1 impose l'accord d'Adam.
### Limite de temps
**1 h 30 maximum par étape.** Si elle est dépassée :
- arrête-toi ;
- écris le blocage dans `docs/journal.md` (symptôme, pistes essayées, hypothèses) ;
- propose 2 options à Adam et attends sa décision.
### Arborescence du dépôt
```
ansible/ inventaire, playbooks, rôles
kolla/ globals.yml, inventaire all-in-one (passwords.yml : chiffré ou hors Git)
openstack/ modèles Heat, flavors, scripts de construction d'images
portail/ application Laravel
proxy-console/ service Go
labs/ copie de travail des labs YAML
design/ maquettes Claude Design (référence exacte, ne pas modifier)
docs/ journal.md, runbooks/, inventaire-serveur.md, post-mortems/
```
---
## 3. Les étapes
### Étape 1 : Reconnaissance (lecture seule)
- Relève sur le serveur :
- OS, noyau, CPU ;
- virtualisation matérielle (`vmx`) et `/dev/kvm` ;
- RAM, disques, RAID ;
- interfaces réseau, IP, passerelle, DNS.
- Vérifie quelle version stable de Kolla-Ansible supporte Debian 12, et choisis-la.
- Écris `docs/inventaire-serveur.md`.
**Critère :** inventaire écrit, KVM disponible, version de Kolla-Ansible choisie et justifiée.
### Étape 2 : Durcissement (Ansible depuis le Mac)
- Installe Ansible sur le Mac s'il manque. Crée `ansible/inventory.ini` et un rôle `durcissement` :
- utilisateur d'administration, `sudo` ;
- SSH par clé uniquement, `PermitRootLogin no` ;
- fail2ban, `unattended-upgrades`, chrony, fuseau `Europe/Paris` ;
- pare-feu nftables : seuls **22, 80 et 443** ouverts sur l'IP publique.
- Attention : `kolla-ansible bootstrap-servers` peut désactiver le pare-feu. Prévois-le dès maintenant (réglage Kolla adapté, ou rôle nftables rejoué après Kolla).
**Critère :**
- connexion par clé OK ;
- connexion par mot de passe refusée, root refusé ;
- depuis le Mac, `nmap 163.172.4.195` ne montre que 22, 80 et 443.
### Étape 3 : Sauvegardes vers Dedibackup
- Dedibackup (`dedibackup-dc3.online.net`) est limité à **1000 fichiers** et passe par du FTP **non chiffré**.
- Fais des **archives quotidiennes** `tar.zst`, **chiffrées avec age** avant l'envoi.
- Rotation : 7 quotidiennes et 4 hebdomadaires.
- À sauvegarder : `/etc`, `/home`, `/root`, `/etc/kolla` (plus tard), les dumps MariaDB de l'app (plus tard), le dossier du portail.
- Programmation : timer systemd quotidien.
- Identifiants dans `/root/.dedibackup` (variables `DEDIBACKUP_HOST`, `DEDIBACKUP_USER`, `DEDIBACKUP_PASS`). La clé privée age ne quitte jamais le serveur : rappelle-moi d'en garder une copie hors du serveur.
- Écris `docs/runbooks/sauvegardes.md`.
**Critère :** une sauvegarde est envoyée et **un fichier est restauré avec succès** dans `/tmp`.
### Étape 4 : Réseau pour OpenStack (accord d'Adam obligatoire)
L'objectif est de ne jamais mettre Neutron sur l'interface publique.
- Crée une interface `dummy0` avec une IP privée (par exemple `10.10.0.1/24`). Elle servira à `network_interface` et `kolla_internal_vip_address` (Kolla sans HAProxy).
- Crée une paire veth. `neutron_external_interface` utilise un bout de la paire. L'autre bout porte la passerelle du réseau externe OpenStack, avec un NAT masquerade nftables.
- Rends le tout persistant avec systemd-networkd et le rôle Ansible `reseau-openstack`.
- Applique la règle anti-coupure SSH, puis **redémarre, avec l'accord d'Adam**.
**Critère :** après redémarrage, le SSH répond, `dummy0` et la paire veth sont présents, et Internet reste joignable depuis le serveur.
### Étape 5 : Kolla-Ansible tout-en-un
- Dans un venv Python : la version de Kolla-Ansible choisie à l'étape 1, puis `kolla-ansible install-deps`.
- Dans `kolla/globals.yml` (**vérifie les noms exacts dans la doc de la version**), les idées sont :
- `enable_haproxy: "no"`, `enable_horizon: "no"` ;
- `enable_heat: "yes"`, `enable_nova_serialconsole_proxy: "yes"` ;
- `nova_compute_virt_type: "kvm"` ;
- `enable_prometheus` et `enable_grafana` si c'est le plus simple pour l'étape 7.
- Mots de passe : `kolla-genpwd`. Jamais de `passwords.yml` en clair dans Git.
- Lance `bootstrap-servers`, `prechecks`, `deploy`, puis `post-deploy`. Installe le client `openstack`.
- Rejoue le rôle pare-feu si Kolla l'a modifié.
**Critère :**
- `openstack service list` et `openstack compute service list` sont OK ;
- aucune API OpenStack n'est joignable depuis l'IP publique (vérifie avec `nmap`).
### Étape 6 : Première VM en console série
1. Crée le projet `paillasse` et un utilisateur de service avec des **application credentials**, stockés dans un fichier `chmod 600`.
2. Construis l'image `paillasse-debian` avec `virt-customize`, à partir de l'image cloud Debian 12 officielle, en suivant la section « Image commune » de `paillasse-contenus/README.md`.
3. Propriété de l'image : `hw_qemu_guest_agent=yes`.
4. Crée la flavor `eleve.petit` : 1 vCPU, 768 Mo, 5 Go.
5. Démarre une VM et récupère l'adresse de sa console série avec l'API Nova `remote-consoles` (protocole `serial`).
**Critère :** un petit script (websocat ou Python) connecté au WebSocket de la console série affiche l'invite de connexion, et une commande tapée s'exécute.
### Étape 7 : Supervision et alertes
- Prometheus, node_exporter et Grafana : ceux de Kolla si possible.
- Uptime Kuma en conteneur Docker, écoutant sur `127.0.0.1`.
- Alertes : disque > 80 %, RAM > 90 %, service OpenStack arrêté, dernière sauvegarde de plus de 26 h, serveur injoignable.
- Notifications par webhook : l'URL est un secret, demande-la-moi.
- Tant qu'il n'y a pas de domaine, accès par tunnel SSH uniquement.
**Critère :** une alerte de test est **reçue** par Adam.
### Étape 8 : Portail Laravel
- Nginx et PHP-FPM, avec une version de PHP compatible avec la dernière version de Laravel.
- MariaDB et Redis **pour l'app** en Docker Compose, liés à `127.0.0.1`, sur des ports différents de ceux de Kolla (par exemple 3307 et 6380).
- **Laravel avec Blade et Vite**, dans `portail/`.
- **Design** :
- le dossier `design/` est la référence exacte. Il contient déjà des vues Blade (`design/resources/views/`) et des routes (`design/routes/web.php`) : reprends-les **telles quelles** ;
- le JS existant (`design/js/`, dont `paillasse-terminal.js`) et le CSS (`design/css/`) sont conservés ;
- compare chaque page avec une capture Playwright de `design/`, et corrige jusqu'à ce qu'elles soient identiques ;
- si `design/` est absent, arrête-toi et demande-le-moi.
- Modèles :
- `Etablissement`, `User` (prof), `Classe`, `Eleve` (identifiant, code secret haché, nom complet facultatif) ;
- `Lab`, `Seance`, `LabSession` (élève, lab, id de la stack ou des VM, état, expiration).
- Connexion, sur un seul écran `/connexion` :
- le prof avec un e-mail et un mot de passe ;
- l'élève avec son identifiant `prénom.initiale` (avec un numéro pour les homonymes) et un code secret de 6 caractères.
- Le prof crée une classe, saisit ou importe en CSV la liste des élèves, et génère leurs identifiants et codes (fiches imprimables avec QR code).
- **Une seule session active par compte** : la précédente est coupée et le prof est alerté.
- **Salle d'attente de séance** : le prof admet les élèves.
- Tests **Pest** pour la connexion, la session unique et la création de classe.
**Critère :** les tests Pest passent, et un élève fictif se connecte et voit son tableau de bord, identique à la maquette.
### Étape 9 : Proxy console Go et xterm.js
- Service Go dans `proxy-console/`, écoutant sur `127.0.0.1:8081`, lancé par systemd. Nginx relaie `/console` en WebSocket.
- Déroulé :
1. Laravel demande à Nova l'adresse de la console série.
2. Laravel la range dans Redis sous un **jeton aléatoire**, pour 5 minutes.
3. Le navigateur reçoit uniquement ce jeton.
4. Le proxy lit Redis, ouvre le WebSocket vers nova-serialproxy et recopie les octets dans les deux sens.
- La console série n'accepte qu'**une seule connexion** : c'est le proxy qui duplique le flux vers la vue du prof, en lecture seule.
- Côté front : xterm.js, avec un onglet par VM du lab.
**Critère :** dans le navigateur, l'élève tape `ip a` dans sa VM et voit le résultat, et un autre élève ne peut pas ouvrir cette console.
### Étape 10 : Liaison Laravel ↔ OpenStack et lab 08
- Client OpenStack dans Laravel, avec le client HTTP de Laravel ou `php-opencloud/openstack`, via les application credentials.
- Modèle Heat du lab 08, d'après `paillasse-contenus/08-deux-lan-routeur/lab.yaml` :
- réseaux de lab **sans DHCP**, avec `port_security_enabled: false` ;
- **aucun routeur vers l'extérieur** : les VM des labs n'ont pas accès à Internet.
- Jobs dans une file Redis :
- `CreerStackLab` ;
- `SupprimerStackLab` ;
- expiration automatique des sessions.
**Critère :** le bouton « Lancer le lab 08 » crée une stack `CREATE_COMPLETE`, et les terminaux de chacune de ses VM s'ouvrent.
### Étape 11 : Stock chaud et correction automatique
**Stock chaud**
- Une liste Redis de stacks prêtes par lab, remplie par un job.
- Taille par défaut : calculée par toi selon la RAM disponible, et configurable.
- Pré-remplissage automatique 10 minutes avant une séance.
**Correction**
- Un petit service `correcteur`, lancé par systemd, écoutant sur un socket Unix.
- Il accepte **uniquement** : uuid d'instance, id du lab, id de la vérification.
- Il exécute les vérifications **définies dans le YAML du lab** avec `guest-exec` de l'agent QEMU (par `virsh`, dans le conteneur `nova_libvirt`).
- Jamais de commande libre venant du portail.
- Laravel enregistre la note et le message pour l'élève.
**Critère :**
- un lab pris dans le stock s'ouvre en **moins de 5 s** ;
- une configuration correcte donne « réussi » ;
- une configuration incorrecte donne « échoué » avec un message utile.
### Étape 12 : Domaine, HTTPS et sécurité
**Avant de commencer, arrête-toi et demande-moi le domaine** (je l'achète et je configure le DNS moi-même).
- Certificat Let's Encrypt (certbot, plugin Nginx) et renouvellement automatique.
- En-têtes de sécurité : HSTS, CSP adaptée à xterm.js, etc.
- Grafana et Uptime Kuma derrière authentification, et la page de statut publique.
- Vérifie à nouveau que seuls 22, 80 et 443 sont ouverts, et qu'aucune VM élève n'a d'IP publique ni d'accès sortant.
**Critère :** le domaine affiche un certificat valide en HTTPS, et le parcours prof → élève → lab → correction fonctionne.
### Étape 13 : Tests de capacité et de résistance
- Lance 20 VM `eleve.petit` en même temps. Mesure la RAM, le CPU et les temps de démarrage, puis écris `docs/capacite.md`.
- Restaure une sauvegarde complète du portail sur une copie de test.
- **Avec l'accord d'Adam**, redémarre le serveur : tout doit revenir seul (Kolla, portail, proxy, correcteur, supervision).
**Critère :**
- les chiffres de capacité sont écrits ;
- la restauration est réussie ;
- tout est revenu seul après le redémarrage.
### Étape 14 : Pages légales et RGPD
Pages à créer :
- `/legal` : mentions légales (Esquisse Formation ; le SIREN et l'adresse sont à compléter par Adam) ;
- la politique de confidentialité ;
- la page de statut.
Job de purge : suppression 90 jours après l'archivage d'une classe.
Interdits :
- logo Marianne ou ministère ;
- drapeau de l'UE ;
- faux « logo RGPD » ;
- KPI inventés ou faux clients.
Formulations autorisées : « Hébergé en France » (c'est vrai), et « Conforme au RGPD » avec un lien vers la politique de confidentialité.
**Critère :** pages en ligne, et un test Pest prouve que la purge fonctionne.
### Étape 15 : Visio de classe (seulement après validation des étapes 1 à 11)
- Jitsi auto-hébergé, derrière Nginx.
- Accès par **jeton JWT** émis par Laravel : seuls les élèves de la classe entrent, avec un nom non modifiable.
- Le prof est seul modérateur. Tout le monde arrive micro et caméra coupés, et seul le prof donne la parole.
- Main levée synchronisée avec celle de l'app, salle d'attente, chat désactivé par défaut.
- Reprends exactement `design/apercu-visio.html` et `design/resources/views/visio.blade.php`.
**Critère :** un prof et deux élèves fictifs rejoignent la visio ; un élève lève la main, le prof lui donne la parole, et un intrus sans jeton ne peut pas entrer.
### Étape 16 : Nettoyage
Propose de retirer `/etc/sudoers.d/90-adam`.
---
## 4. Fin de mission
Écris `docs/bilan.md`, avec :
- ce qui fonctionne, avec la preuve de chaque critère ;
- le temps réel passé par étape ;
- la capacité mesurée (nombre d'élèves en même temps) ;
- les dettes et les risques connus ;
- **ce qu'Adam doit faire lui-même** : secrets à changer, comptes à créer, tests avec un prof, etc. ;
- les prochaines étapes du bloc A :
- self-healing et post-mortem ;
- montée de version d'OpenStack ;
- pilote avec une vraie classe.
Commence par l'étape 1.