299 lines
15 KiB
Markdown
299 lines
15 KiB
Markdown
|
|
# Prompt Claude Code : déploiement complet de Paillasse
|
|||
|
|
|
|||
|
|
> À coller dans Claude Code, lancé depuis la racine du dépôt `Paillasse` sur le Mac d'Adam.
|
|||
|
|
> Remplacer les valeurs entre `<...>` avant de lancer.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 0. Contexte
|
|||
|
|
|
|||
|
|
Tu vas déployer **Paillasse** 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.
|
|||
|
|
|
|||
|
|
**Paillasse** : 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.
|
|||
|
|
- **Une seule IP publique** : `<IP_SERVEUR>`.
|
|||
|
|
- Debian `<12 ou 13>`, installé par Adam depuis la console Dedibox.
|
|||
|
|
- Accès SSH : `ssh <UTILISATEUR>@<IP_SERVEUR>`, avec la clé dédiée `~/.ssh/<CLE_CLAUDE>`. Crée l'alias `paillasse` dans `~/.ssh/config`.
|
|||
|
|
- 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.
|
|||
|
|
|
|||
|
|
**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** : `<DOMAINE>`. Adam l'achète et configure le DNS lui-même.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. Règles non négociables
|
|||
|
|
|
|||
|
|
1. **Ne jamais toucher à `tripoli-cloud`** ni à aucune autre machine que le serveur Paillasse 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 pseudonyme et un code. Pas d'e-mail, pas de nom complet obligatoire.
|
|||
|
|
- 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
|
|||
|
|
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 la Debian installée. Si elle ne supporte pas Debian 13, **arrête-toi** et recommande à Adam de réinstaller en Debian 12.
|
|||
|
|
- É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 <IP_SERVEUR>` ne montre que 22, 80 et 443.
|
|||
|
|
|
|||
|
|
### Étape 3 : Sauvegardes vers Dedibackup
|
|||
|
|
|
|||
|
|
- Utilise **restic**, chiffré, avec **rclone** comme backend FTP vers Dedibackup.
|
|||
|
|
- Le mot de passe restic est dans un fichier root en `chmod 600`.
|
|||
|
|
- À sauvegarder : `/etc`, `/home`, `/root`, `/etc/kolla` (plus tard), les dumps MariaDB de l'app (plus tard), le dossier du portail.
|
|||
|
|
- Programmation : timer systemd quotidien, rétention 7 jours / 4 semaines / 3 mois.
|
|||
|
|
- Écris `docs/runbooks/sauvegardes.md`.
|
|||
|
|
|
|||
|
|
**Critère :** une sauvegarde est faite 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 officielle :
|
|||
|
|
- `qemu-guest-agent` ;
|
|||
|
|
- un getty sur `ttyS0` ;
|
|||
|
|
- l'utilisateur `eleve` ;
|
|||
|
|
- les outils réseau des labs (iproute2, iputils, tcpdump, traceroute, dnsutils, et frr si le lab l'exige).
|
|||
|
|
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.
|
|||
|
|
- 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 minimal
|
|||
|
|
|
|||
|
|
- 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 Inertia et React (starter kit officiel), dans `portail/`.
|
|||
|
|
- Modèles :
|
|||
|
|
- `Etablissement`, `User` (prof), `Classe`, `Eleve` (pseudonyme et code uniquement) ;
|
|||
|
|
- `Lab`, `Seance`, `LabSession` (élève, lab, id de la stack ou des VM, état, expiration).
|
|||
|
|
- Connexion :
|
|||
|
|
- le prof avec un e-mail et un mot de passe ;
|
|||
|
|
- l'élève avec un code de classe et un code élève, sur un seul écran `/connexion`.
|
|||
|
|
- Un prof crée une classe et génère les codes de ses élèves.
|
|||
|
|
- Design : la palette et les polices de `CLAUDE.md`, sobre.
|
|||
|
|
- Tests **Pest** pour la connexion 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.
|
|||
|
|
|
|||
|
|
### É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 sur le port 443.
|
|||
|
|
- 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 (deux LAN et un routeur), d'après `paillasse-contenus/` :
|
|||
|
|
- 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é (Adam a configuré le DNS)
|
|||
|
|
|
|||
|
|
- 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 :** `https://<DOMAINE>` affiche un certificat valide, et le parcours prof → élève → lab → correction fonctionne en HTTPS.
|
|||
|
|
|
|||
|
|
### É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.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 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.
|