SSD Nodes Learn 🎉 VPS dès $5.50/mois
Guides Matt ConnorPar Matt Connor

Configurer firewalld sur un VPS Rocky ou AlmaLinux

Apprenez à ouvrir SSH et les ports web, en fermer un et conserver les règles après redémarrage. Zones, commandes utiles et piège de --permanent expliqués.

Ce qu’est firewalld et pourquoi Rocky et AlmaLinux l’intègrent

firewalld est le gestionnaire de pare-feu installé par défaut sur Rocky Linux, AlmaLinux et les autres reconstructions de Red Hat Enterprise Linux (RHEL). Il n’inspecte pas lui-même les paquets. Il conserve une configuration enregistrée et transforme cette configuration en règles nftables. Une commande, firewall-cmd, permet de la modifier sans mettre le serveur hors ligne.

Si vous savez déjà comment fonctionne ufw sur un VPS Ubuntu, vous connaissez le principe. firewalld ajoute deux notions absentes d’ufw. La première est celle des zones : une stratégie nommée dans laquelle les paquets sont classés. La seconde sépare les règles actives des règles enregistrées. Cette séparation dépend de l’option --permanent et constitue la principale source de confusion avec cet outil.

Toutes les commandes ci-dessous s’exécutent sur votre propre serveur. Testez chaque modification depuis une deuxième machine, car une règle qui semble correcte sur le serveur peut malgré tout être incorrecte depuis Internet.

Ouvrez SSH avant toute autre opération

La plupart des installations Rocky et AlmaLinux ont déjà firewalld installé et actif, et la configuration fournie autorise SSH. Certaines images cloud minimales le suppriment. Vérifiez plutôt que de partir du principe qu’il est présent.

sudo dnf install -y firewalld
sudo systemctl enable --now firewalld
sudo firewall-cmd --state

firewall-cmd --state affiche running. Si le service est arrêté, tout autre appel à firewall-cmd renvoie FirewallD is not running et se termine avec un code différent de zéro. C’est la première chose à vérifier lorsqu’une commande semble ne rien faire.

Lisez maintenant ce qui est actuellement autorisé.

sudo firewall-cmd --list-all

La sortie réelle contient quelques lignes supplémentaires. Voici celles qui nous intéressent :

public (active)
  target: default
  interfaces: eth0
  sources:
  services: cockpit dhcpv6-client ssh
  ports:
  rich rules:

ssh dans la ligne services: explique pourquoi votre session fonctionne encore. S’il est absent, ajoutez-le avant toute autre opération, car démarrer un firewall sans règle SSH met fin à la session et vous empêche de vous reconnecter.

sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload

target: default signifie qu’un paquet qui ne correspond à aucune règle est rejeté avec une réponse ICMP (Internet Control Message Protocol) host-prohibited. Un client qui tente d’atteindre un port fermé reçoit donc No route to host immédiatement. Définir la cible sur DROP rend au contraire le serveur silencieux, et les scanners attendent alors l’expiration du délai.

sudo firewall-cmd --permanent --zone=public --set-target=DROP
sudo firewall-cmd --reload

Mesurez les conséquences avant d’exécuter cette commande : DROP empêche également le serveur de répondre à ping, ce qui interrompt aussi votre propre monitoring.

Pourquoi ma règle a-t-elle disparu ? Le flag --permanent

firewalld conserve deux configurations en parallèle. La configuration runtime correspond à ce que le noyau applique actuellement. La configuration permanente est enregistrée dans /etc/firewalld/zones/public.xml et restaurée après un reload ou un reboot.

Une commande sans --permanent ne modifie que la configuration runtime. Elle prend effet immédiatement, puis disparaît au prochain reload ou boot. Une commande avec --permanent écrit le fichier sans modifier la configuration active. Le port reste donc fermé jusqu’au reload. Aucun de ces comportements n’est un bug. Ils surprennent souvent, car la commande affiche success dans les deux cas.

Écrivez toujours la paire.

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reload

Vous pouvez lire les deux configurations. C’est le moyen le plus rapide de déterminer laquelle des deux erreurs vous avez commise.

sudo firewall-cmd --list-services
sudo firewall-cmd --permanent --list-services

