====== Installer et configurer le parefeu ======
* À partir de Debian Buster (v10) :
* Le parefeu traditionnel //Iptables// est remplacé par //Nftables//.
* //Firewalld// est un outil de gestion du parefeu (une surcouche) qui sait utiliser //Nftables//.
* Commandes réseaux utiles :
* ''netstat -anpt | grep LISTEN'' ou ''ss -lntu'' : pour voir les ports TCP ouverts sur la machine
* '' ss -lnue '' : pour voir les ports UDP ouverts sur la machine.
* ''ip link show'' : pour trouver les noms des interfaces réseaux de la machine (ex. : eth0, eth1, ...).
===== Installer et configurer Firewalld =====
* Ressources :
* [[https://computingforgeeks.com/how-to-install-and-configure-firewalld-on-debian/|How To Install and Configure Firewalld on Debian 10 (Buster)]]
* [[https://www.it-connect.fr/centos-7-utilisation-et-configuration-de-firewalld/#III_Le_service_firewalld|CentOS 7 : Utilisation et configuration de firewalld]]
* [[https://www.linuxjournal.com/content/understanding-firewalld-multi-zone-configurations|Understanding Firewalld in Multi-Zone Configurations]]
* Installer le paquet : ''apt install firewalld''
* Configurer : ''vi /etc/firewalld/firewalld.conf''
* **ATTENTION** : au 2021-03-26, le support de Nftables n'est pas pris en compte dans Docker. Il est donc conseillé de maintenir l'utilisation d'''iptables'' sur le système et donc de l'utiliser avec Firewalld. Voir [[serveurs:installation:parefeu#problemefirewalld_nftables_et_docker|les explications détaillées]].
* Modifier les propriétés suivantes :
# Pour corriger le bug : https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=914694
IndividualCalls=yes
# Forcer l'utilisation de Nftables quand il sera supporté par Docker avec la valeur suivante :
# FirewallBackend=nftables
# En attendant le support de Nftables par Docker nous utilisons iptables :
FirewallBackend=iptables
* Il est aussi nécessaire de mettre à jour l'alternative ''iptables'' avec la commande : '' update-alternatives --config iptables '' : choisir le mode //iptables-nft//.
* **ATTENTION** : avant d'activer le pare-feu veiller à avoir autoriser l'accès SSH sur le nouveau port. Pour cela configurer la //zone public// comme indiqué ci-dessous dans la section "Configuration des zones".
* En cas de problème de connexion SSH suite à l'activation du pare-feu, il est possible de se connecter au serveur via la console VNC disponible sur le Manager d'Ovh au niveau des instances ou via l'interface Open Stack Horizon (aussi accessible depuis le Manager d'OVH).
* Démarrer le pare-feu si vous avez configurer au préalable la //zone public// : ''systemctl start firewalld''
* Activer automatiquement au démarrage de la machine le parefeu : ''systemctl enable firewalld''
* Afficher le status du service //Firewalld// : ''systemctl status firewalld''
===== Quelques commandes Firewalld =====
* Afficher la version de //Firewalld// : '' firewall-cmd --version ''
* Vérifier l'état du parefeu : '' firewall-cmd --state ''
* Voir toutes les zones actives : '' firewall-cmd --get-active-zones ''
* Voir toutes les règles actives d'une zone particulière : '' firewall-cmd --zone= --list-all ''
* Lister toutes les règles actives de la zone par défaut : '' firewall-cmd --list-all ''
* Afficher la zone par défaut : '' firewall-cmd --get-default-zone ''
* Afficher les zones actives (= liées à une interface) : '' firewall-cmd --get-active-zones ''
* Activer une zone en l'associant à une interface : '' firewall-cmd --zone= --change-interface= ''
* Voir la zone d'une interface : '' firewall-cmd --get-zone-of-interface= ''
* Lister les services activables/désactivables : '' firewall-cmd --get-services ''
* Activer un service de manière permanente dans une zone : '' firewall-cmd --permanent --zone="" --add-service="" ''
* Pour ajouter/supprimer un //port// ou //service// utiliser les options du même nom avec les prévixes : '' --add- '' ou '' --remove- ''
* Recharger les règles sur les zones (celles ajoutées avec '' --permanent '' sont sauver, les autres sont annulées) : '' firewall-cmd --reload ''
===== Configuration des zones =====
==== Zone "public" ====
Activer les services suivant pour la zone //public// :
* Au préalable, vous pouvez vous assurer que //**ens3**// est bien associé à **l'IPv4 public** du serveur à l'aide de la commande : '' ip a ''
* Commandes //Firewalld// à exécuter pour la //zone public// :
firewall-cmd --set-default-zone=public
firewall-cmd --zone=public --permanent --change-interface=ens3
#+-------------------------------------------------------------------+
# Spécifique web-srv
firewall-cmd --zone=public --permanent --add-port=/tcp
firewall-cmd --zone=public --permanent --add-service={http,https}
firewall-cmd --zone=public --permanent --remove-service={dhcpv6-client,ssh}
# 2020-08-27 - Activer "masquerade" si vous obtenez un "502 Bad Gateway" par le Nginx des Dockers
firewall-cmd --zone=public --permanent --add-masquerade
#+-------------------------------------------------------------------+
# Spécifique db-srv
firewall-cmd --zone=public --permanent --add-port=/tcp
firewall-cmd --zone=public --permanent --remove-service={dhcpv6-client,http,https,ssh}
#+-------------------------------------------------------------------+
# Spécifique bkp-srv
firewall-cmd --zone=public --permanent --add-port=/tcp
firewall-cmd --zone=public --permanent --add-service={http,https}
firewall-cmd --zone=public --permanent --remove-service={dhcpv6-client,ssh}
# 2020-08-27 - Activer "masquerade" si vous obtenez un "502 Bad Gateway" par le Nginx des Dockers
firewall-cmd --zone=public --permanent --add-masquerade
#+-------------------------------------------------------------------+
firewall-cmd --reload
# Afficher le résultat :
firewall-cmd --info-zone=public
==== Zone "internal" ====
Activer les services suivant pour la zone //internal// (VPN entre les 2 serveurs) :
* Au préalable, vous pouvez vous assurer que //**ens7**// est bien associé à **l'IPv4 du VPN (10.0.1.xxx)** du serveur à l'aide de la commande : '' ip a ''
* Commandes //Firewalld// à exécuter pour la //zone internal// :
firewall-cmd --zone=internal --permanent --change-interface=ens7
firewall-cmd --zone=internal --permanent --remove-service={dhcpv6-client,mdns,ssh,samba-client}
#+-------------------------------------------------------------------+
# Spécifique web-srv
firewall-cmd --zone=internal --permanent --add-port=/tcp
# InfluxDb (si encore présent sur web-srv, normalement devrait être basculer sur bkp-srv)
# firewall-cmd --zone=internal --permanent --add-port=8086/tcp
# firewall-cmd --zone=internal --permanent --add-port=8089/udp
# Docker API
firewall-cmd --zone=internal --permanent --add-port=2376/tcp
#+-------------------------------------------------------------------+
# Spécifique db-srv
firewall-cmd --zone=internal --permanent --add-port=/tcp
# Postgresql
firewall-cmd --zone=internal --permanent --add-service=postgresql
# Docker API
firewall-cmd --zone=internal --permanent --add-port=2376/tcp
#+-------------------------------------------------------------------+
# Spécifique bkp-srv
firewall-cmd --zone=internal --permanent --add-port=/tcp
# InfluxDb
firewall-cmd --zone=internal --permanent --add-port=8086/tcp
firewall-cmd --zone=internal --permanent --add-port=8089/udp
#+-------------------------------------------------------------------+
firewall-cmd --reload
firewall-cmd --info-zone=internal
==== Zone "docker" ====
Zone spécifique pour //Docker// qui sera nommée //docker// :
* Au préalable, vous pouvez vous assurer que //**docker0**// est bien associé à **l'IPv4 172.17.0.1/16** du serveur à l'aide de la commande : '' ip a ''
* Commandes //Firewalld// à exécuter pour la //zone docker// :
# Commencé par créer le réseau Docker si ce n'est pas déjà fait !
# Pour vérifier la présence du réseau utiliser : 'docker network ls' et le détail avec 'docker network inspect nginx-proxy'
docker network create --driver=bridge --subnet=172.18.5.0/24 --ip-range=172.18.5.0/24 --gateway=172.18.5.1 nginx-proxy
# Zone docker
# Depuis la version v20.10.0 Docker créé automatiquement la zone "docker" et y associe ses interfaces (docker0, br-...)
# firewall-cmd --permanent --new-zone=docker
# firewall-cmd --zone=docker --permanent --change-interface=docker0
firewall-cmd --zone=docker --permanent --add-source=172.18.5.0/24
# Permettre l'accès aux sites web public depuis un container (voir si la règle ci-dessous fonctionne dans le temps)
firewall-cmd --zone=docker --permanent --add-rich-rule='rule family=ipv4 source address=172.18.5.0/24 accept'
# Il est peut être nécessaire d'ajouter la règle suivante sur la zone "docker" en plus/à la place de celle existant sur "public".
firewall-cmd --zone=docker --permanent --add-masquerade
# Le 10-04-2022, l'accès à l'IP privée 10.0.1.30 (ping 10.0.1.30) fonctionnait sur l'hôte mais plus dans le docker Borgmatic. Pour résoudre le problème, ajout des 2 règles suivantes:
firewall-cmd --permanent --zone=docker --add-source=10.0.1.0/24
firewall-cmd --permanent --zone=docker --add-rich-rule='rule family=ipv4 source address=10.0.1.0/24 accept'
#+-------------------------------------------------------------------+
# Spécifique web-srv
# Accéder au status du Nginx installé sur l'hôte
firewall-cmd --zone=docker --permanent --add-port=9090/tcp
# Si nécessaire, ajouter d'autres port TCP comme 50081 (cms-adminer), 50080 (cms-nginx), 50084 (wiki-sinp-nginx) avec la commande suivante :
#firewall-cmd --zone=docker --permanent --add-port=/tcp
#+-------------------------------------------------------------------+
# Spécifique bkp-srv
# Accéder au status du Nginx installé sur l'hôte
firewall-cmd --zone=docker --permanent --add-port=9090/tcp
#+-------------------------------------------------------------------+
firewall-cmd --reload
firewall-cmd --info-zone=docker
# ATTENTION : penser à redémarrer le service Docker après : systemctl restart docker
===== Problème : Firewalld, Nftables et Docker =====
* **Symptômes** : les containers présent dans un network Docker de type bridge utilisateur n'arrivent pas à se connecter entre eux via leur nom de domaine.
* Il semblerait que les problèmes d'accès aux noms de domaines rencontrés lors de l'utilisation de Docker soient lié au fait que Docker ne supportent pas encore Nftables [2020-01-03].
* => nous rétrogradons le système pour utiliser Iptables :
* '' update-alternatives --config iptables '' : choisir le mode //legacy//
* Arrêter //Nftables//, //Docker// et //Firewalld// : ''systemctl stop firewalld ; systemctl stop nftables ; systemctl disable nftables ; systemctl stop docker''
* Éditer ''vi /etc/firewalld/firewalld.conf'' et remettre : FirewallBackend=iptables
* Supprimer toutes les règles permanentes : ''rm -fR /etc/firewalld/zones/''
* Redémarrer //Docker// puis //Firewalld// : ''systemctl start firewalld ; systemctl start docker''
* Recréer toutes les règles //Firewalld//
* La solution précédente semble être une solution retenue sur [[https://github.com/moby/moby/issues/26824|le ticket "[feature request] nftables support"]] de [[https://en.wikipedia.org/wiki/Mobi|Mobi (la version en marque blanche de Docker)]]. C'est peut être la plus simple à retenir pour l'instant. La solution consistant à utiliser une table ip et ipv6 à la place d'inet semble engendrer d'autres problèmes...
* Des ressources utiles sur le sujet :
* [[https://www.reddit.com/r/docker/comments/ca4xj0/docker_debian_buster_nftables/|Reddit : Docker, Debian Buster & nftables]] (2019-07-07) : résumé de la situation avec des liens au 2019-07-07.
* [[https://stephank.nl/p/2017-06-05-ipv6-on-production-docker.html|IPv6 on production Docker]] (2017-06-05) : solution pour Debian utilisant Nftables mais pas Firewald... Peut être trop complexe à maintenir car nécessite une bonne connaissance du fonctionnement des pare-feux.
* [[https://github.com/moby/moby/issues/26824|Github : [feature request] nftables support (#26824)]] : ticket du support de Nftables par Docker.
* [[https://serverfault.com/a/1022195|ServerFault: No network connectivity to/from Docker CE container on CentOS 8]] : exemples de règles pour Firewalld permettant de faire fonctionner Docker correctement. NON TESTÉ...
===== Problème : Firewalld et Docker - "connect: no route to host" =====
* **Contexte** : Par défaut, //Firewalld// bloque la communication entre les containers sur le serveur hôte hébergeant Docker. Du coup, les containers n'arrive pas à accéder aux serveurs web public extérieur au container.
* **Vérification** : ce message d'erreur peut aussi apparaître si le service distant ne fonctionne pas. Assurez vous que le service distant est bien disponible sur le port attendu. Pour cela, utiliser ''telnet'' avec : ''telnet ''. Si le service est bien accessible, vous pouvez appliquer les solutions suivantes.
* **Solutions** : pour autoriser la communication entres le containers //Docker//, il est possible d'utiliser une des solutions suivantes :
* **Solution 1** (voir plutôt la solution 2 qui est pérenne) : '' firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 4 -i docker0 -j ACCEPT ''
* Elle devra être suivi des commandes suivantes :
* '' firewall-cmd --reload ''
* ''systemctl restart docker''
* **Solution 2** : //Docker// créé de nouvelles interfaces réseau associés à un nouveau intervalle d'IP privés pour chaque nouveau réseau créé, visible via : ''ip a''. Ainsi, associer l'interface //docker0// à une zone ne suffit pas, il semble plus pertinent de créer une zone spécifique et d'y associer en //source// les IP du réseau //Docker// que nous créons. A noter que depuis sa version 20.10.0, Docker créé automatiquement dans Firewalld la zone "docker" dans laquelle il associe les interfaces qu'il créé. Procédure :
* Créer notre réseau Docker utilisateur, ici nommé //nginx-proxy//, de type //bridge// en spécifiant les IP possible pour les containers ('' --subnet=172.18.5.0/24 '') et la référence à l'IP de l'hôte dans ce réseau ('' --gateway=172.18.5.1 '') : '' docker network create --driver=bridge --subnet=172.18.5.0/24 --gateway=172.18.5.1 nginx-proxy ''
* Créer une zone spécifique pour //Docker// dans //Firewalld// : '' firewall-cmd --permanent --new-zone=docker ''
* Associer l'interface //docker0// à cette zone : '' firewall-cmd --permanent --zone=docker --change-interface=docker0 ''
* Associer le réseau Docker "utilisateur" que nous avons créé : '' firewall-cmd --permanent --zone=docker --add-source=172.18.5.0/24 ''
* Cette alternative semble mieux fonctionner : '' firewall-cmd --zone=docker --permanent --add-rich-rule='rule family=ipv4 source address=172.18.5.0/24 accept' '' : fonctionne correctement au 2021-03-26 avec la version 20.10.5 de Docker et Firewalld 0.6.3.
* Activer sur la zone "public" la //masquerade// semble être nécessaire (à confirmer) : '' firewall-cmd --zone=public --permanent --add-masquerade ''
* Recharger pour supprimer les règles temporaires et appliquer les règles permanentes : '' firewall-cmd --reload ''
* Redémarrer //Firewalld// : ''systemctl restart firewalld''
* Redémarrer //Docker// : ''systemctl restart docker''
* **Notes** : stopper le service Firewalld puis redémarrer le service Docker et enfin démarrer à nouveau Firewalld résout "temporairement" le problème //connect: no route to host//. Il est nécessaire de faire cette manip à chaque redémarrage de container et sur chaque serveur avec lequel le container discute... ! En outre, cet ordre de démarrage des services provoque un autre type d'erreur (cf ci-dessous "Problème : iptables: No chain/target/match by that name").
===== Problème : "iptables: No chain/target/match by that name" =====
* **Erreur** : ERROR: for manager-portainer Cannot start service manager-portainer: driver failed programming external connectivity on endpoint 0f0a6417f223_manager-portainer (55e5c1439a5f2d8880f8e65676b492263223a2ea649aac87d72c4a0a98aac21c): (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 127.0.0.1 --dport 9000 -j DNAT --to-destination 172.18.5.2:9000 ! -i br-e8fc729a5e58: iptables: No chain/target/match by that name.
* **Contexte** : lors du démarrage du Docker Portainer (ex. manager.silene.eu) avec la commande : ''docker-compose up''
* **Solution** : Stopper le service Firewalld et Docker, puis redémarrer dans l'ordre Firewalld puis Docker résout "temporairement" le problème.
* **Erreur** :
mars 26 02:00:00 sinp-paca-web dockerd[8322]: time="2021-03-26T02:00:00.644589374+01:00" level=info msg="ignoring event" container=c6e9cd33b6eb6f60edec8e645541de8d20afb15a7d911197c9bc89ab7a49e780 module=libcontain
mars 26 02:00:00 sinp-paca-web dockerd[8322]: time="2021-03-26T02:00:00.880242322+01:00" level=info msg="ignoring event" container=0c0f81af4cc08fa364adee66ee9202e7c28b883056e26db51d7b144a35f26ccc module=libcontain
mars 26 02:00:02 sinp-paca-web dockerd[8322]: time="2021-03-26T02:00:02.873481139+01:00" level=info msg="ignoring event" container=3a2ce9ad82ea0c1299ebce75e84707dc562a21eb9b2cb05696a1b4807042e6cb module=libcontain
mars 26 02:00:03 sinp-paca-web dockerd[8322]: time="2021-03-26T02:00:03.105072289+01:00" level=info msg="ignoring event" container=e15f443ce2a865ae783689bf103990bdf9804ac533d5772e2c14fbf6f102d354 module=libcontain
mars 26 02:00:24 sinp-paca-web dockerd[8322]: time="2021-03-26T02:00:24.468115748+01:00" level=warning msg="Failed to allocate and map port 50081-50081: (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -
mars 26 02:00:24 sinp-paca-web dockerd[8322]: time="2021-03-26T02:00:24.544748120+01:00" level=error msg="e15f443ce2a865ae783689bf103990bdf9804ac533d5772e2c14fbf6f102d354 cleanup: failed to delete container from c
mars 26 02:00:24 sinp-paca-web dockerd[8322]: time="2021-03-26T02:00:24.544792461+01:00" level=error msg="Handler for POST /v1.24/containers/cms-adminer/start returned error: driver failed programming external con
mars 26 02:00:24 sinp-paca-web dockerd[8322]: time="2021-03-26T02:00:24+01:00" level=info msg="Firewalld: docker zone already exists, returning"
mars 26 02:00:25 sinp-paca-web dockerd[8322]: time="2021-03-26T02:00:25+01:00" level=info msg="Firewalld: docker zone already exists, returning"
mars 26 02:00:25 sinp-paca-web dockerd[8322]: time="2021-03-26T02:00:25.711870702+01:00" level=warning msg="Failed to allocate and map port 50080-50080: (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -
mars 26 02:00:25 sinp-paca-web dockerd[8322]: time="2021-03-26T02:00:25.784483347+01:00" level=error msg="c6e9cd33b6eb6f60edec8e645541de8d20afb15a7d911197c9bc89ab7a49e780 cleanup: failed to delete container from c
mars 26 02:00:25 sinp-paca-web dockerd[8322]: time="2021-03-26T02:00:25.784525338+01:00" level=error msg="Handler for POST /v1.24/containers/cms-nginx/start returned error: driver failed programming external conne
* **Contexte** : Les container cms-wordpress et wiki-sinp-nginx ne veulent pas démarrer et affiche une erreur lié à Iptables. Docker en version 20.10.0+ ajoute automatiquement une nouvelle zone //docker// à Firewalld comme indiqué dans [[https://docs.docker.com/network/iptables/#integration-with-firewalld|la doc concernant Iptables de Docker]]. Il semblerait aussi que le serveur à 2h et 3h du matin effectue une tâche qui provoque l'erreur ci-dessous car //DOCKER// n'existe pas dans Iptables. Cette entrée disparait si on démarre le service Firewalld après celui de Docker. La commande '' iptables -t nat -nvL | grep DOCKER '' ne retourne rien alors qu'elle devrait affiché ceci :
2118 93277 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
0 0 DOCKER all -- * * 0.0.0.0/0 !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
* **Solution** : redémarrer le service Docker après celui de Firewalld. Le redémarrage du service Docker va recréer l'entrée //DOCKER//. Relancer ensuite les containers qui n'ont pas voulu démarré depuis l'interface de Portainer par exemple.
===== Problème : "psql: error: could not connect to server: Connection refused" =====
* **Contexte** : Depuis le container Borgmatic de l'instance //db-srv//, il est impossible d'accéder à Postgresql. La commande suivante '' psql -h 172.18.5.1 -p 5432 -U postgres --no-password '' génère l'erreur :
psql: error: could not connect to server: Connection refused
Is the server running on host "172.18.5.1" and accepting
TCP/IP connections on port 5432?
* **Solution #1 :** sur l'hôte, afficher les IPs sur lesquelles Postgresql écoute avec la commande ''ss -lntu'' et vérifier qu'il y ait bien une ligne :
tcp LISTEN 0 128 172.18.0.1:5432 0.0.0.0:*
Il faut Postgresql (port 5432) écoute sur l'IP de la gateway du bridge de Docker (ici 172.18.0.1). Si cette ligne est absente, redémarrer Postgresql ''systemctl restart postgresql''. Elle devrait apparaitre. Si pour une raison ou une autre le service Docker n'est pas démarré au moment du démarrage de Postgresql (ordre du démarrage des services par la machine lors de son lancement), Postgresql n'écoutera pas sur l'IP 172.18.0.1 car non existante...
* **Solution #2 :** vérifier également via (Portainer) que le réseau nginx-proxy a bine un gateway définie à 172.18.5.1. Il faut également que les containers Dockers dans le réseau 172.18.x.x n'occupent pas les IP 172.18.5.1 et 172.18.5.0. Si le réseau est mal configuré, il faut le supprimer et le recréer correctement (voir section //Zone "docker"//).
===== Problème : aucun accès à internet depuis le container =====
* ** Erreur** :
# Curl
Info: Could not resolve host: acme-v02.api.letsencrypt.org
# Ping
bad address 'www.google.com'
* **Contexte** : aucun accès à internet que cela soit via une IP (8.8.8.8) ou un nom de domaine (www.google.com) mais l'hôte via la passerelle (172.18.5.1) du reste joignable (''ping 172.18.5.1'').
* **Solution** : après avoir essayé les différentes techniques exposées sur [[https://stackoverflow.com/a/68474595/13311850|cette page StackOveflow]], c'est le redémarrage de la machine qui a tout remis dans l'ordre : ''reboot''.