Pas de tun0 ? Faites correspondre l’IPsec déchiffré dans nftables avec meta ipsec
Une passerelle basée sur des règles transmet des paquets internes en clair qui arrivent toujours sur eth0. Faites correspondre le chemin de sécurité et le service, pas une interface absente.
Vous attendez une interface de tunnel, vous la cherchez, et il n’y a rien à faire correspondre dans le pare-feu. C’est normal pour une passerelle basée sur des règles : l’échange chiffré arrive sur l’interface physique, et le paquet interne déchiffré qui est ensuite transmis est lui aussi observé comme arrivant sur cette même interface. Il n’y a pas de tun0 parce que rien n’en a créé.
La requête interne reste un paquet distinct avec sa propre source, sa destination et son service, et le noyau sait de quelle association de sécurité il est sorti. C’est cette connaissance qu’une règle de pare-feu peut faire correspondre.
Deux paquets, une interface
Imaginez la direction entrante. Un paquet encapsulé en ESP ou UDP arrive, le noyau trouve la règle correspondante, le déchiffre et remet le paquet en clair au chemin de transmission. Une règle qui ne regarde que le nom de l’interface ne peut pas distinguer ce paquet de tout autre trafic arrivant sur le même câble.
Ce qui le distingue, c’est le contexte du chemin de sécurité entrant attaché au paquet. nftables l’expose sous la forme meta ipsec exists, qui est vrai lorsque le paquet est arrivé par un chemin de sécurité IPsec. Combinez-le avec le pool de clients, la destination et le service, et la règle devient assez précise pour être sûre.
Omettre le contexte est l’erreur courante, et c’est une erreur de sécurité et non une erreur cosmétique. Une règle qui accepte tout ce qui provient du pool de clients accepte tout aussi volontiers un paquet falsifié venu du réseau local, car l’adresse source n’est en soi la preuve de rien. Le contexte du chemin de sécurité est le moyen le moins coûteux de rendre la source significative. Écrivez la règle avec lui dès le départ plutôt que de l’ajouter après un audit.
Écrivez la règle de transmission
Supposez un service d’accès distant IPv4 authentifié avec des clients dans 10.90.0.0/24 et une destination approuvée à 203.0.113.40. Un fragment de règle pour ce flux ressemble à ceci.
meta ipsec exists ip saddr 10.90.0.0/24 \
ip daddr 203.0.113.40 tcp dport 443 \
oifname "eth0" counter accept
Les adresses et l’interface sont des exemples à remplacer. Placez l’équivalent revu dans la règle de transmission avant la règle qui rejetterait sinon le flux, et ne videz pas un jeu de règles existant pour l’installer ; une passerelle n’est pas une expérience en salle blanche.
Le terme oifname mérite d’être conservé. Il documente la décision de sortie et empêche la règle de correspondre dans une direction que vous n’aviez pas prévue, ce qui compte sur une passerelle ayant plusieurs chemins vers internet. Sur une machine de test à un seul lien montant, le terme paraît redondant, et c’est exactement pourquoi il est supprimé avant que la machine n’obtienne un second lien.
Les autorisations pour l’échange externe sont distinctes. Le chemin d’entrée de la passerelle a besoin des ports IKE et ESP ou d’encapsulation UDP qu’il utilise réellement, et ces autorisations n’autorisent rien concernant le paquet déchiffré à l’intérieur. Mélanger les deux, c’est ainsi qu’un tunnel fonctionnel finit avec un pare-feu qui transmet tout.
Le contexte entrant n’est pas le contexte sortant
La même famille d’expressions couvre l’autre direction avec rt ipsec, qui décrit l’association de sécurité sortante qu’un paquet utilisera. Elle répond à une autre question : non pas d’où vient ce paquet, mais s’il sera protégé sur son tronçon suivant.
Cette distinction compte sur une passerelle qui termine un tunnel et transmet en même temps vers un autre. Un paquet peut arriver déchiffré et devoir néanmoins repartir chiffré, et une acceptation écrite uniquement pour le contexte entrant ne dit rien de l’exigence sortante. Si la règle exige cette protection, le jeu de règles devrait l’exprimer plutôt que de supposer que le routage s’en chargera.
Gardez les deux questions dans des règles séparées afin que le lecteur voie laquelle est appliquée. Une seule règle astucieuse qui vérifie les deux est plus difficile à relire et bien plus difficile à déboguer quand l’une de ses moitiés cesse de correspondre. Deux règles avec des compteurs vous indiquent aussi quelle exigence échoue, ce qu’une règle combinée ne fera jamais.
NAT n’est pas une décision de pare-feu
Le trafic vers internet depuis un pool de clients privé a généralement besoin d’une traduction de source pour que la réponse retrouve son chemin. Mais le trafic sélectionné pour une autre politique IPsec sortante doit conserver son exemption requise, et masquer chaque source privée sans réfléchir à la décision de protection suivante casse cela.
Une acceptation à l’intérieur d’une chaîne NAT n’est pas non plus une autorisation générale. NAT et filtrage ont des tâches différentes, et une chaîne NAT n’examine pas chaque paquet ultérieur d’un flux établi, et c’est précisément pourquoi compter sur elle pour la sécurité produit des règles qui semblent fonctionner en test et échouent sous charge.
L’inverse est vrai aussi et surprend : une acceptation quelque part dans une chaîne de base de filtrage ne pèse rien face à un rejet ultérieur dans une autre chaîne applicable. L’ordre et la portée de la chaîne décident du résultat, pas la présence d’une acceptation dans le fichier.
Vérifiez le service interne exact
Testez la destination et le port précis que vous avez autorisés, depuis un vrai client, et confirmez que la réponse revient. Testez ensuite quelque chose que vous n’avez pas autorisé et vérifiez que cela ne passe pas, car une règle trop large réussit le premier test aussi bien qu’une règle correcte. Une requête qui ne quitte jamais le client n’est pas non plus un résultat de pare-feu, alors lisez les compteurs pour voir si le paquet a été évalué. Deux tests et une lecture de compteur vous en diront plus qu’un après-midi entier à lire le jeu de règles.
La référence nftables documente meta ipsec, rt ipsec et les expressions propres à chaque direction, et c’est là qu’il faut vérifier la syntaxe par rapport à votre version installée. Et gardez la portée explicite : ceci est un exemple IPv4, il ne dit rien d’IPv6, et il ne fait pas d’une redirection de port publique arbitraire une partie du service VPN.