Contents
Apperçu
Formations préalables
Pour mieux comprendre cette formation, il est fortement recommandé d'avoir suivi FormationRoutageLinux, spéficiquement pour:
- comprendre le modèle OSI
- comprendre le rôle du routage, que le firewall vient complémenter
être à l'aise d'utiliser la notation de préfixe CIDR pour référer à des blocs d'adresses IP (sous-réseaux)
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.
- Comprendre le rôle d'un firewall dans la réseautique
Comprendre quels chemins les paquets réseau peuvent suivre lorsqu'ils sont traités par netfilter et comment on peut les influencer
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
- 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:
- démarrer les VMs:
vagrant up pm_buster
vagrant up --no-provision pc_buster
- installer les outils nécessaires sur les VMs:
vagrant ssh pc_buster
sudo apt update; sudo apt install tmux tcpdump nftables
Théorie, concepts et information
TL;DR:
Un firewall en terme de réseautique c'est une machine qui sert à restreindre quels types de paquets réseau ont le droit d'être transmis.
- Avec un firewall, on peut bloquer certains types de paquets spécifiquement et permettre tout le reste, ou bien à l'inverse permettre seulement certains types de paquets et bloquer tout le reste.
- Un firewall peut se comporter différemment selon la provenance et la destination de paquets réseau. Celà nous permet de le configurer pour avoir des règles différentes selon le chemin emprunté par le trafic réseau.
- Pour configurer un firewall, on défini généralement une politique globale (accepter ou rejeter) appliquée aux paquets qui n'ont pas pu correspondre à une règle, puis une série de règles par rapport à la constitution des paquets réseau et des actions à prendre si les paquets correspondent à une règle.
- Dans linux, le firewall fait partie du kernel.
- Sous d'autres plateformes -- généralement des machines construites exclusivement pour cette tâche, le firewall peut être implémenté en circuits électroniques et alimenté par des règles qui sont stockées en mémoire.
- Sous d'autres plateformes, le firewall est peut-être un logiciel qui roule sous le système.
- Les solutions logicielles sont par contre beaucoup trop lentes pour les connexions modernes de centre de données (e.g. 10Gbps, 100Gbps) et donc c'est très rare d'avoir une solution strictement logicielle -- le plus souvent, il y aura un logiciel utilisateur qui permet de configurer le firewall, mais le filtrage lui-même sera fait par le kernel.
L'outil qui est présentement (depuis debian buster) recommandé pour configurer le firewall de Linux est nft
NetFilter est le nom du projet du firewall de linux. nftables (et son interface en ligne de commande, nft) est une réimplémentation de ce projet pour remplacer tous les outils iptables, ip6tables, arptables et ebtables.
- Les firewalls sont utiles pour limiter certaines utilisations du réseau, donc:
ça peut permettre d'isoler certains segments pour définir des droits d'accès
ça aide à réduire la surface d'attaque au niveau réseau pour améliorer la sécurité de l'infrastructure
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.
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:
Link layer - niveau 2 (Data link)
Network layer - niveau 3 (Network e.g. ipv4/ipv6)
Protocol layer - niveau 4 (Transport e.g. tcp/udp)
Application layer - niveau 7 (Application)
Il y a trois grandes sections de traitement des paquets (à la verticale dans le diagramme):
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.
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
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
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:
ip (IPv4), ip6 (IPv6) et inet (IPv4 et IPv6) traitent des adresses IP, donc on tente d'influencer le paquet selon l'information du niveau 3 du modèle OSI.
note: on peut également influencer le niveau 4 du modèle OSI avec les familles d'adresses IP en utilisant les critères de règles
arp traite des adresses MAC source et destination, donc on tente d'influencer le paquet selon l'information du niveau 2 du modèle OSI.
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:
prerouting
- avant même que la décision de routage soit prise pour un paquet entrant, on peut prendre la décision de bloquer un paquet
- ou alors on peut modifier le paquet, ce qui pourra avoir un impact sur la décision de routage
un exemple d'utilisation de prerouting c'est la fonctionnalité NAT qui peut réécrire l'IP de destination lors de l'entrée d'un paquet pour une adresse "interne" qui aurait préalablement établi une connexion.
input
le routage (ou bien le niveau link) a décidé que le paquet était bien à destination de la machine. On peut alors filtrer/bloquer ou modifier le paquet
forward
- le paquet ne fait que passer pas la machine. il sera renvoyé vers une sortie sans passer par une application. c'est un cas qui arrive par exemple dans un routeur quand on ne fait que réacheminer les paquets vers un autre réseau
- on peut tout de même décider de filtrer un paquet lors de son passage vers une autre interface / un autre réseau
output
- le paquet se prépare à sortir d'une interface réseau
- on peut le bloquer par exemple s'il ne serait pas supposé sortir par l'interface qui a été choisie
- on peut le bloquer si on pense qu'une communication en sortie avec un certain port source ou destination ne devrait pas être permis pour les applications
postrouting
- point final de décision ou modification d'un paquet
les paquets qui sont forwardés ne passent pas par le hook output et le hook postrouting peut donc être utilisé pour affecter la sortie du paquet
un exemple d'utilisation de postrouting c'est la fonctionnalité NAT qui peut réécrire l'IP source lors de la sortie d'un paquet pour l'adresse "externe" pour s'assurer que les réponses reviennent bien au bon endroit
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'est possible de tout mettre dans la même table
- On peut également séparer les opérations qui n'ont pas besoin de certains autres objets pour clarifier la configuration en blocs logiques d'opérations plus petits
- Cette séparation permet également d'avoir des configurations de firewall pour différentes applications qui ne sont pas reliés entre elles.
Par exemple, libvirt configure sa propre table lorsqu'on utilise nft comme backend de firewall
Un autre exemple pourrait être fail2ban qui configure son propre filtrage à part
Ça peut nous être également utile pour plus facilement déterminer les différentes fonctionnalités qu'on implémente dans le même firewall. Par exemple de distinguer le filtrage des paquets entrant dans la machine physique du filtrage fait sur un bridge pour les machines virtuelles qui l'utilisent pour la communication.
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:
utiliser un hook et être donc un point d'entrée de traitement
la documentation anglophone de l'outil appelle ces chains des base chains (alors que les autres sont simplement des chains)
dans ce cas, on peut déterminer un politique pour la chain:
accept -- si un paquet est passé par toutes les règles de la chain sans se faire interrompre par une action terminale (voir prochaine section), envoyer le paquet à la prochaine étape (e.g. cause une action accept). C'est la politique par défaut
drop -- si un paquet est passé par toutes les règles de la chain sans se faire interrompre par une action terminale, le paquet est supprimé (e.g. cause une action drop)
ne pas utiliser de hook et n'être utilisée que dans le cas d'un jump vers celle-ci (voir la prochaine section pour plus d'information sur les différentes actions).
- Dans ce cas-ci, on sépare les règles dans cette chain simplement pour mieux organiser les choses
par exemple, une chain n'utilisant pas de hook peut être la destination de plusieurs actions jump provenant de plusieurs autres points différents -- on réduit donc la duplication de code.
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.:
gestion des règles sur la ligne de commandes: https://wiki.nftables.org/wiki-nftables/index.php/Simple_rule_management
expressions principalement utilisées pour identifier un paquet: https://manpages.debian.org/buster/nftables/nft.8.en.html#PAYLOAD_EXPRESSIONS
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:
anonyme et donc immuable s'il est défini directement dans la définition d'une règle en utilisant des accolades et les valeurs du set (e.g. {443, 80} pour définir un set de deux numéros de ports)
nommé s'il est défini dans une instruction à part (add set ...)
Un set nommé peut avoir des éléments ajoutés et retirés de manière dynamique soit via la ligne de commande soit automatiquemnt par une règle du firewall
Un set nommé peut également être défini comme étant immuable. l'utilité de ce cas-ci c'est de pouvoir réutiliser le même set dans plusieurs règles avec une seule définition centralisée (par exemple on pourrait définir les plages d'adresses IP qui sont à nous et qu'on espère voir comme source dans notre centre de données mais qu'on n'espère pas voir comme source à l'extérieur)
On doit y référer en utilisant @nom_du_set dans une règle.
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.
une action terminale met fin au traitement du cheminement d'un paquet
une action non-terminale permet de continuer l'évaluation des règles, mais peut par exemple influencer où l'évaluation des règles continue
Actions terminales:
- modifier le cheminement du paquet:
accept -- terminer l'évaluation des règles pour l'étape courante et envoyer le paquet à la prochaine étape (e.g. la prochaine boîte dans le diagramme en début de section)
drop -- terminer l'évaluation des règles et supprimer le paquet. plus aucun autre traitement ne sera fait sur ce paquet
reject -- équivelent à drop, mais créé et envoie un paquet réseau de réponse destiné à l'adresse source du paquet qu'on a drop pour l'informer de la décision de rejeter le paquet.
Actions non-terminales:
- modifier le cheminement du paquet:
return -- stopper l'évaluation des règles dans la chain actuelle et revenir dans la chain où on se trouvait auparavant pour y reprendre l'évaluation des règles à partir du point où on était au moment d'un jump
jump -- enregistrer la position actuelle dans la chain, puis déplacer l'évaluation des règles vers le début d'une autre chain
goto -- comme un jump mais sans enregistrer la position actuelle dans la chain. Donc on ne peut pas utiliser de return pour revenir au point d'appel d'un goto.
log -- envoyer un message au service de syslog
log level: permet d'envoyer les logs du firewall ailleurs que dans le fichier syslog
meta -- apposer une méta-information au paquet qui sera conservée durant son traitement dans le firewall et qui peut être consultée pour influencer les décisions d'une règle
c.f. https://wiki.nftables.org/wiki-nftables/index.php/Matching_packet_metainformation
- c'est une action plus avancée, mais nous allons l'utiliser dans les exercices pour configurer le traçage de paquets pour débugger les règles.
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:
- ne rien bloquer en sortie
- bloquer tout ce qui rentre sauf
- tout permettre sur le device "loopback"
- les connexions déjà établies
- permettre certains cas essentiels de rentrer sans connexion préàlable: ICMP, IGMP
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:
- ne rien bloquer en sortie
on peut décider d'implémenter une règle stricte pour la sortie mais c'est beaucoup de travail à maintenir pour s'assurer qu'on ne bloque rien de légitime!
- tout bloquer ce qui rentre sauf
- permettre le trafic sur le device "loopback"
- permettre en entrée les services essentiels: ICMP
- permettre en entrée les services qui sont offerts par le serveur: SSH, HTTP(s)
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:
le serveur HTTP: curl http://$ip_de_la_vm/
munin-node: nc $ip_de_la_vm 4949
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.
- notre connexion ssh tient bien le coup. On peut également créer une nouvelle connexion SSH vers la VM
- en effet, on peut voir une ligne de log correspondante dans le syslog:
Sep 24 06:59:12 debian-10-amd64 kernel: [11037.494917] IN=eth0 OUT= MAC=52:54:00:fd:4e:5b:52:54:00:6f:23:42:08:00 SRC=192.168.121.1 DST=192.168.121.183 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=54243 DF PROTO=TCP SPT=55782 DPT=22 WINDOW=64240 RES=0x00 SYN URGP=0
- en effet, on peut voir une ligne de log correspondante dans le syslog:
- http est toujours rejoignable
munin-node n'est pas rejoignable (normal puisqu'on n'a pas permis de connexion vers ce 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:
- débugger les règles
nftrace dans les règles + nft monitor trace -- c.f.: https://wiki.nftables.org/wiki-nftables/index.php/Ruleset_debug/tracing
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:
- ne rien bloquer, sauf à destination de l'IP du routeur lui-même
créer un set dynamique qui pourra être utilisé pour bloquer certaines adresses
- bloquer le trafic réseau qui vient de "l'extérieur" et qui utilise une adresse IP qu'on s'attend à ne voir seulement qu'à "l'intérieur" (e.g. au centre de données, tout ce qui arrive des ISPs et qui utilise des IPs de Koumbit ne doit pas pouvoir continuer son chemin parce que c'est clairement du faux trafic réseau -- on s'attend à ce que les adresses IP de Koumbit ne soient utilisées qu'à l'intérieur de notre infrastructure)
- créer une DMZ: bloquer tout le trafic d'un sous-réseau à tous les autres dans les deux sens, sauf certains cas spécifiques
- c'est une méthode utile pour limiter les façons d'entrer sur des serveurs critiques, mais également de limiter la propagation d'une attaque.
- bloquer certains cas d'attaques (e.g. tcp flood, syn flood, ddos)
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
- conntrack (connection tracking)
- permet de conserver de l'information (un état) par rapport aux paquets réseau. on peut ensuite utiliser l'information pour:
- limiter le nombre de paquets d'un certain type dans une intervalle de temps
implémenter NAT (e.g. se rappeler de l'adresse source ou destination pour une certaine conexion donnée). nft utilise conntrack pour implmenter le NAT
implémenter un proxy FTP pour les connexions actives
- ..autres?
- peut avoir un impact important sur la performance (consommation de mémoire, utilisation de CPU) d'un firewall
- permet de conserver de l'information (un état) par rapport aux paquets réseau. on peut ensuite utiliser l'information pour:
tc (traffic control) pour configurer le traffic shaping
- permet d'implémenter du QoS réseau pour prioriser certains types de trafic réseau
nftables peut interagir avec les règles tc en marquant les paquets comme correspondant à un certain niveau de priorité (e.g. classer les paquets selon nos règles de traffic shaping)
- une machine virtuelle additionnelle dans le kernel qui permet le filtrage de paquets réseau dans sa forme "crue" avant qu'ils ne soient enovyés à la stack réseau de Linux et donc avant qu'il y ait du travail de décapsulation.
- permet de grandement accélérer les choses pour les applications qui doivent inspecter tous les paquets de l'interface (comme tcpdump): ça permet de présélectionner les paquets que l'application désire traiter et donc pas avoir besoin de tout faire remonter vers le niveau applicatif
- la machine virtuelle eBPF peut être utilisée également pour d'autres tâches de traitement dans le kernel que pour les paquets réseau
Koumbit n'utilise présentement pas eBPF, sauf p-e à travers des outils qui le font pour nous comme tcpdump
Exercice complémentaire:
- use case: routeur du bureau
- tout laisser sortir, rien laisser rentrer sauf les connexions établies
- accepter les connexions ssh vers l'IP du routeur lui-même
- NAT pour IPv4
indice: vous aurez besoin d'une chain du type nat et qui utilise le bon hook
cf. https://wiki.nftables.org/wiki-nftables/index.php/Performing_Network_Address_Translation_(NAT)
le choix du hook dépend du type de NAT que vous désirez faire. Il existe trois types de NAT que nftables peut faire, dépendant de quelle information doit être modifiée pendant le traitement des paquets:
- basé sur l'adresse source -- e.g. un paquet arrive d'un réseau privé et doit se rendre sur un réseau public
- basé sur l'adresse destination -- e.g. un paquet arrive d'un réseau public et devrait pouvoir se rendre à une certaine adresse dans un réseau privé (penser port forwarding)
mangling -- e.g. type spécial de NAT basé sur l'adresse source. c'est un raccourci pour automatiquement configurer l'adresse de retour comme étant celle de l'interface de sortie d'un paquet (donc ce type de NAT ne peut être utilisé qu'avec le hook postrouting puisque c'est seulement là qu'on connaît l'interface de sortie)
configurer des niveaux de priorité avec tc
Marquer la priorité de certains paquets avec meta priority pour configurer du trafic shaping