Administration Serveur angularforall.com

- Pare-feu Linux : nftables, iptables et NAT

Nftables Iptables Pare-Feu Firewall Linux Securite Nat Networking Sysadmin Kernel Ufw Docker Production Debian
Pare-feu Linux : nftables, iptables et NAT

Migrez vers nftables : tables et chains, ruleset complet, NAT et port forwarding, sets dynamiques et rate limiting, integration fail2ban et logging journalctl.

Pourquoi migrer iptables vers nftables

Pendant pres de 20 ans, iptables a ete l'outil de configuration du pare-feu Linux. Mais sa conception date de 1998 et accuse plusieurs limitations : syntaxe verbeuse, outils separes pour IPv4 (iptables), IPv6 (ip6tables), ARP (arptables) et bridge (ebtables), absence d'atomicite (une regle echouee pouvait laisser le ruleset dans un etat incoherent), et performances limitees sur les rulesets complexes.

nftables, integre au kernel Linux depuis 3.13 (2014), corrige ces limitations en repensant l'architecture du pare-feu :

  • Outil unifie : une seule commande nft remplace iptables, ip6tables, arptables, ebtables.
  • Atomicite : tout le ruleset est applique ou aucun (transactions kernel).
  • Syntaxe coherente : structure inspiree de la programmation, plus lisible.
  • Performance : les sets et maps utilisent des structures de donnees optimisees (hash, rbtree).
  • Familles d'adresses : ip, ip6, inet (les deux), arp, bridge, netdev.
  • Compatibilite : la commande iptables continue de fonctionner via iptables-nft.

Etat des distributions en 2026

Distribution Backend par defaut Statut iptables
Debian 11+ / 12nftablesiptables-nft (compat)
Ubuntu 22.04 / 24.04nftablesiptables-nft (compat)
RHEL 8+ / Rocky / Almanftablesiptables-nft (compat)
Arch LinuxnftablesDisponible (legacy)
Alpine LinuxnftablesDisponible (legacy)
A retenir : meme si vos regles "iptables" fonctionnent encore, elles passent en realite par nftables sur les distributions recentes. Ecrire en syntaxe nftables native est plus propre, plus rapide, et evite les couches de compatibilite.

Migrer une configuration iptables existante

# Sauvegarde des regles iptables actuelles
sudo iptables-save > /tmp/iptables-backup.v4
sudo ip6tables-save > /tmp/iptables-backup.v6

# Conversion automatique vers la syntaxe nftables
sudo iptables-restore-translate -f /tmp/iptables-backup.v4 > /tmp/converted.nft
sudo ip6tables-restore-translate -f /tmp/iptables-backup.v6 >> /tmp/converted.nft

# Inspection du resultat (verification manuelle obligatoire)
cat /tmp/converted.nft

# La conversion ne couvre pas tous les cas complexes
# Verifiez les modules iptables-extensions utilises avec
sudo iptables -L -n -v

Concepts nftables : tables, chains, sets

Avant d'ecrire des regles, il faut comprendre les briques de base de nftables. La hierarchie est claire et explicite : les tables contiennent les chains, qui contiennent les regles. Les sets et maps sont des structures de donnees referencables depuis les regles.

Familles de tables

# Une table appartient a une famille d'adresses
# inet  : couvre IPv4 + IPv6 (recommande pour la majorite des cas)
# ip    : IPv4 uniquement
# ip6   : IPv6 uniquement
# arp   : pour les regles ARP (rare)
# bridge: pour les ponts reseau
# netdev: pour les hooks de tres bas niveau (XDP, ingress)

# Exemples
nft add table inet filter
nft add table ip nat
nft add table ip6 mangle

Types de chains et hooks

Les chains sont les points d'entree du traitement des paquets. nftables connait deux types de chains : base chains (attachees a un hook kernel) et regular chains (utilisees pour modulariser les regles, appelees depuis d'autres chains via jump).

Hook Position Usage typique
preroutingReception, avant routageNAT destination, mark
inputApres routage, vers la machineFiltrage trafic entrant
forwardApres routage, vers une autre machineFiltrage routeur, NAT
outputGenere localement, avant envoiFiltrage trafic sortant
postroutingApres routage, avant transmissionNAT source, MASQUERADE

Politique par defaut : drop ou accept

# Une chain a une politique par defaut appliquee si aucune regle ne matche
# policy drop   : refuse par defaut (recommande sur input et forward)
# policy accept : accepte par defaut (par defaut sur output)

