documentation/services/networking/nftables.md

356 lines
14 KiB
Markdown

# NFTables
## Introduction
`NFTables` est un outil permettant de de gérer une partie de la gestion des
paquets réseaux dans une machine Linux. Cet outil supplante ses prédécesseurs
xtables (iptables, ip6tables, arptables, ...) et son support est inclus dans les
noyaux Linux modernes. Lorsqu'un paquet traverse, sort de ou entre sur votre
machine, il parcourt un certain chemin de prises de décisions afin de décider de
son sort (jeté, accepté (= intouché), modifié, ...).
## Structure de NFTables
NFTables permet d'organiser les règles définissant le comportement à avoir avec
un paquet en fonction de certains critères.
Les règles ne sont pas appliquées par NFTables à n'importe quel moment. En
effet, certaines règles ne concernent que les paquets issus de la machine,
d'autre les paquets sortant, ...
Afin de regrouper les règles agissant au même endroit, les règles de NFTables
sont regroupées en chaînes d'actions, lesquelles concernent un crochet de prise
de décision du noyau.
Là encore, on peut souhaiter avoir des chaînes différentes pour les paquets IP,
IPv6, arp, ... . Pour cette raison, les chaînes sont regroupées dans des tables,
chacune ne concernant un ou plusieurs type de protocole (généralement de couche
3).
La structure des règles NFTables ainsi obtenue devient :
```txt
+------------------------------------------------------------+
| Table concernant les paquets IP |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| +----------------------------------------------------+ |
| | Chaîne concernant le crochet du filtre d'entrée | |
| | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| | | |
| | +--------------------------+ | |
| | | Règle 1 | | |
| | | ~~~~~~~ | | |
| | | paquet TCP ? | | |
| | | à destination du port 53 | | |
| | | vient-il de a.b.c.d | | |
| | | action: rejeter | | |
| | +--------------------------+ | |
| | | |
| | +----------------------------------+ | |
| | | Règle 2 | | |
| | | ~~~~~~~ | | |
| | | paquet TCP ? | | |
| | | lié à une connexion déjà établie | | |
| | | action: accepter | | |
| | +----------------------------------+ | |
| | | |
| | ... | |
| +----------------------------------------------------+ |
| |
| +----------------------------------------------------+ |
| | Chaîne concernant le crochet du NAT en entrée | |
| | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| | | |
| | +----------------------------------+ | |
| | | Règle 1 | | |
| | | ~~~~~~~ | | |
| | | paquet provenant de a.b.c.d/m | | |
| | | paquet sortant par l'interface x | | |
| | | action: snat to x.x.x.x | | |
| | +----------------------------------+ | |
| | | |
| | ... | |
| +----------------------------------------------------+ |
| |
+------------------------------------------------------------+
+----------+
| Table 2 |
| ~~~~~~~ |
| ... |
+----------+
...
```
Comme le décrit le schéma précédent, les tables sont juxtaposées les unes les
autres. Lorsqu'un paquet est traité, selon le protocole utilisé, aucune, une ou
plusieurs tables seront utilisées pour charger les règles à appliquer. À
chaque étape de traitement du paquet, les chaînes correspondant au paquet seront
alors appliquées.
Un paquet parcourt les règles d'une chaîne dans l'ordre de leur écriture.
Si une action est indiquée dans le corps d'une règle, elle sera exécutée si
toutes les conditions la précédant dans l'écriture de la règle sont vérifiées.
Le parcourt d'une chaîne s'arrête lorsqu'un verdict s'applique au paquet
considéré. Un verdict est une action particulière parmi peut être :
* l'abandon du paquet
* l'abandon du paquet avec choix du message retourné à l'émetteur
* L'acceptation du paquet
* Le saut à une autre chaîne (avec retour à la chaîne en cours ensuite)
* Le saut à une autre chaîne (sans retour à la chaîne en cours après)
## Table
Une table est caractérisée par :
* Un nom
* Un type de paquet : principalement ip (pour l'IPv4), ip6 (pour l'IPv6) et inet
(pour l'IPv4 et IPv6).
Une table peut être ajoutée avec la commande `nft add table <nom de la table>`
si vous souhaitez utiliser la commande `nft`, ou avec le morceau de
configuration suivant :
```txt
table <type> <nom de la table> {
[contenu de la table (chaînes)]
}
```
## Chaîne
Une chaîne est caractérisée par :
* La table dans laquelle la chaîne se trouve
* Le nom de la chaîne
Lors de la création / utilisation d'une chaîne, il faut aussi décider !
* Le crochet NFTables concerné par la chaîne (input, output, ...)
* Le type de la chaîne (filtre, nat, raw, mangle)
* La priorité de la chaîne (filter (=0), srcnat (=-100), dstnat (=100)).
Une chaîne peut aussi avoir un verdict par défaut sur les paquet (accept, drop,
reject [with MSG], ...).
Une chaîne peut être créée par la commande
```txt
nft add chain <type de la table> <nom de la table> <nom de la chaîne> '{ type <type> hook <crochet> priority <priorité>; [policy <verdict par défaut>;] }'
```
ou par le morceau de configuration textuelle suivant :
```txt
chain <nom de la chaîne> {
type <type> hook <crochet> priority <priorité> ; policy <verdict par défaut>;
[contenu de la chaîne]
}
```
## Règle
Une règle est caractérisée par la chaîne dans laquelle elle est contenue.
Dans la définition d'une règle, il est possible d'indiquer :
* des conditions écrites `<type> <valeur>`, où le type est un champ du
paquet et la valeur est la valeur du champ, comme par exemple `ip protocol
udp` (type : `ip protocol` représentant le champ du protocole de couche 4 dans
un paquet IPv4, valeur `tcp`).
Remarque: Il est possible de nier une condition.
Remarque': Il est possible d'utiliser des ensembles dans les conditions (voir
plus bas).
Les types peuvent être décrits par `nft describe <type>`.
* des actions, parmi lesquelles "compter les paquets" (`counter`), logger les
paquets (`log prefix ...`).
Pour ajouter une règle, il est possible d'utiliser
```txt
nft add rule <type de la table> <nom de la table> <nom de la chaîne> '<règle>'`
```
ou d'ajouter la règle sur une ligne dans la configuration textuelle.
## Ensembles et applications
### Ensembles
Il est courant d'utiliser des ensembles lors de la définition d'un pare-feu, non
seulement parce qu'ils permettent de réduire le nombre de règles à écrire, mais
aussi parce qu'ils sont plus efficaces, NFTables pouvant gérer les tests
sous-jacents luiel(de crapaud)-même.
Un ensemble peut être :
* anonyme (eg: `{A, B, C}`). Dans ce cas, l'ensemble est statique.
* nommé. Dans ce cas, l'ensemble peut être dynamique et avoir diverses options
et paramètres associés (eg: permettre de stocker des intervalles, associer
une durée de vie aux éléments (avec éventuellement un délai par défaut),
compter des paquets, une taille).
L'appartenance à un ensemble `E` est un critère valide noté `{...} @E` (sa
négation est notée `{...} != @E`).
L'ajout ou retrait d'un élément à un ensemble `E` est une action valide (si
l'ensemble n'est pas constant) et se note `(add|delete) @E {...}`.
Dans un fichier de configuration, un ensemble est déclaré (dans une table)
par :
```txt
set <nom de l'ensemble> {
type <type dléments>;
}
```
Le type des éléments peut être donné explicitement ou à l'aide de
`typeof ...`, ou `...` est un critère valide (eg:
`typeof tcp dport` est équivalent à `type inet_service`).
Il est également possible de réaliser des produits cartésiens de types. Par
exemple, le type `ipv4_addr . inet_service` est le type des pairs d'adresse
IPv4 et des ports.
Il y a de nombreuses options utiles lors de la déclaration d'un ensemble :
* Le nombre d'éléments max (`size <nombre>`). La valeur par défaut est 65536.
* Les drapeaux (`flags ...`), dont `timeout` ou `intervals`
* Les éléments initiaux (`elements = { e0, ..., en }`)
### Applications
TODO: `nft (add|...) map ...`
## Interaction avec le pare-feu
Il est souvent pratique d'ajuster les règles d'un pare-feu (ajout d'un service,
de règles, réordonnancement des règles, ...). Un moyen naïf pour faire cela est
de réinitialiser le pare-feu et de charger un nouveau fichier de configuration.
L'inconvénient est que cela :
* vide les ensembles et applications remplis dynamiquement
* permet à des intrus d'établir des connections illicites si elles sont
initialisées durant la remise à zéro (moins important car la recharge d'un
fichier de configuration est rapide).
### La commande `nft`
La commande `nft` permet certes d'ajouter des tables, des chaînes et des règles
dynamiquement, comme expliqué ci-dessus. Mais elle permet également de corriger
des erreurs, ce qui peut être pratique par moments.
* Suppression ou remplacement d'une règle par une autre :
* Pour la suppression :
```bash
nft delete rule <type de la table> <nom de la table> <nom de la chaîne> handle <numéro de la poignée (du handler en anglais)>
```
* Pour le remplacement :
```bash
nft replace rule <type de la table> <nom de la table> <nom de la chaîne> handle <numéro de la poignée (du handler en anglais)> <new_rule>
```
__Cas d'usage :__
* si vous utilisez un ensemble anonyme pour gérer quels ports
sont ouverts aux nouvelles connections TCP, vous pouvez le mettre à jour
facilement.
* si vous voulez qu'une règle filtrant les accès TCP permette aussi l'UDP,
vous pouvez par exemple souhaiter remplacer `tcp dport` par
`meta l4proto { tcp, udp} td dport`
* Ajout d'une règle à un endroit précis dans le pare-feu :
Cela peut être fait avec les règles
`nft <add|insert> rule <type table> <nom table> <nom chaîne> handle <numéro> <règle>`.
`add` permet d'insérer la règle après la poignée spécifiée. Si aucune poignée
n'est spécifiée, la règle est ajoutée à la fin de la chaîne.
`insert` permet d'insérer la règle avant la poignée spécifiée. Si aucune
poignée n'est spécifiée, la règle est ajoutée au début de la chaîne.
* Options pratiques (`nft -option_pratique ...`) :
* `-t` permet de ne pas afficher le contenu des ensembles et applications
dans la sortie de la commande qui suit
* `-a` permet d'afficher les numéros de poignée
* `-n` permet d'afficher les noms des services plutôt que les ports
(déconseillé car la fonction des ports dans les noms de service n'est pas
injective)
## Journalisation et surveillance
### Journalisation
Il peut être intéressant d'écrire des entrées dans le journal d'un routeur. Il
est d'usage au Cr@ns de noter toutes les nouvelles connections.
Cela peut être fait grâce à l'action `log prefix "..."`, ou `"..."` est le
préfixe à utiliser dans le journal du système.
### Surveillance
Il est possible de demander à NFTables d'afficher des messages de débogage,
liés entre autre aux changements, retraits ou ajouts de règles, de tables, ... à
l'aide de la commande `nft monitor`
* __Changements__
* Changements précis : `nft monotor ...
<rules|chains|ruleset|tables|sets|elements>`, où `...` peut être rien,
`destroy` ou `add` selon si l'on veut tout entendre, n'entendre que les
retraits ou les ajouts.
* Changements quelconques : `nft monotor` pour tout entendre (y compris les
parcours des paquets marqués décrits ci-dessous).
* __Débogage__
À des fins de débogage, il est parfois pratique de savoir par quelles règles
un paquet donné est passé et quelle verdict il a subit. À cette fin, NFTables
permet de marquer le paquet à l'aide de l'action `meta nftrace set 1`.
Les événements internes de NFTables concernant ce paquet peuvent ensuite être
affichés avec la commande `nft monitor trace`.
## Divers
* `nft flush ruleset` vide la table
* `nft -f <file>` lit la configuration d'un ficher et l'applique
* `systemctl reload nftables` a le même effet que `nft -f /etc/nftables.conf` si
le service existe.
* La famille d'une table peut être omise dans age de la commande `nft` s'il n'y
a pas d'ambigüité.
* Si aucune chaîne / table / règle n'est traversée, le paquet est accepté par
défaut (si on ne filtre pas, tout passe)
* Il est possible d'utiliser des variables dans un fichier de configuration.
Leur valeur sera substituée lors de la lecture du fichier. Un exemple est
donné dans le fichier `services/firewall.md`.