Lab de test multi-nœud ====================== Ce qui fait l'intérêt de two ne se voit qu'à partir de **deux hyperviseurs** : sur un nœud isolé, le trafic reste sur le bridge local et l'absence de plan de contrôle passe inaperçue (voir :doc:`/deploiement/architecture-cluster`). Le lab reproduit la topologie du cluster — hyperviseurs, route reflector, switch L3 — sous forme de VM, sur un serveur physique loué à l'heure. Le pourquoi des choix (serveur physique plutôt que VM cloud, câbles QEMU, MTU 9000, versions) est consigné sur le ticket `#50 `_. Cette page décrit comment s'en servir. .. note:: État actuel : étapes **E0** à **E3** livrées — le cycle de vie du serveur qui porte le lab (``scripts/lab-host.sh``), la description de la topologie et le calcul de son plan (``lab plan``), la génération des arguments QEMU et des fichiers cloud-init de chaque VM (``lab render``), puis leur lancement sur le serveur (``lab up`` / ``status`` / ``down`` / ``ssh``). Les rôles — FRR sur le switch et le route reflector, two sur les hyperviseurs — viendront avec l'étape suivante. Le serveur de lab ----------------- Un serveur **Scaleway Elastic Metal**, créé pour une campagne de tests puis supprimé. .. list-table:: :widths: 25 75 * - Offre - ``EM-B212X-SSD``, zone ``fr-par-1``, **facturation horaire** : 0,321 € HT de l'heure, sans frais de mise en service * - Matériel - 2 × Xeon E5-2620 v4 *or equivalent*, 256 Go, 2 × 1 To SSD * - Système - Debian 12, installé par Scaleway à la création * - Pourquoi Intel - lab3 est en Intel : two lance ses VM en ``-cpu host``, et KVM a deux implémentations distinctes (``kvm_intel``, ``kvm_amd``) *Or equivalent* n'est pas une clause de style : le premier serveur livré était un **Xeon E5-2640 v3** (Haswell, la génération de lab3), pas le E5-2620 v4 annoncé. Relever ``lscpu`` au début de chaque campagne. Prérequis côté Scaleway ----------------------- #. **Un projet dédié au lab**, séparé de toute autre ressource. ``lab-host.sh down`` supprime tout serveur de lab du projet : il ne doit rien y avoir d'autre. #. **Une clé d'API limitée à ce projet**, avec les droits Elastic Metal et la lecture des clés SSH du projet. Rien d'autre. #. **Les clés SSH publiques enregistrées dans le projet**, injectées à l'installation : sans elle, le serveur serait facturé sans que personne puisse s'y connecter, et ``plan`` refuse de continuer. #. **Le quota Elastic Metal.** L'``EM-B212X-SSD`` exige un compte dont le moyen de paiement *et* l'identité sont validés ; le quota est alors de 2. Vérifier dans la console : Organisation → Quotas → Elastic Metal. Voir `les quotas Scaleway `_. Fichiers locaux --------------- Tout ce dont le script a besoin vit sous ``~/.config/two-lab/``, hors du dépôt. ``~/.config/two-lab/scaleway.env`` Identifiants Scaleway, une ligne ``CLÉ=valeur`` chacun : .. code-block:: bash SCW_SECRET_KEY= SCW_DEFAULT_PROJECT_ID= SCW_DEFAULT_ZONE=fr-par-1 * le fichier doit être en ``0600`` : le script refuse de s'en servir s'il est lisible par d'autres que son propriétaire ; * il est **lu, jamais exécuté** — pas de ``source`` ; seules ces trois clés sont reconnues ; * une variable d'environnement du même nom l'emporte sur le fichier ; * la clé d'accès (``SCW…``) n'est pas nécessaire : l'API REST n'authentifie que par la clé secrète, dans l'en-tête ``X-Auth-Token``. Pour changer de clé, remplacer la ligne ``SCW_SECRET_KEY=`` ; rien d'autre à modifier. ``~/.config/two-lab/ssh/lab_ed25519`` Clé SSH dédiée au lab, **sans phrase de passe**, pour que les sessions tournent sans intervention. Sa partie publique doit être enregistrée dans le projet. Quand elle existe, le script l'utilise **seule** (``IdentitiesOnly``, agent désactivé) ; sinon il retombe sur l'agent SSH. Elle ne doit ouvrir que les serveurs éphémères du projet de lab : **ne jamais l'installer sur lab3 ni sur une machine durable**. En cas de doute, la retirer du projet et en générer une autre : .. code-block:: bash mkdir -p ~/.config/two-lab/ssh && chmod 700 ~/.config/two-lab ~/.config/two-lab/ssh ssh-keygen -t ed25519 -N '' -C two-lab-automation -f ~/.config/two-lab/ssh/lab_ed25519 ``~/.cache/two-lab/`` État de la session en cours : adresse et utilisateur du serveur, ``known_hosts`` dédié. Vidé par ``down``. Commandes --------- .. code-block:: text usage: lab-host.sh [arguments] plan résout l'offre horaire, l'OS et les clés SSH, affiche la requête de création et le prix ; ne crée rien up crée le serveur de lab, attend la fin de son installation et son SSH, puis le prépare (voir prepare) status liste les serveurs de lab du projet ssh [commande] se connecte au serveur de lab ; avec une commande, un terminal n'est demandé que si l'entrée standard en est un prepare installe sur le serveur ce dont lab a besoin (qemu, genisoimage), vérifie /dev/kvm et la virtualisation imbriquée ; lancé aussi par up push compile cmd/lab pour linux/amd64 et dépose sur le serveur ~/lab et ~/ ; ensuite : ssh './lab up ' down supprime tous les serveurs de lab du projet et attend leur disparition session [cmd] up, puis la commande distante (ou un shell), puis down quoi qu'il arrive ``plan`` et ``status`` sont **gratuits** ; ``up`` et ``session`` **créent un serveur facturé**. Toujours commencer par ``plan``. Il valide la clé d'API, le quota d'offre, l'OS et les clés SSH, et montre exactement ce qui serait commandé : .. code-block:: text $ scripts/lab-host.sh plan == offre : EM-B212X-SSD (ddaf8ba6-b2b2-4279-8af3-51930fb602f8), facturation hourly, stock available == prix : 0.321 EUR HT par heure, frais de mise en service 0 EUR == os : Debian 12 (Bookworm) (83640d93-a0b8-45ad-9c9f-30cae48380a4), utilisateur root == clés : 2 clé(s) SSH du projet == requête : POST /baremetal/v1/zones/fr-par-1/servers ``session`` est la forme normale d'usage : le serveur est supprimé à la fin, que la commande réussisse, échoue, ou que la session soit interrompue (Ctrl-C, ``TERM``, fermeture du terminal). .. code-block:: text $ scripts/lab-host.sh session 'uname -a; lscpu | grep -E "Model name|^CPU\(s\)|Virtualization"; free -g | head -2; echo "nested=$(cat /sys/module/kvm_intel/parameters/nested)"; ls -l /dev/kvm' == création de two-lab (EM-B212X-SSD, 0.321 EUR/h HT) == serveur 2ecc1e6a-0de8-48c0-a198-93857eee5957 créé, facturé jusqu'à 'lab-host.sh down' == serveur 2ecc1e6a-0de8-48c0-a198-93857eee5957 : ordered, installation to_install == serveur 2ecc1e6a-0de8-48c0-a198-93857eee5957 : ready, installation installing … == serveur 2ecc1e6a-0de8-48c0-a198-93857eee5957 : ready, installation completed == SSH pas encore joignable, nouvel essai dans 20s … == prêt : root@ Linux two-lab 6.1.0-53-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.187-1 (2026-09-07) x86_64 GNU/Linux CPU(s): 32 Model name: Intel(R) Xeon(R) CPU E5-2640 v3 @ 2.60GHz … Virtualization: VT-x total used free shared buff/cache available Mem: 251 1 250 0 0 249 nested=Y crw-rw---- 1 root kvm 10, 232 Oct 3 17:23 /dev/kvm == session terminée (code 0), suppression du serveur == suppression de 2ecc1e6a-0de8-48c0-a198-93857eee5957 == aucun serveur de lab ne reste dans le projet Extrait de la première campagne (lignes répétées remplacées par ``…``). Compter **environ 15 minutes** entre la création et le SSH disponible : 13 min 30 à la première campagne, suppression comprise. SSH ne répond pas tout de suite après la fin de l'installation — environ 100 secondes la première fois — d'où l'attente intégrée à ``up``. Le code de sortie de ``session`` est celui de la commande distante. ``up``, ``ssh`` et ``down`` séparément servent au debug interactif — et laissent la suppression à la charge de l'utilisateur. Une campagne sur le lab enchaîne ces commandes depuis le Mac ; ``lab`` s'exécute sur le serveur (voir `Lancement des VM`_) : .. code-block:: text scripts/lab-host.sh up scripts/lab-host.sh push conf/lab/evpn-2hv.yml scripts/lab-host.sh ssh './lab up evpn-2hv.yml' scripts/lab-host.sh ssh './lab ssh hv1' # shell interactif sur hv1 scripts/lab-host.sh ssh './lab ssh hv1 ip -br a' # commande, code de retour propagé scripts/lab-host.sh down ``push`` transfère par la connexion SSH du script (``cat`` côté serveur, fichier renommé une fois complet) : mêmes options, même clé, même ``known_hosts`` que ``ssh``. Topologie --------- Un lab est décrit par un fichier YAML : des **nœuds** (les VM) et des **segments** (des réseaux L2 portés par un switch). Exemple livré, ``conf/lab/evpn-2hv.yml`` : .. literalinclude:: ../../conf/lab/evpn-2hv.yml :language: yaml Chaque nœud non-switch est relié au switch de chacun de ses segments par un câble virtuel QEMU ; le switch met ces câbles dans un bridge et porte la passerelle du segment. Ce que le fichier déclare : ``images`` ``url`` de l'image qcow2 et ``sums`` du fichier de sommes à vérifier, tous deux en ``https://``. ``segments`` ``switch`` (un nœud de rôle ``switch``), ``cidr`` IPv4 entre ``/8`` et ``/30``, ``mtu`` facultatif — 9000 par défaut, entre 1280 et 9000. Nom : 12 caractères au plus, minuscules et chiffres, parce qu'il devient le nom d'interface dans les VM et, préfixé de ``br-``, celui du bridge (15 caractères au plus sous Linux). ``nodes`` ``role`` (``switch``, ``rr`` ou ``hypervisor``), ``image``, ``cpus``, ``memory`` en Mio (256 au moins), ``segments`` auxquels le nœud est relié, et ``addresses`` pour fixer l'adresse d'un nœud sur un segment (``addresses: {underlay: 10.250.0.50}``). Un switch ne déclare ni ``segments`` ni ``addresses`` : il porte ceux dont il est le ``switch``. Ce que l'outil en déduit, de façon déterministe — même fichier, même plan : .. list-table:: :widths: 30 70 * - Passerelle d'un segment - la première adresse du CIDR, portée par le switch sur ``br-`` * - Adresse d'un nœud - les suivantes, **dans l'ordre de déclaration des nœuds** ; une adresse fixée par ``addresses`` est réservée d'abord et sautée par l'attribution automatique * - Câbles - un par couple (segment, nœud), segments puis nœuds dans l'ordre de déclaration ; le câble *i* utilise les ports UDP ``20000 + 2i`` (côté nœud) et ``20001 + 2i`` (côté switch) * - MAC - ``02:4c::::`` — préfixe localement administré, rang du nœud sur deux octets, rang du segment, ``00`` côté nœud et ``01`` côté switch * - Interfaces - côté nœud, le nom du segment ; côté switch, ``p``, du rang du câble * - SSH d'administration - ``127.0.0.1:<2200 + rang du nœud>`` sur l'hôte du lab .. warning:: Réordonner les nœuds ou les segments dans le fichier **change les adresses, les MAC et les ports**. C'est assumé pour un lab ; ``lab plan`` montre le résultat avant tout lancement. Limites : 1000 nœuds, 256 segments, et autant de câbles que la plage UDP le permet (22 768). ``lab plan`` valide le fichier et affiche le plan, sans rien lancer : .. code-block:: text $ go run ./cmd/lab plan conf/lab/evpn-2hv.yml lab evpn-2hv: nodes 4, segments 1, cables 3 nodes name role image cpus memory ssh sw1 switch debian12 2 1024 MiB 127.0.0.1:2200 rr1 rr debian12 1 1024 MiB 127.0.0.1:2201 hv1 hypervisor debian12 4 16384 MiB 127.0.0.1:2202 hv2 hypervisor debian12 4 16384 MiB 127.0.0.1:2203 segment underlay: 10.250.0.0/24, mtu 9000, switch sw1, bridge br-underlay, gateway 10.250.0.1 node interface address mac udp switch port mac udp rr1 underlay 10.250.0.2/24 02:4c:00:01:00:00 20000 <-> sw1 p0 02:4c:00:01:00:01 20001 hv1 underlay 10.250.0.3/24 02:4c:00:02:00:00 20002 <-> sw1 p1 02:4c:00:02:00:01 20003 hv2 underlay 10.250.0.4/24 02:4c:00:03:00:00 20004 <-> sw1 p2 02:4c:00:03:00:01 20005 Un fichier invalide est refusé avec **toutes** ses erreurs à la fois, et un code de sortie 1. Les champs inconnus et les clés en double sont refusés aussi : .. code-block:: text $ lab plan cassee.yml lab: cassee.yml: segment underlay: rr1 is a rr, not a switch segment underlay: cidr 10.250.0.0/31 prefix length out of range [/8, /30] node sw1: switch carries no segment Les plages d'adresses de l'exemple sont des valeurs de travail : le plan d'adressage du lab reste à définir (#50). Rendu des VM ------------ ``lab render`` produit, pour chaque nœud, ce qu'il faut pour démarrer sa VM — sans rien lancer : .. code-block:: text $ go run ./cmd/lab render -key ~/.config/two-lab/ssh/lab_ed25519.pub conf/lab/evpn-2hv.yml ``//`` reçoit : ``qemu.args`` Les arguments de ``qemu-system-x86_64``, **un par ligne** : rien à échapper, rien à interpréter par un shell. ``meta-data``, ``user-data``, ``network-config`` Les trois fichiers NoCloud de cloud-init, à mettre dans une image de volume ``cidata``. Les chemins de la VM (``disk.qcow2``, ``seed.iso``, ``console.log``, ``qmp.sock``, ``qemu.pid``) sont ceux du répertoire du nœud ; ``-key`` peut être répété, et accepte un fichier ``authorized_keys`` (lignes vides et commentaires ignorés). Les fichiers sont créés en ``0600``. Ce que contiennent les arguments QEMU d'un hyperviseur — extrait réel, côté réseau : .. code-block:: text -netdev user,id=mgmt0,restrict=on,ipv6=off,hostfwd=tcp:127.0.0.1:2202-:22 -device virtio-net-pci,netdev=mgmt0,mac=02:4d:00:02:00:00,romfile= -netdev dgram,id=underlay,local.type=inet,local.host=127.0.0.1,local.port=20002,remote.type=inet,remote.host=127.0.0.1,remote.port=20003 -device virtio-net-pci,netdev=underlay,mac=02:4c:00:02:00:00,host_mtu=9000,romfile= Les choix qui s'y lisent : * **machine** ``q35``, ``-accel kvm -cpu host`` — le KVM imbriqué des hyperviseurs du lab en dépend ; ``-nodefaults`` pour qu'aucun périphérique implicite ne s'ajoute ; * **administration** (``mgmt0``) : le NAT de QEMU, MAC ``02:4d:::00:00``, SSH redirigé sur la boucle locale de l'hôte. ``restrict=on`` pour tous les nœuds **sauf le switch** : un nœud isolé ne joint ni l'hôte ni l'extérieur par là, seule la redirection SSH passe. ``ipv6=off`` partout (voir plus bas) ; * **câbles** : ``dgram`` sur ``127.0.0.1``, les deux extrémités d'un câble se répondent (port local de l'une = port distant de l'autre), ``host_mtu`` annonce le MTU du segment au guest ; * ``romfile=`` vide sur toutes les cartes : pas de ROM de démarrage réseau, donc pas de repli sur un démarrage PXE si le firmware ne trouve pas le disque. Pendant les essais de #50, une VM restée bloquée sans rien écrire sur sa console, CPU au repos, avait toutes les apparences de ce repli ; la cause n'a pas été isolée, l'option est une précaution. Ce que fait cloud-init : * **toutes les VM** : interfaces nommées d'après leur MAC (``mgmt0``, nom du segment, ``p``), ``dhcp4: false`` partout, ``mgmt0`` en ``10.0.2.15/24`` **sans passerelle** ; connexion SSH par clé seulement, utilisateur ``debian``, ``root`` désactivé, mot de passe refusé ; * **un nœud** : adresse sur chaque segment, MTU du segment, route par défaut et DNS (``1.1.1.1``, ``8.8.8.8``) sur son **premier** segment — la sortie Internet passe par le switch ; * **le switch** : route par défaut par ``mgmt0`` ; un service ``lab-switch`` crée un bridge ``br-`` par segment (STP désactivé, MTU du segment), y branche ses ports, porte la passerelle, active le routage et masque (NAT nftables) les segments vers ``mgmt0``. Le service est rejoué à chaque démarrage. Vérifié sur de vraies VM ~~~~~~~~~~~~~~~~~~~~~~~~ Le switch et le route reflector n'ont pas besoin de KVM imbriqué : ``sw1`` et ``rr1`` de l'exemple ont été démarrés **sur un Mac**, en émulation (TCG), avec Debian 12 ``generic`` et les fichiers produits par ``lab render``. .. list-table:: :header-rows: 1 :widths: 60 40 * - Vérification - Résultat * - interfaces nommées et adressées, bridge ``br-underlay`` en ``10.250.0.1/24`` - conforme * - service ``lab-switch`` actif, y compris après redémarrage - conforme * - ``ping -M do -s 8972`` de ``rr1`` vers le switch (MTU 9000, sans fragmentation) - passe * - ``ping -M do -s 8973`` (MTU 9001) - refusé : ``message too long, mtu=9000`` * - Internet depuis ``rr1`` en IPv4 - passe, par ``10.250.0.1`` * - ``rr1`` vers un service TCP de l'hôte par ``mgmt0`` — le switch, témoin, y parvient - bloqué * - ``rr1`` vers Internet par ``mgmt0`` - bloqué Un défaut trouvé par cet essai, et corrigé : sans ``ipv6=off``, le NAT de QEMU annonce un préfixe IPv6 et ``mgmt0`` reçoit une **route IPv6 par défaut** — vers une impasse, puisque ``restrict=on`` bloque tout. Pas de fuite, mais chaque programme qui tente l'IPv6 d'abord (le DNS renvoie d'abord des adresses IPv6) attend un délai avant de se rabattre sur l'IPv4. .. note:: Un ``ping`` vers ``10.0.2.2`` n'est pas un test d'isolation : c'est la passerelle virtuelle de QEMU qui répond elle-même, ``restrict=on`` ou non. Seule une connexion vers un vrai service de l'hôte, avec un témoin qui y parvient, le prouve. Reste à vérifier sur le serveur de lab : les hyperviseurs, qui exigent KVM imbriqué. Lancement des VM ---------------- ``lab`` s'exécute **sur le serveur de lab**. Il garde l'état du lab dans un répertoire (``-run``, par défaut ``~/lab-run``) : ``status``, ``down`` et ``ssh`` n'ont donc pas besoin de la topologie. .. code-block:: text lab up [-run dir] [-cache dir] [-timeout 20m] lab status [-run dir] lab down [-run dir] lab ssh [-run dir] [commande…] ``lab up`` 1. refuse de continuer si un lab tourne déjà dans le répertoire ; 2. télécharge chaque image dans le cache (``-cache``, par défaut ``~/.cache/two-lab``) et la vérifie contre ``SHA512SUMS`` ; une image déjà présente et toujours conforme n'est pas retéléchargée, la liste des sommes est relue à chaque fois ; 3. génère une paire de clés SSH dans le répertoire du lab, si elle n'existe pas encore ; 4. pour chaque nœud : fichiers de ``lab render``, disque **neuf** en overlay qcow2 sur l'image (``qemu-img create -b``, 20 Gio annoncés), image ``cidata`` (``genisoimage``) ; 5. démarre les QEMU, **switchs d'abord**, détachés (``-daemonize``) : ils survivent à la session SSH qui les a lancés ; 6. attend sur chaque nœud la fin de cloud-init (``cloud-init status --wait`` par SSH), jusqu'au délai ``-timeout``. La topologie est copiée dans ``/topology.yml``. Un échec laisse les nœuds démarrés en place : ``lab status``, puis ``lab down``. ``lab status`` Pour chaque nœud : rôle, état du processus QEMU, PID, port SSH sur la boucle locale. ``lab down`` Arrête chaque QEMU par ``SIGTERM``, puis ``SIGKILL`` au bout de 30 s. Les disques sont conservés jusqu'au prochain ``up``, qui les recrée. ``lab ssh`` Ouvre un shell sur un nœud, ou y exécute une commande, avec la clé générée par ``up``. ``lab`` cède la place à ``ssh``, dont le code de retour est donc celui de la commande. Un terminal n'est demandé (``-t``) que si l'entrée de ``lab`` en est un : depuis un script, ni pseudo-terminal ni ``\r\n`` dans la sortie. Comme ``ssh``, ``lab ssh`` recolle ses arguments par des espaces et les confie à un shell distant — et depuis le Mac, il y en a **deux** : celui du serveur, puis celui de la VM. Une commande qui contient elle-même des guillemets se passe en une seule chaîne : .. code-block:: text $ echo | scripts/lab-host.sh ssh './lab ssh hv1 sh -c "exit 42"'; echo "rc 42=$?" rc 42=0 $ echo | scripts/lab-host.sh ssh "./lab ssh hv1 'sh -c \"exit 42\"'"; echo "rc 42=$?" rc 42=42 Dans le premier cas, la VM reçoit ``sh -c exit 42`` : ``exit`` sans argument, ``42`` en ``$0``. Pour plus d'une commande, passer un script sur l'entrée standard : ``scripts/lab-host.sh ssh "./lab ssh hv1 'sudo bash -s'" < script.sh``. Un processus n'est tenu pour celui d'un nœud que si son PID, lu dans ``qemu.pid``, désigne un processus vivant dont la ligne de commande (``/proc//cmdline``) contient ``-name ``. Un PID réutilisé par un autre programme n'est donc jamais signalé. .. warning:: Le cache range une image sous son nom de fichier, et l'URL de Debian est ``latest`` : une nouvelle publication remplace le fichier, et les overlays existants pointeraient sur une base différente. ``up`` recrée toujours les disques, ce qui suffit avec un lab par serveur ; **ne pas relancer un QEMU à la main** à partir d'un ``qemu.args`` après un ``up`` ultérieur. Une campagne réelle, de la création du serveur à la première commande sur une VM — sorties du 2026-10-04 : .. code-block:: text $ scripts/lab-host.sh up == création de two-lab (EM-B212X-SSD, 0.321 EUR/h HT) … == préparation du serveur : qemu, genisoimage, KVM imbriqué … qemu QEMU emulator version 7.2.22 (Debian 1:7.2+dfsg-7+deb12u18+b3), nested=Y == prêt : root@ $ scripts/lab-host.sh push conf/lab/evpn-2hv.yml == compilation de lab (linux/amd64) == déposés sur le serveur : ~/lab, ~/evpn-2hv.yml — ensuite : lab-host.sh ssh './lab up evpn-2hv.yml' $ scripts/lab-host.sh ssh './lab up evpn-2hv.yml' sw1: started rr1: started hv1: started hv2: started sw1: ready rr1: ready hv1: ready hv2: ready $ scripts/lab-host.sh ssh './lab status' node role state pid ssh sw1 switch running 5158 127.0.0.1:2200 rr1 rr running 5171 127.0.0.1:2201 hv1 hypervisor running 5182 127.0.0.1:2202 hv2 hypervisor running 5196 127.0.0.1:2203 ``lab up`` a pris **49 secondes**, téléchargement et vérification de l'image (427 Mio) compris ; l'essentiel du temps d'une campagne est la livraison du serveur (environ 25 minutes avec ``prepare``). Vérifié sur le serveur de lab ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Le 2026-10-04, sur un Xeon E5-2640 v3, Debian 12 et QEMU 7.2 sur le serveur, topologie ``evpn-2hv`` : .. list-table:: :header-rows: 1 :widths: 60 40 * - Vérification - Résultat * - ``ping -M do -s 8972`` de hv1 à hv2 à travers le switch - passe * - ``ping -M do -s 8973`` - refusé : ``message too long, mtu=9000`` * - sortie Internet de hv1 - par ``10.250.0.1`` (le switch), HTTPS 200 ; aucune route IPv6 globale * - hv1 vers un service TCP du serveur par ``mgmt0`` — sw1, témoin, y parvient (200) - refusé * - hv1 vers Internet par ``mgmt0``, route forcée via ``10.0.2.2`` — sw1 y parvient - refusé * - ``/dev/kvm`` et ``nested`` dans hv1 - présent, ``Y`` * - racine de hv1 (overlay de 20 Gio) - 20 Go : ``growpart`` agrandit la partition au premier démarrage * - code de retour à travers ``lab-host.sh ssh`` et ``lab ssh``, sans terminal - propagé jusqu'au Mac * - ``scripts/lab-host.sh ssh './lab ssh hv1'`` depuis un terminal - shell interactif * - ``lab down`` puis ``lab up`` d'une autre topologie - conforme Facturation ----------- .. warning:: Un serveur Elastic Metal est facturé **de sa création à sa suppression, éteint compris**. Éteindre ne suffit pas : il faut supprimer. La granularité n'est pas documentée par Scaleway — compter chaque heure entamée comme une heure pleine. Ce que le script garantit : * il ne commande **jamais** d'offre mensuelle : il exige une seule offre au nom demandé, en facturation horaire, en stock et sans frais de mise en service, sinon il refuse avant toute création. La CLI ``scw`` n'est pas utilisée pour cette raison : son ``server create type=…`` choisit l'offre par son seul nom et peut tomber sur la mensuelle, qui engage un mois ; * il refuse de créer un second serveur si un serveur de lab existe déjà ; * ``down`` agit sur **tous** les serveurs portant le tag ``two-lab`` dans le projet, et ``session`` y ajoute l'identifiant reçu à la création : un serveur créé juste avant une interruption est rattrapé ; * une suppression refusée pendant la livraison ou l'installation est réessayée tant qu'elle dure, dans la limite du délai d'installation augmenté du délai de suppression ; * le serveur n'est déclaré supprimé qu'au 404 de l'API, jamais sur une erreur passagère ; * un échec de suppression se termine par ``SERVEUR(S) DE LAB TOUJOURS FACTURÉ(S)`` et un code d'erreur. Ce qu'il ne peut pas garantir : un ``SIGKILL``, une coupure de courant ou une mise en veille du poste qui lance la session. En cas de doute, toujours : .. code-block:: bash scripts/lab-host.sh status scripts/lab-host.sh down Diagnostic ---------- ``aucune clé SSH active dans le projet`` Aucune clé SSH n'est enregistrée dans le projet de lab. En ajouter une dans la console (projet → Clés SSH). ``offre horaire EM-B212X-SSD : 0 correspondance(s)`` L'offre n'existe pas dans la zone en facturation horaire. Vérifier ``SCW_DEFAULT_ZONE``. ``création incertaine`` La création a échoué ou n'a pas rendu d'identifiant. Le serveur a pu être créé malgré tout — cas typique : le quota (identité non validée), ou une réponse perdue. Le script indique s'il voit un serveur de lab ; dans tous les cas, lancer ``status`` puis ``down``. ``SSH injoignable`` L'installation est terminée mais SSH ne répond pas après 10 minutes. Avec une clé matérielle (Yubikey), chaque connexion demande un PIN ou un toucher : utiliser la clé dédiée du lab. Le serveur est toujours facturé : ``down``. ``SERVEUR(S) DE LAB TOUJOURS FACTURÉ(S)`` La suppression n'a pas abouti dans les délais. Relancer ``down`` ; si l'erreur persiste, supprimer depuis la console Scaleway. ``HTTP 403 insufficient permissions`` La clé d'API est authentifiée mais n'a pas le droit demandé — en général la lecture des clés SSH du projet. Compléter la politique de la clé, limitée au projet. Sécurité -------- * La clé secrète n'apparaît ni dans les arguments des processus (elle est passée à ``curl`` par un descripteur de fichier), ni dans les journaux, ni dans l'environnement de ``ssh``. * **Ne jamais lancer le script sous** ``bash -x`` : la trace afficherait la clé. * Le serveur n'expose que SSH, par clé. Le lab n'a aucune donnée personnelle ni secret de production. * Une clé secrète qui a circulé ailleurs que dans ``scaleway.env`` (conversation, terminal partagé, capture d'écran) se régénère. * **Clé SSH des VM** : générée par ``lab up`` sur le serveur, c'est la seule clé autorisée dans les VM. Elle ne quitte jamais le serveur, n'ouvre que les VM du lab — qui n'écoutent qu'en boucle locale — et disparaît avec lui. La clé publique du Mac n'est jamais envoyée aux VM. * **Clés d'hôte des VM non vérifiées** par ``lab ssh`` (``known_hosts`` jetable) : elles changent à chaque ``up``. Acceptable uniquement parce que la connexion reste sur la boucle locale d'un serveur auquel on s'est authentifié. * **Image** : ``SHA512SUMS`` vient de la même origine que l'image, en HTTPS. La vérification protège contre la corruption, pas contre une origine compromise ; la signature GPG de Debian (``SHA512SUMS.sign``) n'est pas encore vérifiée. Tests ----- .. code-block:: bash bash scripts/lab-host_test.sh go test ./internal/lab/... ./cmd/lab/ Environ une minute et demie, sans réseau : la suite remplace ``curl`` par une fausse API Scaleway qui se place dans le pire cas (offre mensuelle listée avant l'horaire, serveurs d'autres projets, suppressions refusées, erreurs 503, serveur qui tarde à disparaître) et ``ssh`` par un faux client. Elle tourne sous bash 5 comme sous le bash 3.2 de macOS.