La première commande affiche l’ensemble actif. La seconde affiche l’ensemble enregistré. Si l’ensemble actif contient un service absent de l’ensemble enregistré, la règle disparaîtra au prochain reload. Si l’ensemble enregistré contient un service absent de l’ensemble actif, vous avez oublié le reload. sudo firewall-cmd --runtime-to-permanent copie toute la configuration active dans le fichier enregistré. Cette commande est utile après une session de tests.

--reload conserve l’état du suivi des connexions. Votre session SSH reste donc active. --complete-reload recharge également les modules du noyau et perd cet état. Cela met généralement fin à toutes les connexions ouvertes, y compris la vôtre. Utilisez le reload standard.

Une protection est intégrée. Une règle runtime peut expirer automatiquement.

sudo firewall-cmd --add-service=http --timeout=5m

Cette règle se supprime après cinq minutes. Elle ne peut pas être combinée avec --permanent, et c’est précisément son objectif : tester une modification dont vous n’êtes pas certain. L’ancienne protection reste préférable. Gardez une deuxième session SSH ouverte pendant la modification des règles. Ne la fermez pas tant qu’une nouvelle connexion n’a pas confirmé que les nouvelles règles fonctionnent.

Zones et pourquoi seule la zone par défaut compte sur un VPS

Une zone est un ensemble nommé d’autorisations auquel est associé un niveau de confiance. firewalld place chaque paquet entrant dans une seule zone. Il compare d’abord l’adresse source du paquet à la liste sources: de chaque zone. Si aucune correspondance n’est trouvée, il utilise la zone à laquelle l’interface réseau entrante est associée. Si l’interface n’est associée à aucune zone, le paquet est envoyé vers la zone par défaut.

sudo firewall-cmd --get-default-zone
sudo firewall-cmd --get-active-zones

Sur un VPS doté d’une seule interface réseau, la première réponse est presque toujours public, et c’est la seule zone que vous utiliserez. firewall-cmd sans argument --zone= agit sur la zone par défaut. C’est pourquoi toutes les commandes courtes de ce guide fonctionnent sans préciser de zone.

Voici le problème qui peut vous faire perdre une après-midi. Si l’interface est associée à une autre zone, vos règles sont ajoutées à public alors que le trafic est traité ailleurs. Rien de ce que vous ajoutez n’a donc d’effet, et aucun message ne vous en avertit. --get-active-zones affiche l’association :

public
  interfaces: eth0

Si l’interface apparaît sous un autre nom de zone, écrivez vos règles dans cette zone avec --zone=, ou déplacez l’interface.

sudo firewall-cmd --permanent --zone=public --change-interface=eth0
sudo firewall-cmd --reload

NetworkManager gère les interfaces sur Rocky et AlmaLinux. Il réapplique la zone lorsque la connexion est établie. Définissez-la également à cet endroit pour qu’un redémarrage n’annule pas votre configuration. Relevez le nom de la connexion dans la première commande, car il est rarement identique au nom du périphérique.

sudo nmcli connection show
sudo nmcli connection modify "System eth0" connection.zone public

La correspondance sur la source est prioritaire sur celle de l’interface. C’est ainsi qu’une adresse peut recevoir une policy différente. La zone trusted intégrée autorise tout.

sudo firewall-cmd --permanent --zone=trusted --add-source=203.0.113.10/32
sudo firewall-cmd --reload

Soyez prudent avec cette zone. Elle ouvre tous les ports du serveur à cette adresse, y compris celui de la base de données que vous pensiez privée. Utilisez une rich rule lorsque vous souhaitez autoriser un port précis, et non un hôte entier.

Qu’est-ce qu’un service firewalld ?

Un service est un ensemble nommé de ports, fourni sous la forme d’un fichier XML. --add-service=https ouvre 443/tcp parce que /usr/lib/firewalld/services/https.xml définit ce que signifie https.

sudo firewall-cmd --get-services
sudo firewall-cmd --info-service=https

--info-service affiche les ports correspondant à ce nom :

https
  ports: 443/tcp

Utilisez le nom lorsqu’il existe. Il reste lisible dans --list-all six mois plus tard, et des paquets comme Cockpit installent leur propre fichier de service. Utilisez --add-port pour tout ce qui n’a pas de définition.

Le point à surveiller : le service ssh signifie 22/tcp, et rien d’autre. Si vous avez déplacé SSH vers un autre port en renforçant l’accès SSH au serveur, alors --add-service=ssh n’ouvre pas le port que vous utilisez réellement.

sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload

Sur une réinstallation de RHEL, un second verrou s’applique. SELinux (security-enhanced Linux) associe des labels aux numéros de port, et sshd n’est pas autorisé à se lier à un port qui ne correspond pas à ses labels. Il refuse alors de démarrer et le journal indique error: Bind to port 2222 on 0.0.0.0 failed: Permission denied. Attribuez d’abord le label au port.

sudo dnf install -y policycoreutils-python-utils
sudo semanage port -a -t ssh_port_t -p tcp 2222

Comment voir ce qui est ouvert actuellement ?

sudo firewall-cmd --list-all
sudo firewall-cmd --list-rich-rules
sudo nft list table inet firewalld | head -n 40

Les deux premières commandes indiquent ce que firewalld considère comme ouvert. La troisième lit les règles réellement présentes dans le noyau, dans la table gérée par firewalld. Les résultats doivent correspondre.

Cela ne constitue pas une preuve. Effectuez le test depuis une autre machine :

nc -zv 203.0.113.20 443

N’exécutez pas le test sur le serveur lui-même. firewalld accepte tout ce qui arrive par l’interface loopback. curl http://localhost:8080 réussit donc quelles que soient vos règles. Ce test confirme que le service est actif. Il ne fournit aucune information sur le firewall.

Autoriser un port web

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-services

La dernière commande doit maintenant afficher http https en plus des éléments déjà présents. Si le site ne répond toujours pas, le pare-feu n’est peut-être pas en cause. Une règle autorise un paquet. Un processus doit tout de même être en écoute pour le recevoir.

sudo ss -tlnp

Un socket affiché comme 0.0.0.0:443 ou *:443 accepte les connexions depuis n’importe quelle adresse. Un socket affiché comme 127.0.0.1:443 répond uniquement sur loopback. Aucune règle de pare-feu ne le rendra accessible depuis l’extérieur. Ports et sockets en écoute sous Linux explique cette différence plus en détail.

Comment fermer un port ?

sudo firewall-cmd --permanent --remove-service=http
sudo firewall-cmd --reload

La règle --permanent s’applique également ici, et ses conséquences sont plus importantes dans ce sens. Retirez un service uniquement de la configuration active : le port semble fermé, puis le prochain rechargement ou redémarrage le rouvre à partir du fichier enregistré. Vous ne remarquerez pas cette faille, car le contrôle effectué a réussi.

La suppression d’un élément qui n’existait pas affiche Warning: NOT_ENABLED: http et se termine tout de même avec le code 0. L’ajout du même élément deux fois affiche Warning: ALREADY_ENABLED: http. Ces deux situations sont sans danger. Un nom mal orthographié est différent : Error: INVALID_SERVICE signifie que firewalld ne possède aucune définition portant ce nom et qu’aucune modification n’a été effectuée.

Si votre --list-all affiche cockpit et que vous n’utilisez pas la console web Cockpit sur le port 9090, supprimez-le. Chaque port ouvert correspond à un service que vous devez maintenir à jour avec les correctifs de sécurité.

Limiter un port à une seule adresse source

Les rich rules sont la forme longue, à utiliser lorsqu’un simple nom de service ne permet pas d’exprimer la règle voulue. Limiter SSH à une seule adresse du bureau nécessite deux commandes, et la seconde est souvent oubliée.

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.10/32" service name="ssh" accept'
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --reload

Une zone est un ensemble d’autorisations, pas une liste numérotée dont l’évaluation s’arrête à la première correspondance. La rich rule ajoute une autorisation pour une adresse donnée. Elle ne refuse personne. Tant que ssh figure encore dans la ligne services:, l’ensemble d’Internet peut toujours atteindre le port 22 et la rich rule ne change rien de mesurable. Supprimez l’entrée générale, sinon la règle restrictive ne sert à rien.

Pour un port qui n’a pas de nom de service, indiquez directement le port.

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.10/32" port port="5432" protocol="tcp" accept'

Pour bloquer un réseau bruyant tout en conservant une trace, placez l’élément de journalisation avant l’action, car c’est l’ordre attendu par le langage des rich rules.

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="198.51.100.0/24" log prefix="fw-drop " level="info" limit value="3/m" drop'
sudo firewall-cmd --reload
sudo journalctl -k -g fw-drop

