VyOS : Routeur zone-based et containers Podman

VyOS est le routeur central du homelab. Il assure le routage inter-VLAN, le firewalling zone-based et l'hébergement de services critiques via des containers Podman intégrés nativement au système.

Cet article détaille l'architecture réseau, la gestion des zones firewall, le fonctionnement des containers et les bonnes pratiques opérationnelles.


Architecture zone-based

Le firewall VyOS utilise un modèle zone-based avec politique default-deny. Chaque interface (physique, VLAN ou bridge container) est assignée à une zone. Le trafic entre zones n'est autorisé que par des chaînes nommées explicites.

21 zones firewall

Le routeur gère 21 zones distinctes :

  • wan : interface WAN (trafic Internet)
  • lan : VLAN administration
  • iot : VLAN objets connectés (IoT)
  • servers : VLAN serveurs de production
  • k8s : VLAN cluster Kubernetes
  • bind9 : container DNS autoritatif
  • webproxy : container Squid transparent proxy
  • haproxy : container reverse-proxy TLS
  • suricata : container IPS inline
  • clamav : container antivirus
  • cicap : container ICAP connector
  • kuma : container monitoring Uptime Kuma
  • Et d'autres zones spécialisées (VPN, management, DMZ...)

Chaque paire de zones possède une chaîne firewall dédiée avec un nommage cohérent : Zone-source-to-Zone-dest (ex: Iot-to-Webproxy).

Diagramme des zones

flowchart TB
    WAN((WAN)) --> VyOS["VyOS Firewall<br/>21 zones - default deny"]
    VyOS --> LAN["LAN"]
    VyOS --> K3s["K3s"]
    VyOS --> IoT["IoT"]
    VyOS --> Mgmt["Management"]
    VyOS --> Mail["Mail"]
    subgraph pods["Containers Podman"]
        BIND9["BIND9 DNS"]
        Squid["Squid Proxy"]
        HAP["HAProxy TLS"]
        Suri["Suricata IPS"]
        Clam["ClamAV"]
        Kuma["Uptime Kuma"]
    end
    VyOS --> BIND9
    VyOS --> Squid
    VyOS --> HAP
    VyOS --> Suri

Containers Podman

VyOS intègre Podman nativement dans sa configuration (set container name ...). Les containers sont gérés comme des services systemd (vyos-container-name``).

Services déployés

Container Image Réseau Rôle
bind9 bind9-hardened 172.20.2.0/24 DNS autoritatif + forwarder
squid squid-hardened 172.20.0.0/24 Proxy transparent (TPROXY)
clamav clamav-hardened 172.20.0.0/24 Antivirus daemon
c-icap c-icap-hardened 172.20.0.0/24 ICAP connector Squid↔ClamAV
haproxy haproxy:latest 172.20.1.0/24 Reverse-proxy TLS (frontend)
suricata suricata-hardened host network IPS inline NFQUEUE
uptime-kuma uptime-kuma:2 172.20.3.0/24 Monitoring HTTP/TCP/DNS

Les images *-hardened sont déployées par tag de version, posé dans la configuration persistante du routeur. Leur construction est décrite dans le pattern FROM scratch.

Réseau dédié par service

Chaque groupe de containers possède son propre bridge réseau Podman. Cette isolation garantit que :

  • Les containers ne communiquent entre eux que via les règles firewall
  • Chaque réseau est assigné à sa propre zone firewall
  • Les address-groups contrôlent finement les IP autorisées

Interception DNAT

DNS (rule 534)

Le trafic DNS sortant de certaines zones (IoT, containers) est intercepté par une règle DNAT qui redirige vers le container BIND9 :

set nat destination rule 534 description 'DNS interception'
set nat destination rule 534 inbound-interface group 'dnat-dns'
set nat destination rule 534 protocol 'tcp_udp'
set nat destination rule 534 destination port '53'
set nat destination rule 534 translation address '172.20.2.10'

Cette interception garantit que tous les clients utilisent le même cache DNS (important pour la cohérence CDN avec le proxy transparent).

Proxy transparent (PBR)

Le VLAN IoT utilise le Policy-Based Routing (table 100) pour rediriger le trafic HTTP/HTTPS vers Squid :

  • Les paquets marqués sont routés vers le container Squid
  • Un service systemd squid-redirect injecte des règles nft REDIRECT dans le namespace réseau du container (80→3129, 443→3131)
  • Squid fait peek+splice sur le port 3131 pour l'interception HTTPS

Suricata IPS inline

Le container Suricata opère en mode NFQUEUE sur le trafic WAN entrant/sortant. Il nécessite allow-host-networks car NFQUEUE exige le network namespace de l'hôte.

L'option --queue-bypass agit comme failsafe natif : si le container est arrêté, le trafic passe sans inspection plutôt que d'être bloqué.

Voir l'article dédié : Suricata IPS inline NFQUEUE sur VyOS


Gestion de la configuration

Ansible vyos-backup.yaml

La configuration VyOS est sauvegardée et versionnée automatiquement via un playbook Ansible dédié :

  • Export du config.boot complet
  • Commit git automatique si des changements sont détectés
  • Historique consultable des modifications

Commit et nftables

Le commit VyOS avec 130+ chaînes nftables prend 2 à 3 minutes. C'est normal et attendu — ne pas interrompre le processus.


Trois mécanismes qui ne font pas ce qu'on croit

podman stop ne met pas en pause, il supprime

Sur VyOS, les containers Podman sont déclarés dans le config tree et leur cycle de vie appartient à systemd. podman stop court-circuite ce mécanisme : la commande ne met pas le container en pause, elle le supprime. Rien ne le relance ensuite, et il faut repasser par un commit pour le recréer à partir de la configuration.

La commande opérationnelle native passe par le wrapper qui délègue proprement à systemd :

restart container NOM

sudo relève du même problème. Le config tree est un montage unionfs dont les permissions dépendent de l'utilisateur qui a ouvert la session de configuration. Une commande passée sous uid=0 laisse derrière elle des fichiers appartenant à root, et les sessions suivantes échouent sur des erreurs de permissions sans rapport apparent avec la manipulation d'origine. Les commandes de configuration sont prévues pour être exécutées telles quelles par un membre du groupe vyattacfg.

Toutes les sorties de container ont la même adresse source

Podman Netavark masquerade toutes les connexions sortantes des containers. L'IP source vue par les services externes est celle du gateway du bridge (ex: l'IP VyOS du réseau container), pas l'IP du container lui-même. Cela complique le tracing dans les logs — utiliser des identifiants applicatifs (hostnames syslog, headers HTTP) plutôt que les IP source.

En SSH non interactif, les validateurs ne s'exécutent pas

Le wrapper de configuration dépend de variables d'environnement définies dans /etc/default/vyatta. Un shell SSH non interactif ne les charge pas : les commandes set sont acceptées, mais les validateurs de type — adresses IP, entiers, plages de ports — ne s'exécutent pas. Une valeur invalide passe donc sans le moindre message et ne se manifeste qu'au commit, voire au démarrage du service concerné. Sourcer ce fichier avant toute configuration scriptée est la seule parade.


Ce que cette architecture donne

VyOS offre une plateforme unique combinant routage, firewalling zone-based et orchestration de containers — le tout géré via une configuration déclarative versionnable. L'intégration native de Podman évite la complexité d'un orchestrateur séparé tout en maintenant une isolation réseau stricte entre les services.


Liens

Articles connexes