Aller au contenu
Virtualiser avec libvirt en Wi-Fi sur mon Framework : le chemin de croix du NAT sur Fedora
  1. Posts/

Virtualiser avec libvirt en Wi-Fi sur mon Framework : le chemin de croix du NAT sur Fedora

Fabien ALLAMANCHE
Auteur
Fabien ALLAMANCHE
Géomaticien @ Vienne Condrieu Agglomération
Sommaire
J’ai bien un module Ethernet sur mon Framework 13 — mais pas envie de tirer un câble volant jusqu’à mon switch USW-Flex-2.5G-8-PoE à chaque fois que je bricole sur le canapé devant la télé. Résultat : mes VMs libvirt devaient composer avec la seule connexion disponible dans ce contexte, le Wi-Fi. Monter mes premières VMs aurait dû être une formalité — ça s’est transformé en séance de spéléologie dans trois couches réseau différentes. Ou comme dirait Microlinux, je ne pensais pas devoir sauter à travers des cerceaux en feu pour configurer le réseau !

Bridge vs NAT en Wi-Fi
#

Première question à trancher : peut-on faire du bridge ou du NAT libvirt quand on ne veut brancher que le Wi-Fi ?

Un bridge Ethernet classique (br0) ne fonctionne pas sur une interface Wi-Fi. Le protocole 802.11 n’accepte qu’une seule adresse MAC par association à un point d’accès, alors qu’un bridge fait circuler les trames avec les MAC de chaque VM directement sur le lien physique. Un bridge standard sur wlp1s0 ne route donc quasiment rien.

Deux alternatives existent :

  • NAT (le réseau default de libvirt, basé sur virbr0) — routage IP classique, indépendant du type de connexion internet de l’hôte. L’option la plus simple et la plus fiable en Wi-Fi.
  • macvlan en mode bridge — permet aux VMs d’obtenir une IP directement sur le réseau local, avec certaines limites (pas de communication directe hôte ↔ VM).

Pour un usage de test/dev, j’ai choisi le NAT.

Créer la VM en ligne de commande
#

Direction virt-install, l’équivalent CLI de la création de VM (un peu comme qm create sous Proxmox) :

sudo virt-install \
  --name fedora \
  --memory 8192 \
  --vcpus 4 \
  --disk path=/var/lib/libvirt/images/fedora.qcow2,size=30,format=qcow2 \
  --cdrom /home/allfab/virtualization/iso/Fedora-Workstation-Live-44-1.7.x86_64.iso \
  --network network=default,model=virtio \
  --graphics spice \
  --video virtio \
  --os-variant fedora43 \
  --boot uefi

Petite note au passage : Fedora 44 n’était pas encore référencée dans osinfo-db au moment des faits, même après un osinfo-db-import --latest. Utiliser fedora43 comme variante de référence fait très bien l’affaire — ça n’affecte que les valeurs par défaut suggérées par l’installeur, pas la compatibilité réelle du système.

J’ai aussi tenté d’automatiser l’installation via kickstart (inst.ks=) en extrayant manuellement le noyau et l’initrd de l’image Live (boot/x86_64/loader/linux et initrd). Ça démarre, mais ça finit dans un shell d’urgence dracut : les images Workstation Live ne sont pas pensées pour ce mode d’installation automatisée de la même façon qu’une image netinst (boot.iso), qui reste le bon choix pour du kickstart fiable. Faute de temps, installation manuelle cette fois-ci.

Le vrai problème : la VM n’a pas internet
#

Une fois Fedora installée dans la VM, le DHCP fonctionnait (IP 192.168.122.200 bien attribuée), mais aucun ping ne sortait — ni vers la passerelle, ni vers l’extérieur. Trois causes distinctes, empilées les unes sur les autres, ont dû être débusquées une par une.

La méthode qui a payé à chaque étape : un tcpdump côté hôte pour localiser précisément où le paquet disparaît, plutôt que de deviner à l’aveugle.

1. NetworkManager qui se mêle du bridge de libvirt
#