La valeur limit évite qu’un flot de paquets remplisse le journal. Avant de limiter SSH à une seule adresse, vérifiez que cette adresse est stable. Une connexion domestique avec une adresse IP dynamique peut vous empêcher de vous connecter dès qu’elle change. Testez donc au préalable l’accès à la console de votre fournisseur et vérifiez qu’il fonctionne.

commandes ufw et leurs équivalents firewall-cmd

Même objectif, autre outil. Chaque ligne --permanent doit être suivie d’un sudo firewall-cmd --reload, ce qu’une liste de ce type ne peut pas montrer.

  • sudo ufw enable devient sudo systemctl enable --now firewalld
  • sudo ufw disable devient sudo systemctl disable --now firewalld
  • sudo ufw status verbose devient sudo firewall-cmd --list-all
  • sudo ufw allow OpenSSH devient sudo firewall-cmd --permanent --add-service=ssh
  • sudo ufw allow 443/tcp devient sudo firewall-cmd --permanent --add-port=443/tcp
  • sudo ufw delete allow 443/tcp devient sudo firewall-cmd --permanent --remove-port=443/tcp
  • sudo ufw allow from 203.0.113.10 to any port 22 devient la rich rule affichée ci-dessus
  • sudo ufw reload devient sudo firewall-cmd --reload
  • sudo ufw default deny incoming correspond déjà au fonctionnement de la zone public, et --set-target=DROP en est la version silencieuse
  • sudo ufw logging on devient sudo firewall-cmd --set-log-denied=all

Une différence doit être formulée clairement. ufw conserve une liste numérotée et permet d’insérer une règle en position 1. firewalld n’utilise pas de numéros de règle. « Placer cette règle en premier » n’a donc aucun sens ici. Lorsque deux entrées firewalld semblent contradictoires, l’acceptation large l’emporte, car aucune règle de l’ensemble ne refuse le trafic. Vous devez supprimer vous-même l’entrée large.

Pourquoi mon conteneur Docker est-il accessible alors que le firewall semble bloquer les connexions ?

Parce qu’un port publié par un conteneur ne passe jamais par la partie du firewall contrôlée par votre zone. docker run -d -p 8080:80 nginx demande à Docker d’écrire ses propres règles de NAT (network address translation) et de forwarding. Un paquet reçu sur 8080 est réécrit, puis acheminé vers le conteneur. Il est donc forwardé et non remis à l’hôte. Les lignes services: et ports: de votre zone contrôlent les paquets remis à l’hôte. Les règles de Docker contrôlent le chemin de forwarding et les acceptent.

Le résultat est un serveur sur lequel sudo firewall-cmd --list-all n’affiche aucun port 8080, alors que nc -zv 203.0.113.20 8080, exécuté depuis une autre machine, se connecte quand même. Vérifiez ce que Docker a installé :

sudo iptables -t nat -L DOCKER -n

La correction se trouve dans le flag de publication. Liez le port à loopback et placez un reverse proxy devant le conteneur.

docker run -d -p 127.0.0.1:8080:80 nginx

Le conteneur répond maintenant à curl http://127.0.0.1:8080 sur le serveur, mais plus à aucune connexion externe. Les utilisateurs d’Ubuntu rencontrent le même problème, décrit dans pourquoi les conteneurs Docker publient directement leurs ports en contournant ufw. Podman rootful, inclus par Rocky et AlmaLinux dans les dépôts de base, publie les ports avec la même approche NAT. Testez donc depuis une autre machine au lieu de vous fier à la liste de la zone.

Rendre la configuration persistante après un redémarrage et comprendre les erreurs courantes

sudo systemctl is-enabled firewalld
sudo systemctl status firewalld

enabled et active (running) sont les commandes attendues. Un firewall qui fonctionne, mais qui n’est pas activé, vous protège jusqu’au premier redémarrage. Cette vérification doit figurer dans la liste des contrôles à effectuer pendant les dix premières minutes sur un nouveau VPS, avec les clés SSH et les mises à jour.

Les commandes nftables brutes et firewalld ne sont pas compatibles. firewalld gère une table appelée inet firewalld. sudo nft flush ruleset la supprime. Le serveur accepte alors tout le trafic, tandis que firewall-cmd --list-all affiche toujours la configuration prévue, car firewalld indique ce qu’il pense gérer et non ce que contient réellement le noyau. sudo firewall-cmd --reload réinstalle les règles. Écrivez les règles avec firewall-cmd pour qu’elles soient restaurées après un reload.

