Protection d’un serveur web : d’IPtables à NFtables
Pour ce premier contexte, je vous propose de passer d'IPtables à nftables. Je vais, en plus de vous exposer le contexte réel, vous mettre les règles IPtables correspondantes. Cela vous permettra de voir comment passer d'un mode de gestion à l'autre.
Note : Et oui, rappelez-vous, nftables n'est qu'une interface de gestion pour le pare-feu Linux, qui est NetFilter. IPtables est simplement "l'ancêtre" de nftables.
Si vous ne connaissiez pas spécialement IPtables, sachez que vous pourrez très bien faire l'exercice sans prendre pour exemple les règles IPtables 😉
I. Description du contexte
Je dispose donc d'un serveur web hébergé chez un hébergeur dans un grand datacenter français. Mon site reçoit plusieurs dizaines de milliers de visiteurs par jour et aussi quelques pirates qui tentent leur chance pour contourner ma sécurité. J'ai donc mis en place des règles IPtables correspondant aux flux suivants :
- mon service web est accessible en HTTP, mais aussi en HTTPS;
- mes sites web ont besoin, pour contacter d'autres services web, d'effectuer des requêtes en HTTPS seulement (en tant que client), attention au DNS ;));
- je gère mon service web en SSH sur le port TCP/1022;
- mon serveur mail sera amené à questionner des serveurs NTP externes à et envoyer des mails sur des ports TCP/587;
- je souhaite également mettre en place un peu de sécurité en comptant les paquets TCP de types NULL ou XMAS, cela dénote un comportement anormal. Nous allons également les bloquer;
- tout ce qui n'est pas explicitement autorisé sera refusé;
- mon serveur pourra pinguer, mais ne pas être pingué.
Voici mes règles IPtables, pour ceux qui connaissent, vous pourrez vous en servir pour faire la comparaison au final entre le résultat IPtables et nftables :
# On autorise la sortie et l'entrée sur le port 80 iptables -A INPUT -m tcp -p tcp --dport 80 -j ACCEPT # On autorise la sortie du protocole HTTPS (443) iptables -A INPUT -m tcp -p tcp --dport 443 -j ACCEPT iptables -A OUTPUT -m tcp -p tcp --dport 443 -j ACCEPT # On autorise la sortie et l'entrée sur le port 1022 (SSH) iptables -t filter -A INPUT -p tcp --dport 1022 -j ACCEPT iptables -t filter -A OUTPUT -p tcp --dport 1022 -j ACCEPT # On autorise la sortie et l'entrée sur le port 53 (DNS) iptables -t filter -A OUTPUT -p udp --dport 53 -j ACCEPT iptables -t filter -A INPUT -p udp --dport 53 -j ACCEPT # On autorise la sortie des requêtes vers le port 123 (NTP) iptables -t filter -A OUTPUT -p udp --dport 123 -j ACCEPT # On autorise la sortie des mails vers le port 587 iptables -t filter -A OUTPUT -p tcp --dport 587 -j ACCEPT # Refus des TCP XMAS pour éviter les scans de ports iptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP # Refus des TCP NULL pour éviter les scans de ports iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP # On refuse tout le reste iptables -A OUTPUT -j DROP iptables -A INPUT -j DROP
II. Attention, voici le corrigé !
Nous allons maintenant passer au corrigé. J'espère que vous n'avez pas triché et que vous avez réussi à faire le plus gros de l'exercice. Pas d'inquiétude, vous pourrez retourner voir le cours en cas de besoin !
table ip filter { chain input { type filter hook input; ct state established,related accept # handle 2 tcp dport http accept # handle 5 tcp dport https accept # handle 6 tcp dport 1022 accept # handle 7 tcp flags & (fin | syn | rst | psh | ack | urg) > urg counter packets 0 bytes 0 # handle 8 tcp flags & (fin | syn | rst | psh | ack | urg) < fin counter packets 0 bytes 0 # handle 9 icmp type echo-reply accept # handle 10 drop # handle 11 } chain output { type filter hook output; ct state established,related accept # handle 13 tcp dport https accept # handle 14 udp dport domain accept # handle 15 udp dport ntp accept # handle 16 tcp dport 587 accept # handle 17 icmp type echo-request accept # handle 18 drop # handle 19 } }
Je vais maintenant détailler quelque peu ma correction. Pour cela, nous nous repèrerons grâce aux numéros de règles (handle) que j'ai, rappelez-vous, fait apparaitre avec l'option -a de la commande nft.
Dans un premier temps, je crée ma table filter avec une chaine input liée au hook du même nom, même chose pour output.
Pour la chaine input, je commence par spécifier mon suivi de connexion (handle 2). Toutes les connexions entrantes qui sont des connexions déjà établies ou initiées par moi-même (le serveur web, en tant que client) seront acceptées.
Aux handles 5, 6 et 7, j'ouvre les ports de mon serveur qui hébergent des applications utiles aux clients, c’est-à-dire l'HTTP (port TCP/80), l'HTTPS (port TCP/443) et le SSH (port TCP/1022).
Ensuite, je vais compter, puis bloquer les paquets TCP Null et XMS pour des raisons que j'ai exposées dans le cours via les règles aux handles 8 et 9.
Enfin, dans la règle handle 10, je spécifie que les paquets ICMP de type echo-reply (seule les réponses donc) seront acceptés. Je termine par refuser tout ce qui n'a pas été spécifiquement accepté dans les règles précédentes.
Pour ma chaine output, je commence également par mettre en place mon conntrack (suivi des connexions). Avec les règles 15 et 16, j'autorise les paquets UDP à sortir sur les ports 53 (DNS) et 123 (NTP).
J'espère que ce cas pratique vous a plu ! Il donne en effet l'occasion de mettre en application ce que vous avez appris dans un contexte réel et la transition des règles d'IPtables vers nftables. Dans le prochain exercice, nous passerons dans le contexte de la mise en application du NAT avec nftables.