Installer UFW sur un serveur ArchLinux

A brick wall
Photo by Viktor Forgacs / Unsplash

Dans ce bref billet, je vais vous expliquer comment je suis passé du firewall iptables sur l'ArchLinux de mon serveur dédié OVH à ufw.

Pourquoi ce changement ?

Mon serveur dédié OVH possède iptables en pare-feu. Je ne m'en étais jamais trop occupé jusqu'à présent : mes services sont lancés avec Docker et j'expose leur ports. Et hop, tout fonctionne : la magie de l'informatique !

Je suis cependant un train de mettre en place un service Portainer pour contrôler à la fois les conteneurs lancés sur mon NAS à la maison (là où tourne Portainer) et sur mon serveur OVH dans le nuage. Pour ce faire, Portainer permet de déployer un service appelé portainer-agent sur le serveur distant.

Pour sécuriser le tout, il est possible de faire de l'authentification mutuelle, mais je ne souhaite pas en arriver là. En effet, mon serveur OVH dans le cloud est relié par un tunnel OpenVPN avec mon NAS : ils sont donc visibles au sein d'un même réseau considéré comme local.

Je souhaite donc que mon conteneur portainer-agent soit accessible uniquement depuis l'IP "réseau local VPN" du NAS et non par l'extérieur. Mais pour lancer le conteneur de cet agent, je dois exposer le port 9001, qui sera alors accessible à tout le monde, y compris du réseau public extérieur.

Problème : comment je restreins cet accès réseau ? Le moyen le plus simple est de le filtrer directement au niveau du firewall. Mais je suis une bille complète en iptables.

Direction UFW (Uncomplicated FireWall)

ufw est un firewall plus simple à utiliser que iptables. Il permet d'aller dans le détail, certes, mais l'ajout de règle peut-être très simple pour les utilisateurs lambda comme moi.

Installer ufw

Vous pouvez normalement le trouver dans les repositories de votre distribution préférée. Pour Arch, une petite install via pacman suffit largement.

# pacman -S ufw

Switch de iptables à ufw

ufw ne pourra pas réaliser la configuration si c'est iptables qui est toujours au statut enabled dans systemd.

Il faut alors désactiver iptables, puis lancer et activer ufw.

⚠️
Attention : je vous conseille d'ajouter une règle dans ufw autorisant la connexion SSH avant de le lancer. Sinon, vous ne pourrez plus vous connecter à distance !
# systemctl disable iptables
# ufw limit SSH
# systemctl start ufw
# systemctl enable ufw
# ufw enable

Attention de bien comprendre qu'on ne désinstalle pas iptables. ufw va permettre de le piloter plus simplement, mais comme disent certains :

iptables, ce sont les fondations de ton firewall, le cordon bleu de ton tacos. - @Ormaz

Manipuler les règles et voir les règles en cours

Pour ajouter des règles, c'est assez simple avec ufw allow RULE ou ufw deny RULE. Le mot-clef limit permet d'autoriser mais en limitant l'accès. C'est utile pour SSH par exemple, où le traffic sera interdit s'il y a plus de 6 connexions en moins de 30 secondes venant de la même IP.

Pour voir les règles, un petit coup de ufw status et le tour est joué. Vous pouvez ajouter les paramètres verbose et numbered pour avoir le détail de l'action (entrant ou sortant pour le flux) et le numéro de la règle.

Régler mon problème d'accès

Je peux donc maintenant ajouter une règle qui me permet de n'autoriser qu'une connexion depuis une IP particulière sur le port exposé du conteneur de l'agent portainer.

# ufw allow proto tcp from 192.Z.Y.X to any port 9001

Et voilà ! Le problème est réglé !

Un petit aperçu des règles que j'ai sur mon serveur :

# ufw status verbose
Status: active
Logging: off
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp (SSH)               LIMIT IN    Anywhere
80                         ALLOW IN    Anywhere
443                        ALLOW IN    Anywhere
[...]
9001/tcp                   ALLOW IN    192.Z.Y.Z
[...]

WTF : pourquoi on peut toujours y accéder depuis n'importe où ?

Quand on regarde, le problème n'est pas réglé ! On peut quand même accéder au port 9001 du conteneur par le réseau public !

En fait, Docker écrit ses propres règles iptables et elles sont appliquées en-dehors des vôtres !

Heureusement, nous ne sommes pas tout seul sur Internet à avoir le problème et il y a une solution assez élégante trouvable ici :

GitHub - chaifeng/ufw-docker: To fix the Docker and UFW security flaw without disabling iptables
To fix the Docker and UFW security flaw without disabling iptables - GitHub - chaifeng/ufw-docker: To fix the Docker and UFW security flaw without disabling iptables

Il faut modifier le fichier /etc/ufw/after.rules et y ajouter le bloc décrit dans le README du dépôt GitHub. Ce bloc permet de faire communiquer entre eux les réseaux internes de Docker mais d'empêcher le réseau public d'accéder aux ports exposés par les conteneurs.

Un petit redémarrage du service pour appliquer le tout avec un systemctl restart ufw et hop, cette fois, c'est parfait !

Accéder à des conteneurs depuis le réseau public

Avec cette configuration, votre réseau docker - qui est un réseau privé - n'est pas accessible par un réseau public (le trafic Internet).

Pour rendre un service d'un conteneur Docker accessible depuis l'extérieur, il vous fait maintenant ajouter une route vers le port du conteneur. Par exemple, pour un conteneur Docker lancé avec le paramètre -p 6060:6767, pour le rendre accessible depuis l'extérieur, vous devez réaliser les actions suivantes :

  1. ouvrir le port 6060 sur ufw pour que le port soit accessible depuis Internet
# ufw allow 6060/tcp

2. permettre le routage interne du flux vers le port 6767 du conteneur. Il vous faut l'IP du conteneur, accessible via docker inspect.

# ufw route allow proto tcp from any to 172.X.Y.Z port 6767

3. redémarrer ufw avec un petit ufw enable.

Et hop, le tour est joué !

Je vous parlerai de Portainer une autre fois...

Documentations de référence

Mastodon