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 est interface_ip. Avec un autre next-hop, la trame est commutée en L2 sans traverser PREROUTING, 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.254 est centralisé dans metadata.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 en 0x03, contrôleur SCSI en 0x1e, virtio-blk en 0x1f.

  • bus=pci.0 est 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é par netns.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 quit brutal est envoyé à l’expiration de dispatcher.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.Fatal dans une goroutine : os.Exit n’exécute aucun defer.

cloud-init#

  • network-config.tmpl cible eth0 alors que les guests sont en ens3 : 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.password est un hash, pas un mot de passe en clair.

  • instance-id vaut 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, jamais yaml.

  • 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>/password est stocké tel quel (c’est un hash) et restitué par /db?prefix=vm/ du serveur d’administration — contenu par admin.enabled: false et l’écoute en boucle locale.

  • /run/two/metadata/<vm>/vendor-data est en 0644 et 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 DELETE explicite.