# Exemple : chain input avec drop par defaut
nft add chain inet filter input { type filter hook input priority filter \; policy drop \; }

# Plus pratique : ecrire toute la config dans un fichier puis charger

Sets : groupes d'elements

Les sets sont des collections nommees d'IPs, de ports ou d'autres elements, utilisables dans les regles. Plus efficace qu'enumerer chaque element dans plusieurs regles.

# Cree un set IPv4 nomme "trusted_ips"
nft add set inet filter trusted_ips { type ipv4_addr \; }

# Ajoute des elements au set
nft add element inet filter trusted_ips { 192.168.1.10, 192.168.1.20, 10.0.0.5 }

# Set avec timeout (les elements expirent apres une duree)
nft add set inet filter blacklist { type ipv4_addr \; flags timeout \; }

# Ajoute une IP avec un timeout de 1 heure
nft add element inet filter blacklist { 1.2.3.4 timeout 1h }

# Set de ports TCP autorises
nft add set inet filter web_ports { type inet_service \; }
nft add element inet filter web_ports { 80, 443, 8080, 8443 }

Premier ruleset complet

Plutot qu'ajouter des regles une par une avec nft add, l'approche recommandee consiste a ecrire l'ensemble du ruleset dans un fichier /etc/nftables.conf, puis a le charger atomiquement avec nft -f.

Configuration de base pour un serveur web

# /etc/nftables.conf — Configuration nftables serveur web
# ============================================================
#!/usr/sbin/nft -f

# Vide le ruleset existant avant de charger le nouveau
flush ruleset

# ============================================================
# Table inet "filter" (couvre IPv4 et IPv6)
# ============================================================
table inet filter {

    # Set des IPs whiteliste (admins, monitoring)
    set admins_ips {
        type ipv4_addr
        elements = { 78.123.45.67, 203.0.113.10 }
    }

    # Set dynamique pour le rate limiting (IPs bannies temporairement)
    set ratelimit_blacklist {
        type ipv4_addr
        flags dynamic, timeout
        timeout 10m
    }

    # ----- Chain INPUT (paquets destines a cette machine) -----
    chain input {
        # Hook input avec politique drop par defaut
        type filter hook input priority filter; policy drop;

        # Accepte tout le trafic loopback
        iif lo accept

        # Bloque l'acces a 127.0.0.0/8 sur les autres interfaces (anti-spoofing)
        iif != lo ip daddr 127.0.0.0/8 drop

        # Accepte les paquets de connexions deja etablies
        ct state established,related accept

        # Drop les paquets dans un etat invalide (corruption, attaque)
        ct state invalid drop

        # Accepte les paquets ICMP de diagnostic (limite a 10/s contre flood)
        ip protocol icmp icmp type { echo-request, destination-unreachable, time-exceeded } limit rate 10/second accept
        ip6 nexthdr icmpv6 icmpv6 type { echo-request, destination-unreachable, time-exceeded, packet-too-big, parameter-problem } limit rate 10/second accept

        # SSH : accepte depuis les IPs admins, sinon limite avec rate limiting
        tcp dport 22 ip saddr @admins_ips accept
        tcp dport 22 ct state new limit rate 5/minute accept

        # HTTP / HTTPS : accepte depuis n'importe ou
        tcp dport { 80, 443 } accept

        # Log et drop tout le reste (limite pour eviter le spam)
        limit rate 5/minute log prefix "nftables-input-drop: "
    }

    # ----- Chain FORWARD (paquets en transit) -----
    chain forward {
        # Politique drop : pas de routage par defaut
        type filter hook forward priority filter; policy drop;

        # Active si vous avez besoin de router (Docker, VPN, etc.)
        # ct state established,related accept
    }

    # ----- Chain OUTPUT (paquets generes par cette machine) -----
    chain output {
        # Politique accept par defaut (machine de confiance)
        type filter hook output priority filter; policy accept;

        # Optionnel : restreindre les sorties a certains ports
        # tcp dport { 80, 443, 53, 22 } accept
        # udp dport { 53, 123 } accept
        # drop
    }
}

Charger et verifier le ruleset

# Validation de la syntaxe sans appliquer
sudo nft -c -f /etc/nftables.conf
# Aucune sortie = syntaxe correcte

# Application atomique (rollback automatique en cas d'erreur)
sudo nft -f /etc/nftables.conf

# Affiche le ruleset complet actif
sudo nft list ruleset

# Affiche une table specifique
sudo nft list table inet filter