Premier symptôme : virbr0 restait en NO-CARRIER/DOWN, et l’interface vnet de la VM n’était jamais rattachée au bridge (bridge link show ne la listait pas). En creusant :

nmcli device status | grep -i virbr
# virbr0    bridge   connecté (en externe)

NetworkManager gérait virbr0 en parallèle de libvirt, ce qui provoquait des détachements intermittents de l’interface vnet. La solution : dire explicitement à NetworkManager d’ignorer les interfaces de libvirt.

sudo mkdir -p /etc/NetworkManager/conf.d
sudo tee /etc/NetworkManager/conf.d/unmanaged-libvirt.conf << 'EOF'
[keyfile]
unmanaged-devices=interface-name:virbr*;interface-name:vnet*
EOF
sudo systemctl restart NetworkManager

Après ça, virbr0 passe en non-géré, et l’attachement du vnet au bridge redevient automatique et stable à chaque démarrage de VM.

2. La zone firewalld libvirt sans route de sortie
#

Le bridge fonctionnait enfin (ping vers 192.168.122.1 OK), mais rien ne sortait vers internet. Un tcpdump sur l’hôte a permis de localiser précisément le blocage :

sudo tcpdump -i any -n icmp
# 192.168.122.200 > 1.1.1.1 : virbr0 In   ← le paquet s'arrête ici

Le paquet entrait dans virbr0 mais n’était jamais forwardé vers wlp1s0. En cause : la zone firewalld libvirt (à laquelle virbr0 appartient) n’avait aucune policy de routage vers l’extérieur — sur les versions récentes de firewalld, le forward inter-zones passe par des objets policy dédiés, pas par le simple flag forward d’une zone.

sudo firewall-cmd --permanent --new-policy libvirt-zone-routed-out
sudo firewall-cmd --permanent --policy libvirt-zone-routed-out --add-ingress-zone libvirt
sudo firewall-cmd --permanent --policy libvirt-zone-routed-out --add-egress-zone ANY
sudo firewall-cmd --permanent --policy libvirt-zone-routed-out --set-target ACCEPT
sudo firewall-cmd --permanent --policy libvirt-zone-routed-out --add-masquerade
sudo firewall-cmd --reload

Le point piège : le masquerade doit être posé sur la policy, pas sur la zone — sinon le paquet est bien forwardé mais ressort avec son IP privée non routable, et la réponse ne revient jamais.

3. Docker qui verrouille tout le forward par défaut
#

Même après ces deux corrections, toujours rien. Dernier tcpdump, toujours le même arrêt net après virbr0 In. La cause : Docker installe une policy FORWARD DROP sur toute la machine et redirige tout vers une chaîne DOCKER-USER, vide par défaut pour tout ce qui n’est pas Docker.

sudo iptables -L FORWARD -n -v
# Chain FORWARD (policy DROP)
#   → jump DOCKER-USER
#   → jump DOCKER-FORWARD

Il fallait donc autoriser explicitement virbr0 dans cette chaîne, de façon persistante via firewalld :

sudo firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -i virbr0 -j ACCEPT
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -o virbr0 -j ACCEPT
sudo firewall-cmd --reload

Et là, enfin :

64 octets de 1.1.1.1 : icmp_seq=1 ttl=55 temps=16.6 ms

Ce qu’il faut retenir
#

Trois couches indépendantes peuvent bloquer le NAT libvirt sur une Fedora récente, chacune suffisante à elle seule pour tout empêcher :

  1. NetworkManager qui interfère avec la gestion du bridge virbr0
  2. firewalld (zones/policies) qui n’autorise pas explicitement le routage sortant de la zone libvirt
  3. Docker, dès qu’il est installé, qui verrouille la chaîne FORWARD par défaut

Une fois les trois fixes appliqués et persistés (fichier NetworkManager, policy firewalld permanente, règle directe firewalld), le tout survit sans problème à un reboot complet de la machine — testé et confirmé.

Articles connexes