Aller au contenu
Cloud medium

feint : de vraies machines derrière l'API, et SSH sur les trois clouds

18 min de lecture

Donnez un runtime à feint et un serveur créé par l'API devient une machine réelle, sur un vrai pont réseau, portant l'adresse que l'API a publiée, et dans laquelle vous ouvrez une session SSH. C'est ce qui sépare un émulateur d'un serveur de maquettes : le bloc demandé est le bloc obtenu, un groupe de sécurité ferme réellement un port, et la machine répond. Cette page compare les quatre modes, donne la procédure Incus et OVN sur Debian 13, et déroule le parcours SSH sur les trois fournisseurs.

  • Choisir le mode machine selon ce que vous voulez prouver.
  • Installer Incus et OVN sur Debian 13, câblage OVN compris.
  • Vérifier que la machine porte l'adresse publiée par l'API.
  • Ouvrir une session SSH sur Scaleway, Outscale et Exoscale émulés.

Par défaut, feint est un plan de contrôle : il répond, et rien ne tourne. Donnez-lui un runtime et un serveur créé devient un conteneur ou une machine virtuelle réelle, sur un vrai pont réseau, portant l'adresse que l'API a publiée. C'est la différence entre un émulateur et un simple serveur de maquettes.

Les quatre modes ne délivrent pas la même chose, et un seul écart compte vraiment :

--vmMachinesAdressesPare-feuIsolation inter-VPCNoyau propreNécessite
off (défaut)nonnonnonnonnonrien
incusouiouiouinonnonIncus 6.0.4+
incus-vmouiouiouinonouiIncus + KVM
incus-ovnouiouiouiouinonIncus + OVN

L'isolation est la seule capacité qui change de verdict selon le mode. Deux réseaux privés de deux VPC différents ne doivent pas se joindre. En mode pont, ils se joignent, et la cause est documentée par le runtime lui-même : le trafic entre ponts gérés sur un même hôte est routé directement. Un réseau OVN est un réseau logique doté de son propre routeur, donc la séparation vient de la topologie et non d'une règle.

Plutôt que de deviner, demandez à l'émulateur ce que son mode sait prouver :

Fenêtre de terminal
feint start --vm incus
curl -s localhost:4599/_feint/health | jq -c '.capabilities'
{"machines":true,"addresses":true,"firewall":true,"isolation":false,"own_kernel":false}

Le même appel en --vm incus-ovn renvoie "isolation":true. Cette déclaration est faite pour être lue par vos tests : une suite qui compare un nom de mode en dur doit être modifiée à chaque évolution, une suite qui lit les capacités continue de fonctionner.

Debian 13 est la distribution la plus simple pour ce mode, et la procédure tient en trois temps : le dépôt, les paquets, le câblage OVN. C'est ce dernier que personne ne devine, et sans lui incus network create --type=ovn échoue avec une erreur qui ne nomme ni la cause ni le réglage manquant.