# Affiche une chain specifique avec compteurs
sudo nft list chain inet filter input
Avant d'activer le drop par defaut : ouvrez DEUX sessions SSH. Si vous vous bloquez vous-meme, la seconde session vous permet de corriger ou de vider le ruleset avec sudo nft flush ruleset.

Compteurs et statistiques

# Ajoute des compteurs aux regles pour le debug
nft add rule inet filter input tcp dport 22 counter accept

# Affiche les compteurs (paquets et octets)
sudo nft -a list ruleset

# Reinitialise les compteurs
sudo nft reset counters
sudo nft reset counters table inet filter

NAT et port forwarding

Le NAT (Network Address Translation) sert a rediriger ou masquer les adresses IP des paquets. Cas typiques : un VPS qui partage son IP publique avec ses containers Docker (MASQUERADE), un serveur qui expose un port interne sur un port externe different (DNAT), ou un equilibrage de charge basique. nftables traite le NAT dans des chains specifiques de type nat.

Configuration NAT pour exposer un service interne

# Ajout d'une table NAT au fichier /etc/nftables.conf

table ip nat {

    # Chain prerouting (avant routage) pour le DNAT
    chain prerouting {
        type nat hook prerouting priority dstnat;

        # Expose le port 8080 d'un serveur interne (192.168.1.50:80)
        # depuis l'IP publique du serveur sur le port 80
        iifname "eth0" tcp dport 80 dnat to 192.168.1.50:80

        # Idem pour HTTPS
        iifname "eth0" tcp dport 443 dnat to 192.168.1.50:443

        # Expose un autre service (base de donnees) avec changement de port
        # Le port externe 5432 est mappe vers le port interne 5432 du backend
        iifname "eth0" tcp dport 5432 dnat to 10.0.0.20:5432
    }

    # Chain postrouting (apres routage) pour le SNAT / MASQUERADE
    chain postrouting {
        type nat hook postrouting priority srcnat;

        # MASQUERADE : remplace l'IP source des paquets sortants
        # par l'IP de l'interface de sortie (utile pour partage de connexion)
        oifname "eth0" masquerade

        # Variante : SNAT explicite (necessite IP publique connue, plus performant)
        # oifname "eth0" snat to 1.2.3.4
    }
}

NAT et IP forwarding

Pour que les paquets traversent la machine, il faut activer l'IP forwarding au niveau du kernel.

# Active l'IP forwarding immediatement
sudo sysctl -w net.ipv4.ip_forward=1
sudo sysctl -w net.ipv6.conf.all.forwarding=1

# Active de maniere permanente
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.d/99-nftables.conf
echo "net.ipv6.conf.all.forwarding=1" | sudo tee -a /etc/sysctl.d/99-nftables.conf
sudo sysctl --system

NAT et chain forward (autoriser le trafic redirige)

Le NAT seul ne suffit pas : il faut aussi autoriser explicitement le trafic dans la chain forward, sinon le routeur drop les paquets.

# Dans la table inet filter, chain forward
chain forward {
    type filter hook forward priority filter; policy drop;

    # Autorise le trafic etabli (reponses aux paquets sortants)
    ct state established,related accept

    # Autorise le forwarding de eth0 vers le LAN interne
    iifname "eth0" oifname "eth1" accept

    # Autorise le forwarding LAN vers Internet
    iifname "eth1" oifname "eth0" accept
}
Astuce : Docker manipule directement les regles iptables (via la couche de compatibilite iptables-nft). Si vous melangez vos regles nftables avec Docker, ajoutez vos regles dans la chain DOCKER-USER plutot que dans une chain personnalisee. Cela evite les conflits.

Sets dynamiques et rate limiting

Les sets dynamiques sont une fonctionnalite phare de nftables : ils permettent de creer des collections d'IPs qui se peuplent automatiquement en fonction du trafic, avec timeouts auto-gerees. Combine au rate limiting, on obtient un anti-flood et un mini-fail2ban natif kernel.

Rate limiting basique

# Rate limit sur le port SSH : maximum 5 nouvelles connexions par minute
chain input {
    # ...

    # Bloque au-dela de 5/minute, accepte sinon
    tcp dport 22 ct state new limit rate 5/minute accept
    tcp dport 22 ct state new drop

    # Variante avec burst : autorise un pic court avant de limiter
    tcp dport 22 ct state new limit rate 5/minute burst 3 packets accept
}

Auto-blacklisting avec set dynamique