Deux gestionnaires de firewall sur un même serveur. Installer ufw ou iptables-services en plus de firewalld donne deux programmes qui écrivent des règles sans connaître les modifications de l’autre. Le résultat dépend alors du service démarré en dernier. Choisissez-en un seul. Sur Rocky et AlmaLinux, firewalld est celui qui bénéficie du support de la distribution.

Le firewall du fournisseur devant le serveur. De nombreux panneaux de gestion VPS proposent un firewall réseau distinct. Si --list-all indique qu’un port est ouvert, mais qu’une connexion externe échoue toujours, vérifiez le panneau avant de modifier le serveur. L’inverse est également possible : une règle ouverte dans le panneau ne sert à rien si firewalld rejette le paquet.

Exécuter firewall-cmd sans sudo. Chaque modification nécessite les droits root. Sans ces droits, la demande est refusée lors du contrôle d’autorisation et rien n’est modifié. À première vue, cela peut donner l’impression que la commande a été ignorée.

Six commandes couvrent la plupart des opérations courantes : --list-all pour consulter l’état, --permanent --add-service ou --add-port pour ouvrir un accès, --permanent --remove-service pour le fermer, --reload pour appliquer le fichier enregistré, et --runtime-to-permanent après une série de tests. La zone est public, l’option est --permanent, et la seule vérification fiable s’effectue depuis une autre machine.

FAQ

Pourquoi ma règle firewalld a-t-elle disparu après un redémarrage ?

La règle a été ajoutée uniquement à la configuration runtime. sudo firewall-cmd --add-service=http s’applique immédiatement et est supprimée au prochain rechargement ou démarrage, car la configuration enregistrée dans /etc/firewalld/zones/public.xml n’a jamais été modifiée. Ajoutez --permanent, puis exécutez sudo firewall-cmd --reload. Pour conserver les règles que vous avez déjà ajoutées manuellement, exécutez sudo firewall-cmd --runtime-to-permanent. Cette commande copie l’ensemble actif dans le fichier enregistré.

Pourquoi rien ne change-t-il après l’ajout d’une règle avec --permanent ?

Parce que --permanent écrit le fichier sans modifier le firewall en cours d’exécution. Le port reste fermé jusqu’à ce que sudo firewall-cmd --reload charge la configuration enregistrée dans le kernel. Comparez sudo firewall-cmd --list-services et sudo firewall-cmd --permanent --list-services : si la liste enregistrée contient une entrée absente de la liste active, il manque le rechargement.

Dois-je utiliser --add-service ou --add-port ?

Utilisez --add-service lorsqu’un nom existe pour le service que vous exécutez. Cela indique clairement l’intention, et sudo firewall-cmd --info-service=https affiche exactement les ports couverts par ce nom. Utilisez --add-port lorsqu’aucun nom ne définit votre service ou lorsqu’il écoute sur un port non standard. Le service ssh autorise uniquement 22/tcp. Si SSH utilise le port 2222, vous devez donc ajouter --add-port=2222/tcp ainsi qu’un label SELinux pour ce port.

Pourquoi mon conteneur Docker est-il accessible alors que firewall-cmd indique que le port est fermé ?

Un port publié est réécrit par les propres règles NAT de Docker, puis redirigé vers le conteneur. Le paquet n’est donc jamais remis à l’hôte, et les listes de services et de ports d’une zone ne couvrent que les paquets remis à l’hôte. Le conteneur répond depuis Internet alors que --list-all n’affiche rien. Publiez plutôt le port sur loopback avec docker run -d -p 127.0.0.1:8080:80 nginx, puis placez un reverse proxy devant le conteneur.

Puis-je installer ufw sur Rocky Linux à la place de firewalld ?

Deux gestionnaires de firewall sur un même serveur écrivent des règles sans connaître les modifications de l’autre. L’ensemble de règles conservé dépend donc du dernier service démarré. firewalld est l’outil pris en charge sur Rocky Linux et AlmaLinux. Il est déjà installé et utilise le même backend nftables que celui qu’utiliserait ufw. Il suffit de connaître la zone par défaut et l’option --permanent pour maîtriser l’outil.