Un mot sur le choix du dépôt, parce qu'il se discute. Debian 13 « trixie » package Incus 6.0.4, exactement le plancher en dessous duquel les listes de contrôle d'accès sur une interface sont refusées. Le paquet de la distribution suffit donc, contrairement à Ubuntu 24.04 qui livre 6.0.0 et ne bougera plus. Le dépôt Zabbly, publié par le mainteneur d'Incus, apporte la série 7.2 recommandée, celle sur laquelle les mesures du projet ont été faites. Sur ma VM Debian 13, apt-cache policy incus affiche bien 6.0.4-2+deb13u8 avant l'ajout du dépôt.

  1. Installer les prérequis et récupérer la clé du dépôt dans son propre trousseau. Une clé déposée dans /etc/apt/keyrings ne signe que le dépôt qui la nomme, là où le magasin système vaudrait pour tout.

    Fenêtre de terminal
    apt-get update
    apt-get install -y --no-install-recommends ca-certificates curl gpg uidmap
    mkdir -p /etc/apt/keyrings
    curl -fsSL https://pkgs.zabbly.com/key.asc -o /etc/apt/keyrings/zabbly.asc
    gpg --show-keys --with-fingerprint /etc/apt/keyrings/zabbly.asc

    Vérifiez l'empreinte affichée avant de continuer : elle doit valoir 4EFC 5906 96CB 15B8 7C73 A3AD 82CC 8797 C838 DCFD, pour Zabbly Kernel Builds. C'est le seul moment où vous décidez à qui vous faites confiance.

  2. Déclarer le dépôt, en dérivant la suite et l'architecture de la machine plutôt qu'en les écrivant en dur.

    Fenêtre de terminal
    cat > /etc/apt/sources.list.d/zabbly-incus.sources <<EOF
    Enabled: yes
    Types: deb
    URIs: https://pkgs.zabbly.com/incus/stable
    Suites: $(. /etc/os-release && echo "$VERSION_CODENAME")
    Components: main
    Architectures: $(dpkg --print-architecture)
    Signed-By: /etc/apt/keyrings/zabbly.asc
    EOF

    Sur Debian 13, Suites vaut trixie. Aucun curl | sh dans cette procédure : chaque fichier est écrit sur disque, et la clé est vérifiée avant que le moindre paquet n'arrive.

  3. Installer Incus, OVN et Open vSwitch. Incus vient de Zabbly, les paquets OVN de Debian.

    Fenêtre de terminal
    apt-get update
    apt-get install -y --no-install-recommends \
    incus incus-client ovn-central ovn-host openvswitch-switch

    Contrôle immédiat : incus --version doit répondre 7.2 et ovn-nbctl --version ovn-nbctl 25.03.0 sur une trixie à jour.

  4. Démarrer les services, puis attendre. Les unités systemd rendent la main avant que les bases de données OVN n'écoutent, et une commande lancée dans cet intervalle échoue sans raison apparente.

    Fenêtre de terminal
    systemctl enable --now openvswitch-switch ovn-central ovn-host
    while [ ! -S /run/ovn/ovnnb_db.sock ]; do sleep 1; done
  5. Indiquer à Open vSwitch où se trouve la base southbound. C'est la moitié du câblage, celle qui relie le commutateur au plan de contrôle OVN.

    Fenêtre de terminal
    ovs-vsctl set open_vswitch . \
    external_ids:ovn-remote=unix:/run/ovn/ovnsb_db.sock \
    external_ids:ovn-encap-type=geneve \
    external_ids:ovn-encap-ip=127.0.0.1
  6. Initialiser Incus, puis donner au pont d'uplink la plage que les réseaux OVN viendront y découper.

    Fenêtre de terminal
    incus admin init --minimal
    incus network set incusbr0 ipv4.address=10.108.0.1/24
    incus network set incusbr0 \
    ipv4.dhcp.ranges=10.108.0.10-10.108.0.99 \
    ipv4.ovn.ranges=10.108.0.100-10.108.0.199

    L'adresse se fixe dans une commande séparée des plages : celles-ci sont validées contre l'adresse courante du pont, et incus admin init --minimal en choisit une au hasard. Sans ipv4.ovn.ranges, la création d'un réseau OVN est refusée avec un message qui accuse le réseau plutôt que le pont.

  7. Vérifier par un vrai réseau, pas par la liste des paquets installés.

    Fenêtre de terminal
    incus network create ovntest --type=ovn network=incusbr0
    incus network delete ovntest

    Network ovntest created valide l'ensemble du câblage. C'est le seul contrôle qui prouve quelque chose : des paquets installés mais mal reliés passent tous les autres.

Ne désactivez pas AppArmor. La tentation existe, parce que l'intégration continue du projet OVN coupe AppArmor avant ses tests système. Ce contournement vise des binaires compilés depuis les sources, hors des chemins couverts par les profils packagés. Une installation par paquets embarque les profils dont elle a besoin : sur la VM de test, Incus, OVN et Open vSwitch fonctionnent tous avec AppArmor actif. Couper un contrôle d'accès obligatoire pour faire tourner un outil de test est un mauvais échange.

Le verdict final ne se lit pas dans apt, mais dans la capacité déclarée :

Fenêtre de terminal
feint doctor --vm auto
feint start --vm auto
curl -s localhost:4599/_feint/health | jq -c '.machines, .capabilities.isolation'

Sur la VM Debian 13 fraîchement installée, doctor annonce machine runtime: incus-ovn avec isolation true, et l'API de santé confirme "incus-ovn" puis true. Une valeur false signifie qu'OVN est absent, ou présent mais non câblé.

La machine porte-t-elle vraiment l'adresse publiée ?

Section intitulée « La machine porte-t-elle vraiment l'adresse publiée ? »

Oui, et cela se vérifie en trois commandes. Créez un réseau privé sur un bloc choisi, rattachez-y un serveur, démarrez-le, puis demandez au runtime, pas à l'émulateur, ce que la machine porte.

Fenêtre de terminal
# le bloc demandé est le bloc obtenu
curl -s -X POST "http://127.0.0.1:4599/vpc/v2/regions/fr-par/private-networks" \
-H 'Content-Type: application/json' \
-d '{"name":"lab","subnets":["10.51.0.0/24"]}' | jq -c '{id, subnet: .subnets[0].subnet}'
# ce que l'API publie
curl -s "http://127.0.0.1:4599/ipam/v1/regions/fr-par/ips" | jq -r '.ips[0].address'
# ce que la machine porte, vu du runtime
incus exec "feint-scw-<id-du-serveur>" -- ip -4 -o addr show

Sur mon poste, l'IPAM publie 10.51.0.2/24 et le conteneur porte 10.51.0.2/24 : les deux adresses coïncident. Le même essai en --vm incus-ovn donne 10.52.0.2/24 des deux côtés. La machine créée s'appelle feint-scw-<identifiant du serveur>, ce qui permet de la retrouver sans chercher.

Tout ce que l'émulateur crée est étiqueté, et feint clean retire exactement ces objets, sans toucher au reste de votre hôte :

Fenêtre de terminal
feint clean --vm incus
removed 1 machine(s), 1 network(s), 1 rule set(s)

Si Incus n'est pas encore installé sur votre poste, la section Incus du site couvre la mise en place.

Comment ouvrir une session SSH sur une machine émulée

Section intitulée « Comment ouvrir une session SSH sur une machine émulée »

L'adresse publique que l'API publie est réellement routée, et vous pouvez vous y connecter en SSH sur les trois fournisseurs. La section précédente portait sur le plan privé, l'adresse du sous-réseau ; celle-ci porte sur le plan public, celui par lequel on ouvre une session. C'est le point où l'émulation cesse d'être une affaire de réponses JSON : vous enregistrez une clé par l'API du fournisseur, vous créez un serveur, et vous ouvrez une session dessus avec le client SSH ordinaire, exactement comme sur le cloud réel.

Session SSH ouverte sur une machine créée par l'API Scaleway émulée, à l'adresse 203.0.113.2 publiée par l'API

Chaque fournisseur publie ses adresses dans son propre bloc de documentation, et ce détail a une raison précise : deux clouds émulés sur la même machine ne doivent jamais router le même /32.

FournisseurBloc publiéNormeUtilisateur par défaut
Scaleway203.0.113.0/24TEST-NET-3root
Outscale198.51.100.0/24TEST-NET-2outscale
Exoscale192.0.2.0/24TEST-NET-1celui que déclare le template

Ces plages sont réservées à la documentation par la RFC 5737 : elles ne sont routables nulle part sur Internet, ce qui en fait exactement le bon choix pour une adresse publique qui n'existe que sur votre poste. Le parcours ci-dessous demande un runtime de machines actif, donc --vm incus au minimum.

Fenêtre de terminal
ssh-keygen -t ed25519 -N '' -C feint-lab -f ./id
eval "$(feint env scaleway)"
# la clé passe par l'API IAM, comme sur le vrai Scaleway
scw iam ssh-key create name=feint-lab public-key="$(cat ./id.pub)"
# une adresse flexible, puis le serveur qui la porte
ip_id=$(scw instance ip create zone=fr-par-1 -o json | jq -r '(.ip // .).id')
srv=$(scw instance server create name=ssh-lab type=DEV1-S zone=fr-par-1 ip="$ip_id" -o json | jq -r .id)
scw instance server start "$srv" zone=fr-par-1
# l'adresse que l'API publie est celle où l'on se connecte
ip=$(scw instance server get "$srv" zone=fr-par-1 -o json | jq -r '.public_ips[0].address')
ssh -F /dev/null -i ./id -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "root@$ip"

Une fois dans la machine, l'interface porte l'adresse publiée en /32, à côté de l'adresse privée du réseau :

ssh-lab
root
eth0 203.0.113.2/32
eth0 10.209.84.9/24

Une adresse privée de sous-réseau répond depuis l'hôte en mode pont, mais pas en OVN, et l'émulateur le déclare au lieu de le laisser deviner. La cause tient à la topologie : le routeur logique qui sépare deux VPC traduit aussi les connexions venues de l'hôte au retour.

Fenêtre de terminal
curl -s localhost:4599/_feint/health | jq -c '.capabilities'
{"machines":true,"addresses":true,"firewall":true,"isolation":false,"own_kernel":false,"private_from_host":true}

En --vm incus-ovn, la même requête renvoie "isolation":true et "private_from_host":false. Les deux capacités varient donc en sens inverse, et c'est le compromis à connaître : le mode qui isole vraiment deux VPC est celui depuis lequel votre poste ne joint pas une adresse interne. Le plan public reste joignable dans les deux modes, ce qui est précisément pourquoi les parcours SSH ci-dessus passent par une adresse publique plutôt que par l'adresse privée.

  • Par défaut feint ne fait tourner aucune machine : le mode off est un plan de contrôle, et c'est ce que la CI exécute.
  • Seul --vm incus-ovn isole deux VPC, au prix d'une adresse privée que l'hôte ne joint plus.
  • La machine porte l'adresse publiée par l'API, vérifiable depuis le runtime plutôt que depuis l'émulateur.
  • Une session SSH s'ouvre sur les trois fournisseurs, chacun sur son bloc d'adresses de documentation.
  • feint clean ne retire que ce que l'émulateur a créé, le reste de l'hôte n'est jamais touché.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn