19 mars 2024

Tuto Nftables, partie II: Manipulation de tables, chaines et règles

Tutoriel révisé en décembre 2022 / Debian 11

 

Dans ce second article de la série sur Nftables, nous allons faire de la mise en pratique et étudier les commandes qui vont nous permettre d’interagir avec Nftables.

 

I – Utiliser Nftables

Pour utiliser nftables, plusieurs solutions s’offrent à nous :

En ligne de commande, à la manière d’iptables, avec nft suivi de la commande :

# nft list ruleset

En cli interactif :

# nft -i

Où l’on pourra alors taper :

nft > list ruleset
nft> quit

Et bien sur, à l’aide de scripts, ce que nous verrons après.

Pour lister ce qui a en cours, vous l’avez compris, c’est :

# nft list ruleset

Et pour tout flusher :

# nft flush ruleset

A y est, on est prêt !

II – Les tables

La base, ce sont les tables et de base y’a walou… Comme je vous le disais, on s’organise comme on veut.

Nftables remplaçant plusieurs outils, on peut travailler sur différents types de tables :

  • arp, géré avant par arptables
  • bridge -> ebtables
  • ip -> iptables
  • ip6 -> ip6tables
  • inet -> regroupe à la fois ip et ipv6

Pour le moment, on ne fait que du ipV4, donc type ip.

Pour ajouter une table, c’est :

# nft add table ip toto

Très simple : on ajoute une table de type ip nommée toto.

On aurait pu aussi écrire :

# nft add table toto

Par défaut, sans présence de type, c’est ip.

On peut donc par exemple ajouter une autre table toto, mais de type arp par exemple

# nft add table arp toto

On peut lister les tables présentes :

# nft list tables

Et pour effacer :

# nft delete table toto
# nft delete table arp toto

Note : plus besoin de flush une table « remplie » avant de l’effacer, c’est automatique.

Ceci dit, si besoin de flusher une table spécifique :

# nft flush table toto

Note : Truc à la con, le flush ne supprime que les règles et pas les chaines.

On peut se faire avoir si on a la police sur drop par exemple… Bah oui, la règle qui vous autorise dégage, mais le drop de la police reste et paf, au revoir le ssh… Bref, méfiance.

Maintenant, ajoutons donc une nouvelle table dédié au filtre :

# nft add table filter

Je l’appelle filter, mais ce n’est en aucun cas une obligation… Mais soyons pragmatique, c’est quand même un nom assez équivoque, autant l’utiliser !

III – Chaines

Maintenant, ajoutons notre première chaine :

# nft add chain filter input { type filter hook input priority 0\;}

Vous noterez la présence du \ pour échapper le ;…

Le début de la commande est simple, on ajoute une chaine input dans la table filter.

Ensuite, on vient déclarer un hook, transformant notre chaine en « chaine de base », c’est à dire qu’elle s’accroche dans netfilter. Ainsi, un paquet arrivant en input, passera dans notre chaine.

La encore, je la nomme input car c’est le nom le plus évident, mais je pourrais tout à faire l’appeler toto, ce qui est important, c’est de se brancher au bon endroit.

En ce qui concerne la priorité, on peut jouer avec plusieurs tables et plusieurs chaines qui s’accrochent au même endroit et préciser ainsi qui passe avant (priorité la plus faible, -100 passe avant 0…)

Pour ajouter une chaine « standard » :

# nft add chain filter hoho

Ce genre de chaine sera utile pour faire des jump, goto…

Pour lister, on peut faire :

# nft list table filter

ou bien :

# nft list chains

Et pour supprimer :

# nft delete chain filter hoho

IV – Règles

Passons maintenant à la création des règles.

Le fonctionnement est simple :

nft add rule latable lachaine condition 1, condition 2, etc... action1, action2....

Tant que le paquet vérifie les conditions, ils continuent la règle et à la fin, on applique la ou les actions.

Oui, gros changement par rapport à iptables, on peut faire plusieurs actions dans une même règle.

Si a un moment, une condition n’est pas remplie, le paquet passe à la règle suivante.

A – Ajout d’une règle

# nft add rule filter input tcp dport 22 accept

On ajoute une règle dans la chaine input de la table filter et tout ce qui à pour port destination 22, on accepte. Vu que la police par défaut est sur accept, ça ne sert à rien, on est d’accord, on fera quelque chose de réaliste après.

Si on veut filtrer sur l’ip source :

# nft add rule filter input ip saddr 10.10.10.10 tcp dport 22 counter accept

Et au passage, on place un compteur. Bon la encore, cette règle ne sert à rien car la règle du dessus accepte déjà les paquets pour le port 22.

 

B – Suppression

Bon, comment la supprimer alors ?

On va déjà afficher une info en plus :

# nft list ruleset -a

Qui nous affiche les handles :

table ip filter { # handle 197
    chain input { # handle 1
        type filter hook input priority 0; policy accept;
        tcp dport ssh accept # handle 3
        ip saddr 10.10.10.10 tcp dport ssh counter packets 0 bytes 0 accept # handle 4
        }
}

L’œil curieux remarquera qu’il manque le handle 2 : il était associé à la chaine hoho que nous  avons supprimée tout  à l’heure.

Le handle ne se rapporte pas à l’ordre d’exécution (c’est toujours du début à la fin), mais à l’ordre d’ajout.

Pour supprimer notre règle, deux options :

Soit on retape tout :

# nft delete rule filter input ip saddr 10.10.10.10 tcp dport 22 counter accept

Mais c’est long…

Ou bien en passant par son handle :

# nft delete rule filter input handle 4

Et si on liste :

table ip filter { # handle 197
    chain input { # handle 1
        type filter hook input priority 0; policy accept;
        tcp dport ssh accept # handle 3
    }
}

C – Insertion

Par défaut, add sans précision ajoute à la fin de la chaine :

# nft add rule filter input tcp dport 23 accept

Et on regarde :

table ip filter { # handle 197
    chain input { # handle 1
        type filter hook input priority 0; policy accept;
        tcp dport ssh accept # handle 3
        tcp dport telnet accept # handle 5
    }
}

Notre règle est avec le handle 5, à la fin de la chaine.

Et si je veux insérer une règle entre 3 et 5 :

# nft add rule filter input position 3 tcp dport 25 accept

Nous donne :

table ip filter { # handle 197
    chain input { # handle 1
        type filter hook input priority 0; policy accept;
        tcp dport ssh accept # handle 3
        tcp dport smtp accept # handle 6
        tcp dport telnet accept # handle 5
    }
}

On peut aussi utiliser insert, par exemple pour insérer entre 3 et 6

# nft insert rule filter input position 6 tcp dport 80 accept

Ce qui nous donne :

table ip filter { # handle 197
    chain input { # handle 1
        type filter hook input priority 0; policy accept;
        tcp dport ssh accept # handle 3
        tcp dport http accept # handle 7
        tcp dport smtp accept # handle 6
        tcp dport telnet accept # handle 5
    }
}

Et naturellement, insert sans indication :

# nft insert rule filter input tcp dport 8080 accept

Nous ajoute la règle en début de chaine :

table ip filter { # handle 197
    chain input { # handle 1
        type filter hook input priority 0; policy accept;
        tcp dport http-alt accept # handle 8
        tcp dport ssh accept # handle 3
        tcp dport http accept # handle 7
        tcp dport smtp accept # handle 6
        tcp dport telnet accept # handle 5
    }
}

Voila pour la manipulation des règles. Bien évidement, par la suite, on va voir des choses un peu plus évolués…

 

V – Conclusion

On a vu comment manipuler les tables, les chaines et les règles.

Pour info, il existe dans le paquet iptable la commande iptables-translate qui permet de traduire vos anciennes règles :

# iptables-translate -t filter -A forward -i eth2 -p tcp -s 10.10.10.10 -d 10.10.10.20 --dport 22 -j ACCEPT

Vous donnera la traduction :

# nft add rule ip filter forward iifname "eth2" ip saddr 10.10.10.10 ip daddr 10.10.10.20 tcp dport 22 counter accept

 

Ceci dit, taper ces commandes à la main, c’est bien pour apprendre, mais pour faire quelque chose de fonctionnel et automatique, c’est pas top…

Puis on n’a pas vu le nat…

Pour le coup, dans l’article suivant, on va mettre en place un premier script pour un firewall simple.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *