
đ Aussi en: English · Deutsch · Español
Petite mise Ă jour du dĂ©pĂŽt : le dĂ©pĂŽt GitHub www_k3s vient de recevoir une sĂ©rieuse refonte de son pare-feu. L’ancienne configuration reposait sur le bon vieux iptables â ça marchait trĂšs bien, mais ça commençait Ă ressembler Ă une voiture de collection avec starter manuel et double dĂ©brayage. Il Ă©tait temps de passer Ă autre chose. Le pare-feu est dĂ©sormais entiĂšrement réécrit en nftables, le successeur moderne livrĂ© par dĂ©faut sous Linux depuis le noyau 3.13. Voici pourquoi c’est le bon choix, et une visite complĂšte de ce que fait concrĂštement le nouveau jeu de rĂšgles. đ§”
⥠nftables vs iptables â pourquoi c’est important
iptables est le filtre de paquets standard de Linux depuis la fin des années 90. Il fonctionne, il a fait ses preuves, et environ un milliard de réponses sur Stack Overflow y font référence. Mais il accuse son ùge :
- Pas de mises Ă jour atomiques des rĂšgles â les modifications sont appliquĂ©es ligne par ligne, ce qui laisse une courte fenĂȘtre pendant laquelle votre jeu de rĂšgles n’est que partiellement appliquĂ©
- Des outils sĂ©parĂ©s pour IPv4/IPv6 â iptables et ip6tables, deux stocks de rĂšgles distincts Ă garder synchronisĂ©s
- Pas de sets natifs â les listes de blocage d’IP nĂ©cessitent des outils tiers comme ipset
- Performances â la correspondance des rĂšgles est linĂ©aire ; plus il y a de rĂšgles, plus c’est lent
nftables (introduit avec Linux 3.13, par défaut sur la plupart des distributions depuis 2019 environ) rÚgle tout cela :
- â
Un seul jeu de rĂšgles pour IPv4 + IPv6 (
table inet) - â Remplacement atomique des rĂšgles â tout le jeu de rĂšgles est Ă©changĂ© en un seul appel systĂšme
- â Des sets natifs avec gestion des dĂ©lais d’expiration (listes de blocage d’IP intĂ©grĂ©es, pas besoin d’ipset)
- â De meilleures performances grĂące Ă une machine virtuelle compilĂ©e JIT dans le noyau
Sous AlmaLinux 9, nftables est la solution par dĂ©faut du systĂšme. La commande iptables n’y est littĂ©ralement qu’une couche de compatibilitĂ© au-dessus de nftables. Utiliser la vraie syntaxe iptables en 2025, c’est donc Ă©crire des rĂšgles iptables qui seront de toute façon traduites en rĂšgles nftables â en perdant au passage toute la jolie syntaxe. đ
đ Le jeu de rĂšgles â section par section
Le pare-feu est un template Jinja2 Ansible (roles/common_firewall/templates/fw.nft.j2) rendu pour chaque hÎte et appliqué via nftables. Parcourons-le.
đïž Les sets de blocage
set banned4 {
type ipv4_addr
flags timeout
timeout 1d
}
set banned6 {
type ipv6_addr
flags timeout
timeout 1d
}Deux sets â un pour IPv4, un pour IPv6 â qui servent de liste de blocage dynamique. flags timeout + timeout 1d signifie que chaque entrĂ©e expire automatiquement au bout de 24 heures. fail2ban Ă©crit dans ces sets dĂšs qu’il dĂ©tecte une attaque par force brute ou un scan de ports. Pas besoin de tĂąche cron pour nettoyer les vieux bannissements. đ§č
đ ChaĂźne INPUT â le plat de rĂ©sistance
La politique par dĂ©faut est DROP â tout ce qui n’est pas explicitement autorisĂ© est rejetĂ© en silence.
iif lo acceptLe trafic loopback (127.0.0.1, ::1) est toujours acceptĂ©. Toute la communication interne Ă l’hĂŽte â kubectl, kubelet, collecte Prometheus, sockets de base de donnĂ©es â passe par ici.
ct state established,related accept
ct state invalid dropLe suivi de connexions (connection tracking) fait le gros du travail. Les connexions dĂ©jĂ Ă©tablies sont acceptĂ©es sans examiner d’autres rĂšgles. Les paquets Ă l’Ă©tat invalide (par ex. des paquets TCP qui n’appartiennent Ă aucune connexion connue) sont rejetĂ©s immĂ©diatement.
tcp flags & (fin|syn|rst|psh|ack|urg) == fin|syn|rst|psh|ack|urg drop # XMAS
tcp flags & (fin|syn|rst|psh|ack|urg) == 0x0 drop # NULLLe grand classique : jeter les paquets malformĂ©s. Les paquets XMAS ont tous les drapeaux TCP activĂ©s en mĂȘme temps â impossible dans un trafic rĂ©el, utilisĂ© par les scanners de ports (nmap -sX). Les paquets NULL n’ont aucun drapeau â mĂȘme combat. Ce sont deux astuces de fingerprinting ou d’Ă©vasion. Poubelle. đđ«
ct state new tcp flags & (syn|ack) == syn|ack reject with tcp resetAnti-usurpation : un SYN,ACK qui arrive comme nouvelle connexion signifie que quelqu’un forge de toutes piĂšces une rĂ©ponse Ă une poignĂ©e de main TCP. RejetĂ© avec un TCP RST.
tcp flags & (syn|ack|fin|rst) == rst limit rate 1/second accept
tcp flags & (syn|ack|fin|rst) == rst dropProtection contre les inondations de RST. Les paquets RST sont légitimes (ils servent à fermer des connexions), mais un déluge de RST est une technique de DoS. On en autorise au maximum un par seconde, le reste part à la poubelle.
ip saddr @banned4 drop
ip6 saddr @banned6 dropVĂ©rification des sets de blocage dynamiques alimentĂ©s par fail2ban. Ils sont placĂ©s avant les rĂšgles d’exception K3s, pour qu’une IP bannie ne puisse jamais se faufiler par les exemptions du CIDR des pods plus bas.
{% if 'nextcloud' in group_names %}
ip saddr {{ k3s_pod_cidr }} tcp dport 3306 accept
ip saddr {{ k3s_pod_cidr }} tcp dport 6379 accept
{% endif %}Une condition Jinja2 â ce bloc n’apparaĂźt que sur les hĂŽtes Nextcloud. Sur les serveurs Nextcloud, MariaDB et Redis tournent comme services de l’hĂŽte (pas dans K3s), les pods doivent donc les joindre via l’IP de l’hĂŽte. Sur les hĂŽtes WordPress, MariaDB tourne dans le cluster sous forme de pod : ces rĂšgles sont inutiles et donc omises. đ§
ip saddr {{ server_ipv4 }} tcp dport { 3000, 10254 } acceptingress-nginx tourne avec hostNetwork: true, ce qui veut dire qu’il vit dans l’espace de noms rĂ©seau de l’hĂŽte. Quand il transmet une requĂȘte Ă Grafana (port 3000) via un ExternalService Kubernetes, l’IP source est l’IP publique de l’hĂŽte lui-mĂȘme â pas une IP de pod. Idem pour les sondes de santĂ© du kubelet sur le port 10254. On autorise donc l’IP propre de l’hĂŽte, mais uniquement pour ces deux ports prĂ©cis â pas d’accept gĂ©nĂ©ral. đŻ
ip saddr {
0.0.0.0/8, 10.0.0.x/8, 127.0.0.0/8, 169.254.0.0/16,
172.16.0.x/12, 192.168.0.x/16, 224.0.0.0/4, 240.0.0.0/5
} dropOn rejette toutes les plages sources RFC-1918 et rĂ©servĂ©es qui arrivent sur l’interface externe. Si un paquet venu de l’Internet public prĂ©tend provenir de 10.x.x.x ou de 192.168.x.x, il est usurpĂ©. Les exceptions du CIDR des pods K3s ci-dessus sont placĂ©es avant ce bloc justement pour que le trafic lĂ©gitime des pods ne s’y fasse pas piĂ©ger.
icmp type { destination-unreachable, echo-request, time-exceeded, parameter-problem } acceptOn n’autorise que les types ICMP rĂ©ellement nĂ©cessaires : ping, traceroute et les signaux de rĂ©seau injoignable. Tout le reste (redirect, router-advertisement, etc.) est rejetĂ©.
icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem,
router-advertisement, router-solicitation,
nd-neighbor-solicitation, nd-neighbor-advertisement,
echo-request, echo-reply } acceptICMPv6 nĂ©cessite une liste d’autorisation plus longue, car IPv6 en dĂ©pend fondamentalement. Le Neighbor Discovery Protocol (NDP) â l’Ă©quivalent IPv6 d’ARP â passe par ICMPv6. Sans ces types, IPv6 ne fonctionne tout simplement pas.
ip daddr {{ server_ipv4 }} tcp dport { 80, 443 } ct state new accept
ip daddr {{ server_ipv4 }} udp dport 443 ct state new accept
ip6 daddr {{ server_ipv6 }} tcp dport { 80, 443 } accept
ip6 daddr {{ server_ipv6 }} udp dport 443 acceptOuverture de HTTP, HTTPS et UDP/443 (QUIC/HTTP3) pour le serveur web. En IPv4 comme en IPv6. C’est la rĂšgle UDP qui permet HTTP/3 â les navigateurs le dĂ©couvrent via Alt-Svc, puis basculent dessus pour les connexions suivantes. đ
ip daddr {{ server_ipv4 }} tcp dport 10022 ct state new acceptSSH sur un port non standard. Le port 22, Ă ce stade, ce n’est plus que du bruit.
limit rate 2/minute log prefix "nftables DROP IN: " level warn flags allJournalisation à débit limité de tout le reste avant le rejet par la politique par défaut. Vous voyez ce qui est bloqué sans inonder le journal du noyau. Le préfixe permet de le retrouver facilement avec grep dans journald.
đ ChaĂźne FORWARD
K3s gĂšre ses propres chaĂźnes KUBE-* pour le routage des services â cette chaĂźne doit seulement laisser passer le trafic pod-Ă -pod et pod-Ă -service dans le CIDR des pods, et bloquer tout le reste. Les sets de blocage sont vĂ©rifiĂ©s ici aussi, pour que les IP bannies ne puissent pas exploiter le trafic transfĂ©rĂ©.
đ€ ChaĂźne OUTPUT
Le trafic sortant est lui aussi restreint par un DROP par dĂ©faut. AutorisĂ©s : DNS, NTP, HTTP/HTTPS pour le tĂ©lĂ©chargement des paquets et le renouvellement des certificats ACME, SMTP pour l’envoi des e-mails, le serveur d’API K3s sur 6443 et QUIC en sortie. Tout le reste â y compris les connexions sortantes alĂ©atoires qu’un malware pourrait tenter â est rejetĂ© et journalisĂ©.
đ En rĂ©sumĂ©
Le jeu de rĂšgles nftables abat beaucoup de travail en relativement peu de lignes. Un seul fichier, double pile IPv4/IPv6, listes de blocage dynamiques natives, suivi de connexions et exceptions adaptĂ©es Ă Kubernetes â le tout dans une unitĂ© atomique appliquĂ©e proprement Ă chaque exĂ©cution d’Ansible. C’est typiquement le genre de chose qui fait apprĂ©cier l’infrastructure as code. đ€
Le dépÎt se trouve sur github.com/aptupgrademe/www_k3s si vous voulez creuser.