# Set dynamique pour bannir les IPs trop bavardes
table inet filter {

    # Set dynamique : flags dynamic + timeout
    set ssh_blacklist {
        type ipv4_addr
        flags dynamic, timeout
        timeout 1h
        size 65536
    }

    chain input {
        type filter hook input priority filter; policy drop;
        ct state established,related accept

        # Drop immediatement les IPs deja blacklistees
        ip saddr @ssh_blacklist drop

        # Si plus de 5 nouvelles connexions SSH en 1 minute,
        # ajoute l'IP au set ssh_blacklist (timeout 1h herite du set)
        tcp dport 22 ct state new \
            add @ssh_blacklist { ip saddr limit rate over 5/minute } \
            drop

        # Sinon, accepte SSH
        tcp dport 22 accept

        # Reste de la config...
    }
}

Whitelisting avec set

# Maintient une whiteliste mise a jour facilement
table inet filter {

    set whitelist_ssh {
        type ipv4_addr
        elements = {
            78.123.45.67,
            203.0.113.0/24,
            10.0.0.0/8
        }
    }

    chain input {
        # ...

        # Accepte SSH uniquement depuis les IPs whitelistees
        tcp dport 22 ip saddr @whitelist_ssh accept
        tcp dport 22 drop
    }
}

# Mise a jour live de la whitelist (sans recharger le ruleset)
nft add element inet filter whitelist_ssh { 1.2.3.4 }
nft delete element inet filter whitelist_ssh { 1.2.3.4 }
nft list set inet filter whitelist_ssh

Geo-blocking simplifie

Pour bloquer le trafic depuis certains pays, utilisez un set IP charge depuis une liste publique (ex: ipdeny.com).

# Telecharge la liste des plages IP d'un pays (Russie en exemple)
wget -O /tmp/ru.zone https://www.ipdeny.com/ipblocks/data/aggregated/ru-aggregated.zone

# Cree le set et charge les elements
nft add set inet filter geoblock_ru { type ipv4_addr \; flags interval \; }

# Charge toutes les plages IP via xargs
cat /tmp/ru.zone | while read net; do
    nft add element inet filter geoblock_ru { $net }
done

# Ajoute la regle qui drop ce set
nft add rule inet filter input ip saddr @geoblock_ru drop
Performance : les sets nftables utilisent des structures hash table ou rbtree (selon le type) pour des recherches en O(1) ou O(log n). Meme avec 100 000+ elements dans un set, les regles restent rapides.

Persistance et demarrage automatique

Par defaut, le ruleset nftables est volatil : il disparait au reboot. Le service systemd nftables.service charge automatiquement /etc/nftables.conf au demarrage.

Activer le service nftables

# Active le service nftables au boot
sudo systemctl enable nftables

# Demarre le service (charge /etc/nftables.conf)
sudo systemctl start nftables

# Verifie l'etat
sudo systemctl status nftables

# Sortie attendue
# nftables.service - nftables
#      Loaded: loaded (/lib/systemd/system/nftables.service; enabled)
#      Active: active (exited)
#     Process: 1234 ExecStart=/usr/sbin/nft -f /etc/nftables.conf

# Recharge apres modification du fichier
sudo systemctl reload nftables

Sauvegarder le ruleset actuel

# Exporte le ruleset actuel vers un fichier (utile apres modifications interactives)
sudo nft list ruleset > /tmp/current-ruleset.nft

# Inspecte avant remplacement
diff /etc/nftables.conf /tmp/current-ruleset.nft

# Remplace la config persistante (ATTENTION : pas de shebang dans la sortie)
sudo bash -c 'echo "#!/usr/sbin/nft -f" > /etc/nftables.conf
echo "flush ruleset" >> /etc/nftables.conf
nft list ruleset >> /etc/nftables.conf'

# Verifie la syntaxe
sudo nft -c -f /etc/nftables.conf

Reload atomique sans interruption

# nft -f remplace atomiquement le ruleset complet
# Aucune fenetre ou le ruleset est vide
sudo nft -f /etc/nftables.conf

# Verifie l'absence d'erreurs
echo $?
# Sortie attendue : 0

# Pour forcer un reload meme si le fichier n'a pas change
sudo systemctl reload nftables
A retenir : nftables charge le fichier dans une transaction kernel. Si une regle est invalide, AUCUNE regle n'est appliquee et le ruleset precedent reste actif. C'est le grand avantage par rapport a iptables qui pouvait laisser un ruleset partiellement applique en cas d'erreur.

Configuration multi-fichiers

Pour les rulesets complexes, decoupez en plusieurs fichiers et incluez-les depuis le fichier principal.

# /etc/nftables.conf
#!/usr/sbin/nft -f

flush ruleset

# Inclut les definitions de sets
include "/etc/nftables.d/sets.nft"

# Inclut les chains de filtrage
include "/etc/nftables.d/filter.nft"

# Inclut le NAT
include "/etc/nftables.d/nat.nft"

# Inclut les regles fail2ban (auto-genere par fail2ban)
include "/etc/nftables.d/fail2ban.nft"

Logging et integration fail2ban

Le logging permet de voir ce qui se passe dans votre pare-feu : quels paquets sont droppes, quelles attaques sont detectees, quels patterns trafic emergent. nftables peut logger directement vers journalctl via le keyword log.

Logger les paquets droppes

# Ajoute du logging avant le drop final
chain input {
    # ... regles d'acceptation ...

    # Log les paquets refuses (limite a 5/min pour eviter le spam)
    limit rate 5/minute log prefix "nftables-input-drop: " level info

    # Drop final (politique par defaut, pas besoin de regle explicite)
}

Lire les logs

# Logs nftables via journalctl (le prefix permet le filtrage)
sudo journalctl -k | grep "nftables-input-drop"

# Suit les logs en temps reel
sudo journalctl -k -f | grep "nftables"

# Filtre les logs avec dmesg
sudo dmesg | grep "nftables-input-drop"

# Pour separer les logs nftables dans un fichier dedie
# /etc/rsyslog.d/10-nftables.conf
:msg, contains, "nftables-" -/var/log/nftables.log
& stop

sudo systemctl restart rsyslog

Integration avec fail2ban

fail2ban peut utiliser nftables comme backend de bannissement. C'est plus efficace que iptables sur un systeme nftables-natif.

# Edite /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local
# /etc/fail2ban/jail.local

[DEFAULT]
# Backend nftables natif (multi-port)
banaction = nftables-multiport

# Type de blocage : drop (silencieux) ou reject (renvoie un ICMP)
chain = input

[sshd]
enabled = true
maxretry = 3
bantime = 3600
findtime = 600

# Override le banaction si besoin (par exemple allport)
# banaction = nftables-allports
# Recharge fail2ban
sudo systemctl restart fail2ban

# Verifie que la table fail2ban est creee
sudo nft list table inet f2b-table

# Sortie attendue (apres premier ban)
# table inet f2b-table {
#     set addr-set-sshd {
#         type ipv4_addr
#         elements = { 1.2.3.4 }
#     }
#     chain f2b-chain {
#         type filter hook input priority filter - 1; policy accept;
#         ip saddr @addr-set-sshd reject
#     }
# }

# Statut des jails fail2ban
sudo fail2ban-client status sshd

# Liste les IPs actuellement bannies
sudo nft list set inet f2b-table addr-set-sshd

Integration avec ipset (legacy)

Si une partie de votre infrastructure utilise encore iptables et ipset, sachez que nftables remplace les deux. Vous pouvez migrer progressivement : nftables et iptables-nft cohabitent sans probleme.

A retenir : sur un systeme nftables-natif, surveillez les regles ajoutees par d'autres outils (Docker, fail2ban, Kubernetes, libvirt). Ces outils manipulent leurs propres tables : ne les supprimez pas accidentellement avec un flush ruleset intempestif.

Conclusion

nftables modernise radicalement l'experience du pare-feu Linux : syntaxe unifiee, application atomique, performances superieures, structures de donnees riches. Pour un nouveau serveur, ecrire en syntaxe nftables native est le bon choix par defaut. Pour un serveur historique en iptables, la conversion automatique avec iptables-restore-translate demarre la migration en quelques minutes — la verification manuelle des regles converties prend ensuite plus de temps que la conversion elle-meme.

Les sets dynamiques avec timeout, le rate limiting natif et l'integration directe avec fail2ban offrent des protections puissantes sans outil tiers. Le logging vers journalctl simplifie le debug et l'analyse forensique. Au final, nftables fait beaucoup plus avec beaucoup moins de regles que iptables.

A retenir : testez toujours vos rulesets avec deux sessions SSH ouvertes. Validez avec nft -c -f avant d'appliquer. Utilisez flush ruleset en dernier recours seulement : il vide TOUTES les tables, y compris celles de Docker, fail2ban et autres outils qui en dependent.

Partager