15 KiB
Prompt Claude Code : déploiement complet de Paillasse
À coller dans Claude Code, lancé depuis la racine du dépôt
Paillassesur 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'aliaspaillassedans~/.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
- Ne jamais toucher à
tripoli-cloudni à aucune autre machine que le serveur Paillasse et le Mac d'Adam. - Aucun secret dans le code, dans Git, dans le journal ou dans la conversation.
- Les secrets vivent dans des fichiers
chmod 600sur le serveur, ou dans un.envignoré par Git. - Les secrets Ansible passent par Ansible Vault. Le mot de passe du vault reste hors du dépôt.
- Kolla :
passwords.ymln'est jamais commité en clair.
- Les secrets vivent dans des fichiers
- 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.
- suppressions (
- 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. - Tu ne commandes et ne paies jamais rien.
- É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.
- 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.
- chaque configuration passe par un rôle Ansible idempotent dans
- 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.
- Documentation en français (docs, commentaires, messages de commit).
2. Méthode de travail
Pour chaque étape
- Annonce l'étape et son critère de réussite.
- Fais le travail.
- Vérifie le critère par un test réel et montre la sortie.
- É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.
git commit, puisgit push.- 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.iniet un rôledurcissement:- utilisateur d'administration,
sudo; - SSH par clé uniquement,
PermitRootLogin no; - fail2ban,
unattended-upgrades, chrony, fuseauEurope/Paris; - pare-feu nftables : seuls 22, 80 et 443 ouverts sur l'IP publique.
- utilisateur d'administration,
- Attention :
kolla-ansible bootstrap-serverspeut 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
dummy0avec une IP privée (par exemple10.10.0.1/24). Elle servira ànetwork_interfaceetkolla_internal_vip_address(Kolla sans HAProxy). - Crée une paire veth.
neutron_external_interfaceutilise 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_prometheusetenable_grafanasi c'est le plus simple pour l'étape 7.
- Mots de passe :
kolla-genpwd. Jamais depasswords.ymlen clair dans Git. - Lance
bootstrap-servers,prechecks,deploy, puispost-deploy. Installe le clientopenstack. - Rejoue le rôle pare-feu si Kolla l'a modifié.
Critère :
openstack service listetopenstack compute service listsont OK ;- aucune API OpenStack n'est joignable depuis l'IP publique (vérifie avec
nmap).
Étape 6 : Première VM en console série
- Crée le projet
paillasseet un utilisateur de service avec des application credentials, stockés dans un fichierchmod 600. - Construis l'image
paillasse-debianavecvirt-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).
- Propriété de l'image :
hw_qemu_guest_agent=yes. - Crée la flavor
eleve.petit: 1 vCPU, 768 Mo, 5 Go. - Démarre une VM et récupère l'adresse de sa console série avec l'API Nova
remote-consoles(protocoleserial).
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 sur127.0.0.1:8081, lancé par systemd. Nginx relaie/consoleen WebSocket sur le port 443. - Déroulé :
- Laravel demande à Nova l'adresse de la console série.
- Laravel la range dans Redis sous un jeton aléatoire, pour 5 minutes.
- Le navigateur reçoit uniquement ce jeton.
- 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.
- réseaux de lab sans DHCP, avec
- 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-execde l'agent QEMU (parvirsh, dans le conteneurnova_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.petiten même temps. Mesure la RAM, le CPU et les temps de démarrage, puis écrisdocs/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.