15/11/2024

Les règles à action multiple dans NFtables

nftables introduit un nouveau concept intéressant au niveau des règles. Nous avons en effet vu qu'une règle était composée :

  • d'un filtre qui permet de cibler des paquets selon certaines caractéristiques ;
  • d'une action qui permet d'effectuer de bloquer ou laisser passer le paquet.

Sous IPtables, chaque règle ne pouvait avoir qu'une action. Cela signifie que si l'on souhaitait écrire des journaux et rejeter les paquets qui avaient comme IP source 192.168.50.4, on devait utiliser deux lignes distinctes, ce qui alourdissait bien évidemment la gestion des règles :

root@debian:~# iptables –A INPUT –s 192.168.50.4 –J NFLOG –nflog-prefix "DENY 192.168.50.4 – "
root@debian:~# iptables –A INPUT –s 192.168.50.4 –J DROP

nftables permet d'effectuer plusieurs actions en une seule ligne, par exemple, produire des logs, et bloquer les paquets.

I. Produire des logs avec nftables

nftables intègre le fait de pouvoir journaliser l'application d'une règle et l'action qu'elle effectue, ce qui permet de journaliser plus facilement des évènements traités par nftables.

Note : La fonctionnalité de logging du trafic dans nftables est pleinement utilisable à partir de la version 3.17 du kernel Linux. Si vous êtes dans une version inférieure, vous aurez à charger un module du Noyau Linux avec la ligne de commande suivante : modprobe ipt_LOG

Si l'on suit un exemple du même type que celui vu plus haut avec IPtables, cela donnera cette commande (blocage des adresse visant l'adresse 8.8.8.8) :

root@debian:~# nft add rule mon_filtreIPv4 input ip saddr 192.168.10.1 log drop

Si j'utilise ensuite une machine avec cette IP et que j'essaie de communiquer avec mon serveur sur lequel se trouve nftables, nous verrons des évènements dans le fichier /var/log/kern.log :

root@debian:~# nft list chain ip mon_filtre output
table ip mon_filtre {
    chain output {
        type filter hook output priority filter; policy accept;
        ip daddr 8.8.8.8 log drop
    }
}
root@debian:~# nslookup it-connect.fr 8.8.8.8
^C
root@debian:~# tail -n 1 /var/log/kern.log 
Jan 9 15:30:30 debian kernel: [32255.907870] IN= OUT=ens33 SRC=192.168.34.7 DST=8.8.8.8 LEN=59 TOS=0x00 PREC=0x00 TTL=64 ID=7996 PROTO=UDP SPT=39219 DPT=53 LEN=39

Ici j'ai tenté de faire un requête DNS ciblant le serveur DNS ayant l'adresse 8.8.8.8. Non seulement ma requête DNS n'a pas aboutie (a été bloquée) mais le paquet bloqué à également été journalisé. Un autre exemple, avec nftables, on peut facilement journaliser tout le trafic entrant. Il faut cependant ne pas oublier que cela peut être assez lourd à gérer pour le système, tant en terme de vitesse de traitements que d'espace de stockage.

root@debian:~# nft add rule filter input log

Une autre notion intéressante est celle des préfixes. Celle-ci permet, lorsque l'on produit des logs à partir d'une règle, d'y ajouter un préfixe, une sorte de commentaire :

root@debian:~# nft add rule mon_filtre output ip daddr 8.8.8.8 log prefix "DNS_8.8.8.8" drop

On pourra ensuite retourner voir, après avoir généré des logs, dans notre fichier /var/log/kernel.log afin voir notre préfixe :

root@debian:~# nslookup it-connect.fr 8.8.8.8
^C
root@debian:~# tail -n 1 /var/log/kern.log 
Jan 11 15:35:36 debian kernel: [32562.219519] DNS_8.8.8.8IN= OUT=ens33 SRC=192.168.34.7 DST=8.8.8.8 LEN=59 TOS=0x00 PREC=0x00 TTL=64 ID=15095 PROTO=UDP SPT=48848 DPT=53 LEN=39

Il est alors envisageable d'avoir plusieurs préfixes, chacun affecté à une règle particulière.

II. Action counter dans nftables

Les counters offrent la possibilité de compter les octets et le nombre de paquets qui correspondent à une certaine règle. Cela permet d'obtenir, par exemple, des statistiques qui permettent de répondre à la question "combien de paquets/octets ont passés et dont la structure correspondait avec cette règle ?".

Un premier exemple pour que vous voyez à quoi cela ressemble : Je souhaite compter tous les paquets sortants qui porteront sur le protocole DNS en UDP. Pour faire plus clair, je souhaite savoir combien de requêtes DNS mon système va émettre. Je saisis donc, dans ma table de la famille IPv4, dans ma chaine attachée au hook OUTPUT la règle suivante :

root@debian:~# nft add rule mon_filtreIPv4 output udp dport 53 counter

Si l'on étudie cette règle d'un peu plus près :

  • nft add rule : j'ajoute une règle via nftables;
  • mon_filtreIPv4 output : je l'ajoute dans la table mon_filtreIPv4 dans la chaine output;
  • udp dport 53 : je filtre les paquets utilisant le protocole de transport UDP  avec pour port destination 53;
  • counter : la seule action que je vais effectuer sur ces paquets est counter, c’est-à-dire compter combien d'octets passent.

Après avoir passé cette règle, en l'ayant adaptée à vos tables et chaines existantes, vous pourrez effectuer des requêtes DNS. Ensuite, nous pourrons regarder notre table mon_filtreIPv4 via la commande habituelle :

root@debian:~# nft list table mon_filtreIPv4 –a

Et voici ce que l'on pourra apercevoir :

root@debian:~# nslookup it-connect.fr
Server: fd0f:ee:b0::1
Address: fd0f:ee:b0::1#53

Non-authoritative answer:
Name: it-connect.fr
Address: 35.181.159.169
Name: it-connect.fr
Address: 52.47.187.175
Name: it-connect.fr
Address: 15.188.66.177

root@debian:~# nft -a list table mon_filtre 
table ip mon_filtre { # handle 8
    chain output { # handle 21
        type filter hook output priority filter; policy accept;
        udp dport 53 counter packets 2 bytes 118 # handle 22
   }
}

On voit en effet que la valeur counter s'est incrémentée. J'ai donc généré 2 paquets DNS, ce qui représente 118 octets.

À noter que je n'ai effectué ici qu'un counter. J'aurais également pu, grâce à la prise en compte des multi-actions compter et accepter les paquets dans la même commande (ce qui n'aurait été utile que si je terminais ma chaine par un drop) :

root@debian:~# nft add rule mon_filtre output udp dport 53 counter accept

Il faut tout de même noter une particularité intéressante de la fonction counter. Celle-ci va être effective en fonction de l'endroit où vous la placez. Je m'explique, pour cette règle, tous les paquets UDP en destination du port 53 et de l'adresse IP 1.1.1. passant sur le hook ouput vont être comptés :

root@debian:~# ip daddr 1.1.1.1 udp dport 53 counter

En revanche pour celle-ci, tous les paquets à destination de l'adresse IP 1.1.1.1, peut importe le port de destination, vont être comptés :

root@debian:~# ip daddr 1.1.1.1 counter udp dport 53

Avez-vous vu la différence ? L'instruction counter n'est pas positionnée au même endroit ! L'endroit où est positionné la règle counter va donc jouer sur ce qui va être compté. Encore une fois, c'est une subtilité qui permet de rendre l'écriture de règle et la manipulation de nftables plus souple.

Suite à l''application de ces règles, je fait un nslookup (requête DNS) en utilisant le serveur DNS 1.1.1.1, ainsi qu'une requête HTTP avec wget. Voici l'état des compteurs après ces opérations :

table ip monfiltre { # handle 15
    chain output { # handle 8
        type filter hook output priority filter; policy accept;
        ip daddr 1.1.1.1 counter packets 8 bytes 449 udp dport 53 # handle 9
        ip daddr 1.1.1.1 udp dport 53 counter packets 2 bytes 118 # handle 10
    }
}

La règle 0 a comptée l'échange HTTP et l'échange DNS (8 paquets en tout), alors que la règle 10 n'a comptée que l'échange DNS (2 paquets parmi les 8). Cela qui montre qu'en fonction de l'endroit où vous positionnez votre counter, il ne comptera pas les mêmes choses. Soyez donc prudent.

Enfin, si vous souhaitez remettre les compteurs à 0, c'est tout à fait possible grâce à la commande suivante :

(echo "flush ruleset"; nft --stateless list ruleset) | nft -f -

 

author avatar
Mickael Dorigny Co-founder
Co-fondateur d'IT-Connect.fr. Auditeur/Pentester chez Orange Cyberdéfense.
Partagez cet article Partager sur Twitter Partager sur Facebook Partager sur Linkedin Envoyer par mail