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-redirectinjecte 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.bootcomplet - 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.