Invariants et pièges#
Contraintes découvertes en production ou en corrigeant des bugs. Les enfreindre casse quelque chose qui fonctionne, souvent en silence.
Note
Cette page reprend la section « Invariants et pièges » de CLAUDE.md, qui reste la
référence de développement et fait foi en cas d’écart.
Réseau#
Ne pas retirer la route ``/32`` vers ``169.254.169.254`` de l’option 121, même quand elle paraît redondante avec la route par défaut. La DNAT vers le serveur de metadata est posée dans le netns du VPC en
PREROUTING: le paquet n’y est traité en L3 que si son next-hop estinterface_ip. Avec un autre next-hop, la trame est commutée en L2 sans traverserPREROUTING, et le provisionnement cloud-init échoue.RFC 3442 : un client qui lit l’option 121 ignore l’option 3. Toute route par défaut doit donc figurer dans la 121 ; l’option 3 ne sert que les clients qui n’implémentent pas la 121.
La route vers le CIDR du VPC garde ``interface_ip`` comme next-hop dans tous les modes sauf
bridge: sur un subnet à IP publique, le trafic interne ne doit pas sortir par la gateway publique.169.254.169.254est centralisé dansmetadata.ServiceIP— ne pas le réécrire en dur.
QEMU et VM#
Un seul disque ``vdX`` par VM ; les disques additionnels passent par le SCSI (
sdX). La carte PCI en dépend : NIC en0x03, contrôleur SCSI en0x1e, virtio-blk en0x1f.bus=pci.0est explicite sur les trois-device: un passage de la machine en q35 casserait le démarrage (Bus 'pci.0' not found).QEMU est lancé par
systemd-run --scope, jamais en unit transitoire : le scope est exécuté par le processus appelant et hérite du netns posé parnetns.Call. Une unit transitoire, forkée par PID 1, démarrerait dans le netns racine et ne verrait pas le tap.L’arrêt d’une VM ne touche jamais aux fichiers disque. En revanche, un
quitbrutal est envoyé à l’expiration dedispatcher.timeout_seconds.
Arrêt de l’agent#
Ordre imposé : serveurs HTTP → drainage des workers → fermeture de la base. L’inverser crée une course.
Budget d’arrêt dépassé ⇒ la base n’est pas fermée : fermer Badger sous un écrivain concurrent est pire qu’un rejeu du journal au démarrage suivant.
Jamais de
log.Fataldans une goroutine :os.Exitn’exécute aucundefer.
cloud-init#
network-config.tmplcibleeth0alors que les guests sont enens3: il ne s’applique donc à rien, et le réseau vient du DHCP. Ne pas le « corriger » ni le supprimer — le rendre opérant ferait remplacer par cloud-init la configuration réseau de l’image sur toutes les VM.Un document fourni par l’appelant est servi verbatim ; un document absent retombe sur le template ; un document explicitement vide est servi vide. Les trois cas sont distincts.
metadata.passwordest un hash, pas un mot de passe en clair.instance-idvaut le nom de la VM : recréer une VM du même nom sur le même disque fait que cloud-init la reconnaît et n’applique pas le user-data.
Configuration#
Le chargement se fait par viper : tags
mapstructure, jamaisyaml.Un chemin configurable se propage par les signatures de fonction, jamais par une variable ou un setter de paquet.
Sécurité connue et acceptée#
Ces points sont documentés parce qu’ils sont assumés en l’état, pas parce qu’ils sont sans conséquence. Ils doivent être réévalués avant toute exposition élargie de l’API.
L’API n’a aucune authentification et l’exemple de configuration l’expose sur
0.0.0.0:8080. Quiconque atteint ce port pilote le host KVM.vm/<name>/passwordest stocké tel quel (c’est un hash) et restitué par/db?prefix=vm/du serveur d’administration — contenu paradmin.enabled: falseet l’écoute en boucle locale./run/two/metadata/<vm>/vendor-dataest en0644et contient ce hash : tout compte local du host peut le lire.pkg/systemd.New()n’a pas de timeout : si le socket D-Bus accepte sans répondre, l’appelant se fige. Concerne le watchdog, la création et la suppression de subnets, et le serveur de metadata.Il n’y a pas de rollback : un échec partiel de création laisse des objets réseau orphelins jusqu’à un
DELETEexplicite.