Filtrer le trafic autorisé : pare-feu

Pour filtrer les accès au strict minimum sur les machines de Picasoft, on utilise un firewall. Pour cela on utilise ufw qui est une interface pour netfilter (la partie du noyau qui s’occupe entre autres de filtrer les paquets) assez simple à utiliser.

Important:

Docker passe au travers des règles firewall configurées via ufw. Tout les services Docker qui écoutent directement sur un port de la machine (Traefik par exemple) sont donc automatiquement exposés sur Internet, sans protection du firewall. Pour filtrer des paquets en direction d’un conteneur, il faut utiliser iptables, une autre interface pour netfilter avec une syntaxe moins simple. ufw et iptables, utilisés tels que présentés dans cette page, ne devraient pas interagir entre eux.

Note:

Sur Debian 11, iptables est disponibles en deux versions : une moderne (nft) et une ancienne (legacy). La version moderne n’est plus compatible avec ufw, qui construit des règles iptables.

Beaucoup de gens critiquent ufw, et à raison. En revanche, pour ce qu’on en fait, et pour simplifier l’administration système et l’intégration des nouvelles personnes dans l’équipe technique, on décide de garder un système de pare-feu simple et homogène. Il faut donc utiliser l’ancienne version.

Exécuter la commande :

update-alternatives --set iptables /usr/sbin/iptables-legacy

sur les machines qui font tourner Debian 11 pour utiliser l’ancienne version d’iptables.

On commence par l’installer:

snippet.bash
apt-get install ufw

Sur la plupart des machines, l’idée va être d’autoriser uniquement les accès aux ports SSH. On pourra ouvrir le port du socket Docker si nécessaire. Le reste est à ajouter en fonction des besoins.

Les règles de base seront donc :

snippet.bash
# Autorisation du SSH
ufw allow in 22
# Autorisation pour le web
ufw allow in 80
ufw allow in 443
# On ouvre la socket Docker sur le réseau
ufw allow in 2376

Question:

Heu, c’est Traefik qui gère les services web, donc Docker, et tu nous as dit que ça bypassait ufw… Ça sert à quoi d’autoriser les ports 80 et 443 ?

Pour un cas très spécifique : lorsque vous essayez d’accéder à un conteneur depuis un autre conteneur situé sur la même machine avec son URL publique. Si le conteneur A (a.picasoft.net) et B (b.picasoft.net) tournent sur la même machine, alors sans ces règles :

snippet.bash
docker exec -it A bash
$ curl b.picasoft.net

sera bloqué. La magie de Docker et d’iptables

Sur la plupart des machines, l’idée va être d’autoriser uniquement les accès aux ports SSH, DNS et Proxmox. On pourra ouvrir d’autres ports si nécessaires, mais ça ne devrait pas arriver trop souvent, les services tournant sur les machines virtuelles.

Les règles de base seront donc :

snippet.bash
# Autorisation du SSH
ufw allow in 22
# Autorisation pour DNS
ufw allow in 53
# Autorisation pour Let's Encrypt (certificats Proxmox)
ufw allow in 80
# Autorisation du GUI Proxmox
ufw allow in 8006

Enfin on définit une politique d’accès par défaut (fermé en entrée, ouvert en sortie) et on active ufw.

snippet.bash
ufw default deny
ufw default allow outgoing
ufw enable

La commande ufw status liste les règles activées sur le système. Exemple :

Status: active

To                         Action      From
--                         ------      ----
9100/tcp                   ALLOW       91.224.148.61             
22                         ALLOW       Anywhere                  
80                         ALLOW       Anywhere                  
443                        ALLOW       Anywhere                  
2376                       ALLOW       Anywhere                  
64738                      ALLOW       Anywhere                  
9323/tcp                   ALLOW       91.224.148.61             
9100/tcp                   ALLOW       2a03:7220:8080:3d00::1    
22 (v6)                    ALLOW       Anywhere (v6)             
80 (v6)                    ALLOW       Anywhere (v6)             
443 (v6)                   ALLOW       Anywhere (v6)             
2376 (v6)                  ALLOW       Anywhere (v6)             
64738 (v6)                 ALLOW       Anywhere (v6)             
9323/tcp                   ALLOW       2a03:7220:8080:3d00::1

Attention:

Un status: inactive indique que le pare-feu n’est pas lancé.

Pour supprimer une règle, on récupère son “numéro”, avec la commande :

ufw status numbered

Puis on peut la supprimer :

ufw delete <number>

Attention:

La plupart des règles sont en IPv4 et en IPv6, il faut faire deux fois l’opération, en commençant par le numéro le plus haut pour éviter les erreurs.

fail2ban s’occupe déjà de bannir les adresses IP qui essayent de se connecter trop de fois en SSH. Mais l’analyse de logs est compliquée, et parfois certaines IP vont consulter de manière répétée des URL inexistantes sur nos services, essayer de forcer les connexions… Difficile de déterminer l’origine de ces comportements, mais lorsqu’ils sont répétés et viennent de la même IP, il peut être pertinent de bloquer les IP.

Comme on vient de le voir, Docker est « prioritaire » sur les règles ufw, il faut donc utiliser la chaîne spéciale DOCKER-USER pour ajouter nos bans IP. Les règles de cette chaîne sont évaluées avant tout forwarding vers les réseaux privés gérés par Docker.

Important:

Avant de bloquer une IP, il faut faire bien attention : les IP des VPS peuvent être ré-attribuées, et les spammeurs changent souvent de serveur. On essayera donc de bloquer des IP « stables », après plusieurs jours où on constate le même comportement venant des mêmes IP, et on pourra essayer de les débloquer après quelques temps.

snippet.bash
sudo iptables -I DOCKER-USER -i eth0 -s <IP> -j DROP
  • -I permet d’insérer la règle au début de la chaîne (car elles sont évaluées dans l’ordre, il faut que le DROP soit réalisé en premier)
  • -i spécifie l’interface d’entrée (normalement eth0) sur toutes nos machines virtuelles
  • -s spécifie l’IP source
  • -j spécifie l’action à effectuer : ici, jeter le paquet.

On liste les règles de la chaîne DOCKER-USER avec leur numéro :

snippet.bash
sudo iptables -L DOCKER-USER --line-numbers 
Chain DOCKER-USER (1 references)
num  target     prot opt source               destination         
1    DROP       all  --  192.154.2.3          anywhere            
2    RETURN     all  --  anywhere             anywhere

On veut supprimer la première règle, alors :

snippet.bash
sudo iptables -D DOCKER-USER 1
snippet.bash
sudo iptables -L DOCKER-USER
Chain DOCKER-USER (1 references)
target     prot opt source               destination         
DROP       all  --  XXX.XXX.X.X          anywhere            
RETURN     all  --  anywhere             anywhere

On retrouve bien notre règle insérée en premier.

En revanche, les règles iptables ne sont pas persistantes. Un paquet Debian permet de les sauvegarder et de les repeupler au démarrage de la machine.

snippet.bash
sudo apt install iptables-persistent

Ce paquet installe un service netfilter-persistent qui charge les fichiers /etc/iptables/rules.v4 et /etc/iptables/rules.v6 dans iptables au démarrage de la machine. On peut vérifier qu’il tourne bien avec

snippet.bash
systemctl status netfilter-persistent

Important:

Chaque changement de règle doit s’accompagner de ces commandes pour que les règles soient chargées au démarrage :

snippet.bash
sudo iptables-save | sudo tee /etc/iptables/rules.v4
sudo ip6tables-save | sudo tee /etc/iptables/rules.v6

  • technique/adminsys/secu/firewall.txt
  • de qduchemi