A netfilter synproxy intercepts new TCP connections and handles the initial 3-way handshake using syncookies instead of conntrack to establish the connection. Running synproxy on a listening server port thus prevents a SYN flood attack on that port from consuming limited conntrack resources.
Ça évite également d'envoyer des messages TCP SYN ACK à répétition à un interlocuteur possiblement usurpé.
Les cookies TCP laissent passer une partie du trafic pourri (mais je n'ai pas affiné les paramètres noyau), Netfilter ne laisse rien passer.
Sur un système Debian GNU/Linux, les cookies et timestamps TCP sont activés par défaut.
Il ne reste plus qu'à activer le mode strict du suivi des connexions TCP au démarrage. (Par défaut, ce suivi est souple : les connexions existantes sont automatiquement ajoutées à la table des états. Donc un émetteur qui ne terminerait pas la poignée de main TCP pourrait quand même créer un état, pile ce qu'on veut éviter.) Il s'agit du paramètre noyau /proc/sys/net/netfilter/nf_conntrack_tcp_loose (0 = strict ; 1 = loose = souple).
Au démarrage, ce paramètre n'existe pas. Il est créé par un module noyau. Vu son nom, on devine que c'est le module nf_conntrack. On vérifie avec lsmod : quand on crée une règle avec une action synproxy en l'absence de toute autre règle, le module nft_synproxy est chargé et entraîne celui du module nf_conntrack (lsmod : « nf_conntrack 204800 3 nft_ct,nft_synproxy,nf_synproxy_core »).
Lors d'un démarrage, il nous faut donc charger un module noyau puis changer l'un des paramètres du noyau (pas du module, donc modprobe nf_conntrack nf_conntrack_tcp_loose=0 ne fonctionnera pas). Possible race condition, donc (si le paramétrage se déclenche avant le chargement du noyau, ça ne le fera pas). Heureusement, par défaut, systemd-sysctl est configuré pour démarrer après systemd-modules-load (pour vérifier : systemctl list-dependencies --after systemd-sysctl | grep modules-load).
Du coup, au travail :
echo 'nft_synproxy' > /etc/modules-load.d/nft_synproxy.conf
echo 'net.netfilter.nf_conntrack_tcp_loose = 0' > /etc/sysctl.d/98-synproxy.conf
Pour le reste, on suit le tutoriel du wiki officiel nftables pointé par ce shaarli.
Contrairement à ce qu'on peut lire sur le web, et comme l'expose le wiki nftables, il ne faut pas mettre le synproxy devant l'ensemble des ports TCP, mais uniquement devant les ports en écoute. Sinon, il sera impossible d'établir des connexions sortantes.
Comme l'expose succinctement le wiki nftables, l'action synproxy vaut acceptation de la connexion entrante si la poignée de main TCP est achevée. Il n'y a donc pas besoin d'une règle d'acceptation en sus. Si je reprends l'exemple du wiki nftables qui travaille sur les connexions au port 8888, il n'y a pas besoin, dans « input », d'une règle additionnelle de la forme « tcp dport 8888 accept ».
Si l'on veut être pointilleux, la règle prerouting « tcp dport 8888 tcp flags syn notrack » proposée par le wiki nftables est trop permissive : elle sera déclenchée par tous les messages TCP dont au moins le drapeau SYN est activé. Dit autrement : un message SYN FIN, par exemple, sera aussi accepté, alors qu'il est invalide, au sens de la norme TCP, pour initier une connexion TCP.
Pour ne déclencher que sur les paquets qui ont uniquement le drapeau SYN : « tcp dport 8888 tcp flags & (urg|ack|psh|rst|syn|fin) == (syn) notrack ».
Explications :
nft describe tcp flags. Notons que les constantes « ALL » et « NONE » d'iptables n'existent plus et que les autres s'écrivent désormais en minuscule ;
Si l'on filtre aussi les connexions sortantes (dans « output »), la règle usuelle, « ct state established,related counter accept », ne laissera pas passer le message SYN ACK de la poignée de main, donc les connexions entrantes ne seront pas établies.
Il faut ajouter une règle supplémentaire. Pour l'exemple du wiki nftables, qui ouvre le port TCP 8888, cette règle est : tcp sport 8888 ct state invalid,untracked tcp flags & (fin|syn|rst|psh|ack|urg) == (syn|ack) accept. Le ct state invalid,untracked est superfétatoire puisque les connexions suivies et relatives auront été capturées en amont par la règle usuelle, donc, forcément, il ne reste que les connexions invalides ou non-suivies.