Autoriser un réseau libvirt à en joindre un autre n’est plus une affaire de firewall-cmd --direct. C’est le pare-feu NAT de libvirt qui refuse le trafic NEW entre bridges. La forme des règles change selon la version de RHEL — et les outils aussi.
Le problème
Deux réseaux NAT libvirt isolés, par exemple :
br-network-1→172.17.1.0/24br-network-2→172.17.2.0/24
Chaque VM joint l’hôte et l’extérieur (via NAT). Un ping d’un réseau vers l’autre échoue avec Destination Port Unreachable depuis l’IP du bridge hôte.
firewalld peut déjà afficher forward: yes sur la zone libvirt. Ce n’est pas suffisant : libvirt rejette le paquet avant.
Deux backends, deux chaînes d’outils
| Hôte | Pare-feu libvirt | Où vivent les règles inter-bridges |
|---|---|---|
| RHEL 10+ | nftables natif | ip libvirt_network → guest_cross / guest_nat |
| RHEL 9 | backend iptables (iptables-nft) | ip filter → LIBVIRT_FWX, ip nat → LIBVIRT_PRT |
Repérage sur l’hôte :
| |
Sous RHEL 9, il n’y a en général pas de table ip libvirt / ip libvirt_network. Les chaînes apparaissent dans les tables filter / nat gérées par iptables-nft.
Scripts empaquetés (même fichier de conf) : github.com/tinsjourney/libvirt-bridge-forward
| Script | Install | Cible |
|---|---|---|
libvirt-bridge-forward | ./install.sh | RHEL 10+ |
libvirt-bridge-forward-iptables | ./install-iptables.sh | RHEL 9 |
Fichier de politique commun — bridge-forward.conf
| |
oneway— uniquementfrom → toboth— les deux sens
On édite, puis on restart l’unité systemd correspondante. Les subnets pour le bypass NAT viennent de la route IPv4 kernel de chaque bridge sur l’hôte.
RHEL 10+ — nftables natif
Libvirt crée :
| |
Un paquet inter-bridges rate guest_cross, tombe dans guest_input et meurt. firewalld s’exécute plus tard (filter + 10) : le forward de zone ne peut pas aider.
Approche
On laisse les bridges dans la zone libvirt. On insert un ACCEPT en tête de guest_cross (nécessaire au forward). En option, un return dans guest_nat pour ne pas masquerader le guest↔guest (sinon le pair voit l’IP hôte sur le bridge de sortie ; le conntrack fait quand même marcher le ping).
| Fichier | Rôle |
|---|---|
/etc/libvirt/bridge-forward.conf | Politique |
/usr/local/sbin/libvirt-bridge-forward | apply / flush / status |
/etc/systemd/system/libvirt-bridge-forward.service | Boot / restart |
/etc/libvirt/hooks/network | Réapplique après reconstruction des règles nft |
| |
Sur apply, le script attend guest_cross / guest_nat, purge les règles lbf-*, puis insert les paires forward + bypass NAT du conf.
Chemin d’un paquet
- Le guest route via l’IP hôte du bridge (
172.17.1.1). - L’hôte forward (
net.ipv4.ip_forward=1). guest_cross:iif br-network-1 oif br-network-2→ accept.guest_nat:saddr 172.17.1.0/24 daddr 172.17.2.0/24→ return (optionnel ; évite le SNAT).- Le forward firewalld de la zone
libvirtaccepte le trafic intra-zone.
RHEL 9 — iptables-nft (LIBVIRT_*)
Le backend iptables de libvirt crée des chaînes visibles via iptables-nft, par exemple :
| |
LIBVIRT_FWX joue le rôle de guest_cross. Préférer iptables pour inserer les règles (ces tables sont gérées par iptables-nft ; un nft brut sur ip nat affiche do not touch).
| Fichier | Rôle |
|---|---|
/etc/libvirt/bridge-forward.conf | Même format de politique |
/usr/local/sbin/libvirt-bridge-forward-iptables | apply / flush / status |
/etc/systemd/system/libvirt-bridge-forward-iptables.service | Boot / restart |
/etc/libvirt/hooks/network (depuis hooks/network-iptables) | Réapplique après démarrage réseau |
| |
Sur apply, les règles sont inserées en tête de LIBVIRT_FWX et LIBVIRT_PRT avec le préfixe de commentaire lbf-.
Hook réseau libvirt + SELinux
Au démarrage d’un réseau virtuel, libvirt reconstruit son pare-feu et les inserts manuels disparaissent. Le hook rappelle apply.
| |
Sans virt_hooks_unconfined, virtnetworkd échoue le hook avec exit 126 / Permission denied, et les réseaux en autostart ne remontent pas après reboot.
Activer aussi le forward de zone firewalld :
| |
Exemples concrets — forward dans un sens à la main
RHEL 10+ (nftables natif) :
| |
IP source réelle en option :
| |
RHEL 9 (iptables-nft) :
| |
IP source réelle en option :
| |
Ces one-liners disparaissent au redémarrage du réseau libvirt, sauf si on utilise les scripts et le hook du dépôt.