Apperçu

Formations préalables

Pour mieux comprendre cette formation, il est fortement recommandé d'avoir suivi FormationRoutageLinux, spéficiquement pour:

Temps estimé

De 1 à 2 heures

Objectifs

On vise par cette formation à utiliser l'outil nft pour configurer le firewall de Linux. Nous n'utiliserons pas la commande iptables puisque celle-ci n'est plus la commande incluse par défaut dans debian.

  1. Comprendre le rôle d'un firewall dans la réseautique
  2. Comprendre quels chemins les paquets réseau peuvent suivre lorsqu'ils sont traités par netfilter et comment on peut les influencer

  3. Configurer le firewall de linux à l'aide de la commande nft et de fichiers "scripts" pour nft

    • Vider (flush) les règles
    • Configurer une règle qui bloque un type de paquet spécifique en entrant
    • Configurer une série de règles qui bloquent tout sauf un certain type de paquet entrant
    • Changer la politique d'entrée de paquets pour bloquer par défaut, et ajouter des règles pour permettre certains types de paquets spécifiques
  4. Faire un peu de débuggage minimal des règles de firewall

Préparation des participant.e.s

Notez que le setup d'exercices peut être reproduit ailleurs dans l'absence des outils si quelques personnes n'ont pas accès au control-repository puppet de Koumbit. Ici, on utilise simplement deux VMs qui sont connectées directement une à l'autre via un lien réseau:

  • pc_buster est utilisée pour configurer le firewall

  • les adresses IP des VMs sont pré-configurées

Outils nécessaires pour rouler les exercices de la formation: dans le control-repository puppet, on vise utiliser les VMs (pc_buster et pm_buster) déjà décrites dans le fichier Vagrantfile qu'on démarera à l'aide de vagrant et libvirt avec KVM comme outil de virtualisation (commandes plus bas). Voir Vagrant pour la mise en place du setup de machines virtuelles.

A faire pour se préparer aux exercices:

Théorie, concepts et information

TL;DR:

Quand on parle de la "stack réseau" du kernel, on réfère généralement aux différentes sous-routines du kernel qui traitent les paquets réseau et au cheminement que les paquets prennent dans celles-ci.

https://en.wikipedia.org/wiki/File:Netfilter-packet-flow.svg

Nous n'explorerons pas toutes les boîtes de traitement des paquets dans le graphique ci-dessus pendant la formation. Les boîtes qui nous intéressent sont surtout celles nommées filter.

Un exercice complémentaire dans la section Information complémentaire devra également toucher un peu à certaines boîtes nommées NAT lors de la configuration d'un "routeur de bureau".

Toutes les définitions et explications dans la partie d'information proviennent de la page de manuel de `nft`. C'est une excellente source d'information à laquelle vous devriez vous référer. Cette formation-ci sert surtout à présenter les concepts de manière plus succinte et en laissant tomber certaines informations qui sont plus complexes.

Chemins des paquets réseau dans netfilter

Dans le diagramme au début de la section, à l'horizontale les quatre sections réfèrent plus ou moins aux niveaux du modèle OSI (plus de détails sur le modèle OSI dans FormationRoutageLinux) suivant. On peut voir comment les paquets peuvet "monter" dans les niveaux en entrant pour se rendre vers l'application, et à l'inverse comment les paquets doivent "descendre" vers le niveau link pour pouvoir éventuellement sortir sur l'interface:

Il y a trois grandes sections de traitement des paquets (à la verticale dans le diagramme):

  1. input -- l'entrée des paquets dans une interface réseau

    • normalement après cette étape, les données contenues dans le paquet réseau seront acheminées vers l'application qui écoute sur un certain port TCP ou UDP.
  2. forward -- un paquet peut être renvoyé directement vers l'output sans passer par les applications. quand ça arrive, on "forward" le paquet vers la sortie

  3. output -- un paquet s'apprête à sortir de l'interface réseau

    • On peut voir dans le graphique que la décision de routage (voir FormationRoutageLinux) est prise avant tout autre traitement du paquet au niveau du firewall et cette décision de routage influence donc comment le firewall devra interagir avec le paquet. En effet, selon le choix de quelle interface réseau utiliser en sortie, le firewall pourrait prendre des décisions bien différentes!

Concepts de base de NetFilter

nft_ruleset.png

Exemple plus ou moins tiré de: https://wiki.nftables.org/wiki-nftables/index.php/Classic_perimetral_firewall_example

familles d'adresses:

netfilter catégorise les paquets réseau comme ayant des familles d'adresses. Celles-ci correspondent, bien entendu, au type d'adressage qu'on veut influencer ou vérifier lors du passage d'un paquet réseau. Entre autres:

hooks:

Pour chaque famille d'adresse, une série de hooks sont définis pour permettre de faire interagir le firewall avec les paquets.

Dans le diagramme au début de la section, le bas des boîtes, en gris pâle, contient le nom des hooks que le firewall pourra utiliser pour interagir avec les paquets durant ces étapes là.

Particulièrement, pour les familles d'adresses ip (IPv4), ipv6 et inet (IPv4 et IPv6), il existe cinq hooks:

Voir la section des chains pour de la documentation sur comment les hooks sont configurés.

priorité d'un hook:

Le haut des boîtes, soit en bleu ou en vert, est représenté par nft comme un niveau de priorité apposé aux hooks. Les priorités sont numériques par défaut, mais les noms qui sont dans le haut des boîtes du graphique peuvent être utilisés dans l'établissement d'une chain comme raccourcis pour les priorités numériques correspondantes.

Voir la section des chains pour de la documentation sur comment les priorités sont configurées.

tables:

Une table est un conteneur pour des chains, des sets et autres objets contenant un état. Une table doit être assignée à une famille d'adresses spécifique. Les objets contenus dans une table ne peuvent être référencés que par les autres objets dans la même table.

c.f.: https://wiki.nftables.org/wiki-nftables/index.php/Configuring_tables

chains:

Une chain est une série de règles qui seront toutes inspectées une par une lorsque l'évaluation d'un paquet passera par cette chain. Une chain peut soit:

c.f.: https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains

règles:

Une règle évalue les informations d'un paquet, et prend une certaine action (voir prochaine section) si les informations du paquet correspondent à la règle. Une règle contient généralement une série d'expressions qui permettent d'identifier un paquet selon ses propriétés, puis une action. Une règle ne commence par par type, hook ou priority -- ceux-ci sont des paramètres de chain.

c.f.:

sets:

Un set est un tableau de données (éléments) d'un certain type (par exemple des adresses IP, ou encore des numéros de ports TCP ou UDP) qu'on peut utiliser dans des règles.

L'utilisation de sets n'est pas obligatoire et constitue généralement plutôt une manière d'optimiser la configuration du firewall en terme de nombre de règles et de performance de recherche d'information pour les règles.

Un set peut être:

c.f.: https://wiki.nftables.org/wiki-nftables/index.php/Sets

Actions prises par une règle

Lorsqu'une règle a ses critères de remplis, celle-ci peut prendre un certain nombre d'actions.

Actions terminales:

Actions non-terminales:

Scripts nftables

On peut créer des scripts de configuration de firewall:

https://wiki.nftables.org/wiki-nftables/index.php/Scripting

Les scripts de configuration sont chargés via nft -f nom_du_fichier et les changements qui sont apportés par le script sont atomiques et donc si la configuration reste valide, on ne risque pas de perdre des paquets en plein changement des règles. Un script peut également en inclure d'autres.

Les scripts nft nous permettent de créer une configuration permanente pour le firewall.

Et finalement, un autre avantage des scripts c'est qu'on peut définir des variables qu'on pourra réutiliser dans plusieurs règles.

Exercices

Veuillez noter que, comme l'interaction avec la commande nft doit être faite sous l'utilisateur root, toutes les commandes dans les exercices plus bas doivent être exécutées sous root, et donc il es recommandé de vous connecter à cet utilisateur avec la commande:

sudo -i

Aussi, pour nous permettre de retrouver plus facilement l'historique de nos commandes, on est toujours mieux de rouler les commandes sous une session tmux.

tmux

N.B.: Dans les exemples plus bas, certaines commandes utilisent des guillemets, et d'autre pas.

Les guillemets sont utilisés seulement dans les cas où certains caractères seraient autrement interprétés par bash avant d'être envoyés à la commande, notemment les accolades {} et les point-virgules ;.

Explorer les règles de firewall en place

Pour pouvoir se retrouver dans l'exploration du firewall, on doit tout d'abord être capables de lister les règles et autre éléments présentement configurés.

Pour lister toutes les règles de toutes les chains dans toutes les tables:

nft list ruleset

Présentement, il n'y a aucune configuration en place.

C'est possible qu'il y ait déjà des tables utilisant les noms de tables d'iptables -- l'outil précédent nft -- donc filter, nat, mangle et raw.

Si c'est le cas, les tables qui sont là ont comme objectif de créer un espace de compatibilité pour les anciens outils: iptables, ip6tables, arptables et ebtables.

Il est généralement recommandé de ne pas modifier ou utiliser ces tables pour des configurations via nft: iptables conceptualisait les tables et les chains différemment ça risque de devenir mélangeant, mais aussi les outils de compatibilités pourraient effacer les règles nftables contenues dans ces tables sans que ça soit l'objectif désiré.

Donc on est toujours mieux de créer des tables avec des noms différents de ceux nommés plus haut pour éviter la confusion et les conflits de gestion.

Ok, on va plonger donc! On va ajouter une simple table, avec une simple chain:

nft add table precipice
nft "add chain precipice gouffre { type filter hook input priority 0; policy drop ; }"

.... heh? .. oh 💩 ! on a perdu contact avec la VM! Changer les règles de firewall peut avoir ça comme effet. Oups!

Va falloir reprendre contrôle de la VM par un autre moyen que le réseau donc. La console à la rescousse!

virt-manager

Dans la liste des VMs, si on clique sur celle qui s'appelle control-repo_pc_buster, et ensuite sur le bouton Open, on obtient une console comme si on avait un clavier et un écran direct sur la VM. Fiou! avec ça on va pouvoir remettre le réseau sur les rails.

Ce qu'on vient de faire ici, c'est exactement la même chose qu'on peut faire au centre de données soit avec la console via ganeti pour un VPS, soit avec la console série pour une machine physique.

On utilise ici un outil différent puisque c'est celui disponible pour la gestion des VMs locales avec Vagrant.

Note: on pourrait théoriquement aussi avoir une console sur le command line vers la VM (e.g. virsh console control-repo_pc_buster), mais ça n'est pas fonctionnel par défaut. Pour faire fonctionner ça il faudrait démarrer le service serial-getty@ttyS0 dans la VM pour qu'il y ait qqch qui réponde sur la console. On a décidé de conserver les images pour vagrant les plus simples possible pour qu'on puisse en fait tester que puppet est bien capable de tout configurer à partir de zéro. Par exemple si on utilise le profile grub, ça devrait en principe rendre la console fonctionnelle -- donc c'est une bonne façon de tester que le profile fait bien son travail.

Sur la console, on va devoir se connecter en tant que l'utilisateur vagrant, et le mot de passe est le même que le nom d'utilisateur. Une fois connecté, on peut devenir root avec sudo -i. Rendu là, on peut retourner dans notre labo:

tmux attach

Ok! tout est comme on avait laissé les choses. On va voir ce qui se passe ici.

Pour lister seulement une table, on peut utiliser:

nft list table precipice

Certaines commandes de gestion des configurations de firewall requièrent l'utilisation d'une information additionnelle pour référer à une règle: son identifiant, nommé handle dans la documentation.

Pour lister les règles en montrant leurs identifiants, on peut ajouter l'argument -a à nft. Attention, les arguments de nft doivent se trouver avant les commandes et leurs paramètres (e.g. list ruleset est la commande list et son paramètre ruleset).

Par exemple pour lister toutes les configurations du firewall avec les identifiants:

nft -a list ruleset

Les tables ont un numéro de handle global, pour permettre de les différencier.

Par contre, tous les autres objets contenus dans les tables, donc les chains et les règles ont des numéros de handle qui sont relatifs à la table qui les contient. Ça veut donc dire que vous pourriez voir plusieurs fois le même numéro de handle pour plusieurs règles ou chains mais à travers plusieurs tables.

Une autre conséquence de la configuration qu'on a ajoutée c'est que la VM n'est plus capable de rien rejoindre sur le réseau par elle-même:

ping 8.8.8.8  # pas de réponse

La raison pour ça, c'est que notre chain bloque tout ce qui entre: aucune réponse ne peut jamais se rendre à nous.

Bon, allons: si on retire la table qu'on a créée plus tôt, on va revenir à 0 et enlever du même coup la chain problématique qui nous a embarré en dehors de la VM:

nft delete table precipice

Voilà! on a à nouveau accès SSH. On peut conserver la console ouverte au cas où on s'embarre encore une fois. La console sera également utile pour tester les résultats de configurations du prochain cas d'usage.

Cas d'usage: une workstation

Configurer le firewall pour une workstation est généralement un cas plutôt simple. on veut:

Comme c'est un cas plutôt simple, on va encore une fois utiliser la commande nft directement pour faire la configuration. On utilisera par contre des scripts pour les prochains cas d'usage.

On commence par créer une table qui contiendra notre configuration:

nft create table workstation

Comme on ne veut rien bloquer en sortie, on n'a pas besoin d'ajouter de configuration sur la sortie des paquets. On voudra simplement affecter l'entrée des paquets sur la VM.

On va donc se créer une chain similaire à notre exemple précédent où on a perdu accès (donc préparons-nous à utiliser la console), mais cette fois on va permettre à la VM de recevoir des réponses aux connexions que la VM elle-même a établie.

nft "add chain workstation entree { type filter hook input priority 0; policy drop; }"

hop, bloqué comme plus tôt. Si nous n'étions pas déjà dans la console, c'est maintenant le bon moment de s'y rendre.

Un autre truc qu'on peut vérifier, c'est que ping vers la VM ne fonctionne plus. Pour trouver l'adresse IP de la VM, on peut demander à vagrant:

vagrant ssh-config pc_buster
ping 192.168.121.183  # pas de réponse

On peut commencer par permettre les connexions sur localhost. Le trafic réseau sur localhost devrait toujours pouvoir transiter, sinon certaines applications pourraient se comporter vraiment bizarrement ou ne pas fonctionner du tout -- par exemple, si on a Exim ou Postfix qui écoute sur localhost pour réacheminer les courriels administratifs, ceux-ci ne recevraient pas les courriels à acheminer si le trafic était bloqué pour le device "loopback":

nft add rule workstation entree iifname lo accept

Maintenant on veut permettre les réponses aux connexions établies à la VM elle-même:

nft "add rule workstation entree ct state {established, related} accept"

Voilà, maintenant on peut parler à l'extérieur: ping 8.8.8.8 obtient des réponses!

oh, mais une autre conséquence joyeuse: notre connexion ssh est revenue à la vie. En effet, on avait déjà établi la connexion avant que le filtre ne nous coupe ça et donc maintenant on peut continuer de l'utiliser. Par contre, on ne pourrait plus établir une nouvelle connexion SSH à partir de l'extérieur: si on essaye de se connecter avec vagrant ssh pc_buster, on se heurte au mur et on n'obtient pas de réponse.

Le dernier détail à gérer sur une workstation, c'est les services essentiels qui ont besoin de pouvoir entrer sans qu'il n'y ait eu de connexion préalable:

nft add rule workstation entree ip protocol icmp accept  # permet de faire ping vers la VM
nft add rule workstation entree ip protocol igmp accept  # permet à la VM de s'inscrire dans les groupes multicast (par exemple pour le streaming vidéo, pour certains jeux, et certains protocoles d'auto-découverte de services dans le réseau local

Voilà, avec ça on a une configuration de base pour une workstation! (note: il manque probablement quelques services essentiels pour une configuration réelle, comme par exemple NTP, DHCP. mais l'exemple est gardé simple par exprès)

On peut inspecter le résultat avec:

nft list ruleset

L'output de nft list ruleset peut être sauvegardé dans un script nftables directement.

Comme on a terminé notre cas d'usage, pour nous permettre de passer au prochain exercice, on va retirer toutes les règles de firewall qui ont été configurées dans l'exercice ici pour recommencer à 0:

nft flush ruleset

La commande qu'on vient d'utiliser a comme effet d'effacer:

  • toutes les règles
  • toutes les chains
  • toutes les tables

donc si jamais vous faites ça sur votre laptop directement, attention aux dommages collatéraux: ça veut dire que les configurations ajoutées par des applications seront retirées aussi.

Par exemple les VMs libvirt pourraient perdre la connectivité réseau puisque libvirt ajoute des règles automatiquement au firewall au démarrage du service

Cas d'usage: Un serveur

Le cas d'un serveur ressemble beaucoup à celui d'une workstation sauf qu'on veut permettre à certaines connexions entrantes d'être établies pour les services qui sont supposés être offerts par le serveur. Généralement on veut:

Pour faire un peu différent, on va créer une script nftables qui configurera le tout d'un seul coup quand on l'appliquera. Le script ressemblera beaucoup à l'output de nft list ruleset à la fin de l'exemple de la workstation.

Avant de sauter dans le firewall, on va commencer par installer deux services, dont un qu'on ne voudra pas exposer sur le réseau:

apt install nginx
apt install munin-node

Vérifier qu'on peut rejoindre:

Créer le répertoire /etc/nftables, puis le fichier /etc/nftables/only_services_in.conf, et y inscrire le contenu suivant:

table inet serveur {
        chain entree {
                type filter hook input priority 0; policy drop;
                iifname "lo" accept
                ct state { established, related } accept
                ip protocol icmp accept


                # let http(s) traffic reach the VM
                tcp dport { 80, 443 } accept
                # let ppl connect here with ssh
                tcp dport 22 log accept
        }
}

A noter: on a ajouté log sur la règle qui accepte les paquets entrant ssh. L'effet de ça, c'est une ligne de log sera créée dans le syslog à chaque nouvelle connexion ssh sur le serveur.

Maintenant, pour appliquer ce script on peut demander à nft de le charger et l'appliquer:

nft -f /etc/nftables/only_services_in.conf

Hop! d'un seul coup tout est appliqué. L'application d'un script est atomique et donc s'il y a une erreur quelque part dans le script, aucun changement ne sera appliqué.

Maintenant, on peut revérifier les service.

Encore une fois, pour nous permettre de faire le prochain exercice, on va retirer toutes les règles de firewall:

nft flush ruleset

Cas d'usage: Un routeur

TODO ajouter ça dans un des scripts pour ce cas-ci:

Pour pouvoir bien tester que la configuration du firewall fonctionne comme prévu pour ce cas-ci, on voudra probablement utiliser des VMs différentes, comme par exemple les mêmes VMs utilisées pour la FormationRoutageLinux: rtr_client, rtr0 et rtr_upstream0, et on fera la configuration du firewall sur rtr0.

Dans le cas d'un routeur, on risque d'avoir une configuration plus complexe vu qu'on doit gérer l'entrée/sortie de paquets de provenance de plus de réseaux. La configuration du firewall d'un routeur permet d'établir des règles d'accès à certains réseaux en provenance et à destination de certains autres réseaux. On peut séparer les configurations de certains cas dans différents scripts, et même dans différentes tables, pour garder les choses dans des blocs plus "logiques" et faciliter la compréhension des choses pendant la maintenance.

Quelques exemples de concfigurations qui peuvent être combinées:

TODO: comment faire ça -- utiliser des scripts nft

TODO: Commencer par définir des variables pour permettre de réutiliser un même script nftables pour plusieurs serveurs (noms des interfaces, sets des adresses IPs qui correspondent à des réseaux conus)

Rétrospective

Pendant un maximum de 15 minutes, les participant.e.s sont invité.e.s à partager les éléments qui ont bien ou moins bien fonctionnés et les idées qui pourraient survenir pour des manières d'améliorer le processus.

Quelques éléments qui peuvent faire partie de la rétrospective, dépendant de la grosseur du projet ou de la formation:

  • Sommaire collectif (e.g. résumé rapide en termes que tout le collectif peut comprendre)
    • Pas trop utile pour les projets vraiment simples ou les formations.

  • Chronologie des événements marquant pour le projet
    • Surtout utile pour les projet qui se sont étalés sur plusieurs jours ou plus ou bien quand beaucoup de choses se sont produites simultanément, ce qui a rendu la compréhension des influences de chaque événement complexe.

  • Les bons coups -- qu'est-ce qui a bien fonctionné et qu'on veut tenter de reproduire
  • Les problèmes
    • échecs -- avec l'aide de la chronologie (si elle a été faite), tenter de situer les échecs dans un contexte selon ce qui était connu des participant.e.s aux moments qui ont mené à l'échec
    • problèmes techniques
    • manques de ressources
    • perturbations externes
  • Une liste d'actions à court et/ou à plus long terme pour améliorer les choses telles que soulignées pendant les points précédents
    • Transférer les actions dans redmine pour qu'elles puissent être suivies!

N'hésitez pas à partager les échecs à l'extérieur de l'équipe puisqu'on peut apprendre beaucoup de ceux-ci, mais surtout évitez de les formuler comme un blâme sur la/les personne(s) ayant échoué.

Une rétrospective est surtout utile quand on la partage: ça permet aux autres d'apprendre de nos erreurs et aussi de nos idées d'améliorations. On peut par exemple envoyer une forme écrite par email, ou bien sauvegardée comme page wiki.

Information complémentaire

Exercice complémentaire:


CategoryFormation CategoryFormationPublique

FormationFirewallLinux (last edited 2023-06-16 09:59:52 by mathieul)