
Actions Avancées sur NFTables
Table of Contents
Pour l’un de mes derniers oraux à l’ESGI, j’ai créer un cours sur complet NFTables principalement ciblé pour les admins sys’ confirmés. J’aimerais refaire un point sur une poignée de ses actions intéressante
NFTables
En tant que remplaçant d’iptables, NFTables est devenu l’outil de filtrage de paquets par défaut sur la plupart des distributions Linux. Grâce à une syntaxe plus simple et une architecture flexible, il offre des possibilités avancées pour la gestion des règles de pare-feu. bla bla bla j’arrête mon prosélytisme sur NFTables
Pour rappel, voici un exemple de se que à quoi des règles NFTables ressemblent:
#!/usr/sbin/nft -f
flush ruleset
define eternet0 = eth0
define PC-Yanis = {192.168.1.20}
define Laptop-Yanis = {192.168.1.21}
table ip filtreIPv4 {
chain input {
type filter hook input priority 0; policy drop;
# Relations établies
ct state established,related accept
# Autorisation de la loopback
iifname lo accept
# Accès SSH Yanis
tcp dport 22 ip saddr {$PC-Yanis, $Laptop-Yanis} accept
}
chain output {
type filter hook output priority 0; policy drop;
# Relations établies
ct state established,related accept
# ICMP
ip protocol icmp accept
# DNS
tcp dport 53 accept
tcp dport 53 accept
# Accès Web
tcp dport {80, 443} accept
# NTP
udp dport 123 accept
}
}
// Règles totalement fictives :p btw
Les deux actions principales de NFTables sont :
« Accept » : permet de laisse passer le paquet et d’omettre les règles suivantes. Le paquet passe simplement dans la machine. Équivalant direct du « Exempt » chez FortiGate.
« Drop » : permet de supprimer le paquet et d’omettre les règles suivantes. Le paquet est détruit et n’est pas traité dans la machine.
Mais NFTables ne s’arrête pas à ça (loin de là d’ailleurs).
Actions Avancées
Reject : le drop avec réponse
Le « reject » fonctionne comme le « drop » sauf que cette fonction peut renvoyer des paquets icmp personnalisés en cas de refus du firewall.
chain input {
type filter hook input priority 0; policy drop;
# Relations établies
ct state established,related accept
# Autorisation de la loopback
iifname lo accept
# Accès SSH
tcp dport 22 accept
# Rejet Wake-On-LAN
udp dport 9 reject with icmp type net-unreachable
}
Ici, NFTables bloque la connexion sur le port 9 en UDP et renvoie un paquet ICMP avec l’info « net-unreachable ».
On peut choisir le message de rejet suivant la version de icmp :
| ICMPX REASON | ICMPv6 | ICMPv4 |
|---|---|---|
| admin-prohibited | admin-prohibited | admin-prohibited |
| port-unreachable | port-unreachable | port-unreachable |
| no-route | no-route | net-unreachable |
| host-unreachable | addr-unreachable | host-unreachable |
| Article complet sur le wiki NFTables |
Log : comment générer des logs avec NFTables
« log » est une action qui vient dire à NFTables d’envoyé un log vers le journal du noyau Linux, qui sont ensuite gérés par le démon de journalisation du système (comme syslogd ou systemd-journald) et écrits dans un fichier journal du système.

Ici NFTables va loguer les paquets SSH puis les accepter. Cependant les paquets ICMP sont juste loguer puis « drop » grâce à la “policy” de notre chaine.
Voici un aperçu de ce que NFTables renvoie (avec journalctl -f):
Par défaut, on récupère toutes les infos de la couche 2 à 5 OSI (MAC, IP, transport et session). Et c’est paramétrable !
.
De base tous les logs NFTables sont considéré comme une entrée ‘warning’, on peut modifier cela aussi ! Grâce au paramètre level.
| Sévérité | “norme” syslog pour NFTABLES |
|---|---|
| Emergencies | emerg |
| Alerts | alert |
| Critical | critic |
| Errors | err |
| Warnings | warn |
| Notifications | notice |
| Informational | info |
| Debugging | debug |
Counter : les compteurs paramètrables
« counter » permet de compter le nombre de paquets passés par une règle. Tout comme le « log », le « counter » n’est pas forcément une action finale. Il existe deux type de « counter » :
- Anonyme : qui est utile pour les tests (mais qui n’est pas appelable)
- Nommé : ce compteur à un nom et est appelable (et est plus lisible par l’humain)
Pour les compteur nommé, il faut tout d’abord créer les futurs compteurs au même niveau que les chaînes. Voici une implémentation simple:

Plusieurs commandes de manipulation:
nft list counter ip [TABLE] [COUNTER]
nft list counters
nft reset couter ip [TABLE] [COUNTER]

Limit : limitations sur une durée
Tout comme « log » et le « counter », « limit » est une action qui ne termine pas forcement la règle ! Il y a deux type de « limit » :
- La limitation par paquet sur un temps donné
- La limitation par bytes sur un temps donné
Exemple d’un paquet qui arrive sur une règle avec une limite :
Le paquet n°2 passe sur la règle mais ne correspond plus à la limite il passe donc à la suite. Dans ce cas la règle est final et la policy appliqué au paquet est donc « drop ».
« burst » est un paramètre de « limit » il définit le nombre maximal de paquets ou la quantité de données (en bytes/kilobytes…) pouvant dépasser temporairement la limite imposée par rate.
Le burst est utile pour éviter de bloquer immédiatement le trafic dès le franchissement de la limite de débit : il laisse passer de petites rafales, mais bloque si les excès persistent.
« limit » et « quota » utilise tous deux l’algorithme « token bucket »
.
Si on n’indique pas explicitement de valeur burst, la valeur par défaut est de 5 paquets pour un limiteur basé sur les paquets. « burst 1 » force donc la limite à 1 paquet/minute.
On peut aussi limiter la taille des paquets :

Ici je limite les paquets DNS supérieur à 60 bytes par minute au total.
plus de 60 secondes entre les deux paquets
moins de 60 secondes entres les deux paquets

Quota : en résumé limit + counter
Le « quota » est un mélange de « limit » et de « counter ». Il permet de faire une action sur une règle seulement si un nombre de data est dépasser ou inversement. Tout comme « counter »; Il existe deux type de « quota ».
- Anonyme : qui est utile pour les tests (mais qui n’est pas appelable ni RESETABLE)
- Nommé : ce quota à un nom et est appelable (et plus lisible par l’humain)
Exemple de quota nommé :
Le quota est le suivant : jusqu’à 100 bytes d’ICMP on accepte les paquets.
Ici j’ai déjà envoyé un ping de 60 octets.
Gestion des règles avec jump et goto
« jump » et « goto » sont là pour permettre d’aider à la gestion des tables qui commence sérieusement à être lourde en taille !
Ces deux actions permettent de changer de chaine NFTables depuis une chaîne “hooked” vers des chaines regulières (en gros des chaines sans “hook”). Voici directement un exemple concret :
Avec « jump » :
Avec « goto » :

Voici la différence :
Si vous utilisez « jump » pour faire traiter un paquet dans une autre chaîne, le paquet reviendra à la chaîne de la règle appelante une fois le traitement terminé.
En revanche, si vous utilisez « goto », les paquets seront traités dans une autre chaîne mais ne reviendront pas à la chaîne de la règle appelante. Dans ce cas, la politique par défaut appliquée au paquet sera celle de la chaîne de base d’origine qui a commencé à traiter le paquet.
Bonus : la coloration syntaxique sur NFTables
Pendant la rédaction de cette article je me suis pris à chercher comment colorer mes blocks de code en markdown -> plus simple à dire qu’à faire !
Le seul software que je connais qui comprend la fameuse “syntax high-lighting” compatible avec NFTables est GNU Nano :’). Merci Arturo Borrero González à jamais dans mon cœur 🧡.
## Syntax highlighting for the packet-filtering rules of Netfilter.
## Original author: Arturo Borrero González <arturo@debian.org>
## License: GPL version 3 or newer
syntax nftables "\.(nft|nftables)$"
header "^#!.*(nft|nftables)"
comment "#"
# Objects and operations
color green "\<(chain|hook|policy|priority|ruleset|set|table|type|v?map)\>"
color green "\<(define|include)\>"
color red "\<(add|delete|flush|insert|remove|replace)\>"
# Families
color yellow "\<(arp|bridge|inet|ingress|ip6?|netdev)\>"
# Terminal statements
color red "\<(drop|reject)\>"
color brightblue "\<(accept|continue|(d|s)nat|goto|jump|masquerade|return)\>"
# Comments
color cyan "(^|[[:blank:]])#.*"
# Trailing whitespace
color ,green "[[:space:]]+$"
# Strings
color yellow ""([^"\]|\\.)*"|'([^'\]|\\.)*'"
# Syntactic symbols
color green "[][{}():;|`$<>!=&\]"
# Basic variable names
color brightred "(\$|@)[[:alpha:]_-][[:alnum:]_.-]*"
Encore une raison de se servir de nano.
Avis différent détecté 🔫