Howto netfilter
- Documentation : https://netfilter.org/documentation/
netfilter est la couche de
firewalling sous Linux, contrôlable via les commandes
iptables
et ip6tables
, ou plus récemment avec
la commande nft
(que nous n’utilisons pas encore). Sur les
serveurs, nous utilisons notre script minifirewall
permettant une gestion simple d’un firewall local.
Installation
# apt install iptables
# iptables --version
iptables v1.8.9 (nf_tables)
# modinfo iptable_filter
filename: /lib/modules/6.1.0-18-cloud-amd64/kernel/net/ipv4/netfilter/iptable_filter.ko
description: iptables filter table
author: Netfilter Core Team <coreteam@netfilter.org>
license: GPL
depends: ip_tables,x_tables
retpoline: Y
intree: Y
name: iptable_filter
vermagic: 6.1.0-18-cloud-amd64 SMP preempt mod_unload modversions
sig_id: PKCS#7
signer: Debian Secure Boot CA
sig_key: 32:A0:28:7F:84:1A:03:6F:A3:93:C1:E0:65:C4:3A:E6:B2:42:26:43
sig_hashalgo: sha256
signature: 71:F7:F9:B6:38:B0:B8:63:41:D6:DA:39:41:2B:AD:EB:E4:5A:05:36:
AF:9E:55:6F:CF:39:E2:D9:CD:23:7B:49:B0:C6:4D:BE:1F:A5:68:F2:
6D:EA:88:A1:CD:B5:4F:CE:3B:A2:10:3D:74:E7:DB:29:31:00:AF:81:
CD:EA:31:BC:6D:68:F2:5B:1B:3B:85:6E:2C:52:B5:B7:14:44:62:C8:
B9:2F:89:5A:E0:3A:9B:D9:89:48:A0:E1:F3:92:B7:B8:C2:C2:F4:6D:
34:95:4D:0A:2F:15:E9:F4:1D:3B:07:49:7B:8C:AB:56:58:18:C8:8D:
67:70:D7:D5:60:07:33:86:39:DA:5A:D0:6C:F3:37:F4:0A:84:A9:AF:
FE:AC:8F:ED:C9:CA:C6:A6:DA:4B:23:AA:4F:7D:F0:37:90:AF:1C:EC:
73:F7:71:E2:50:42:48:0D:8A:7D:61:D4:F3:E2:FC:77:CF:96:0F:4D:
96:7D:85:A2:EC:7D:A9:A6:2E:41:58:DB:B3:72:A2:02:39:6C:B9:6A:
02:47:F2:DB:9B:AD:77:D9:04:8E:F8:42:D4:BA:40:D1:CE:3D:36:CB:
4B:18:C6:CC:4A:09:4A:7C:95:AF:4B:8B:04:83:13:3A:6C:DA:A0:BD:
CB:D9:74:5E:8C:76:CF:03:CB:D2:E5:4B:F4:10:FB:E5
parm: forward:bool
Utilisation basique
Lister les règles dans les 3 tables filter/nat/mangle :
# iptables -L -nvt filter --line-number
# iptables -L -nvt nat --line-number
# iptables -L -nvt mangle --line-number
# ip6tables -L -nvt filter --line-number
# ip6tables -L -nvt nat --line-number
# ip6tables -L -nvt mangle --line-number
Interdire toutes les connexions venant de l’IP 192.0.2.66 :
# iptables -I INPUT -s 192.0.2.66 -j DROP
Renvoyer les connexions extérieures vers un port à un autre port (avec 192.0.2.42 l’IP du serveur local) :
/sbin/iptables -t nat -A PREROUTING -p tcp --dport 443 -j DNAT --to 192.0.2.42:22
Désactiver toutes les règles et laisser tout passer :
# iptables -P INPUT ACCEPT
# iptables -P OUTPUT ACCEPT
# iptables -P FORWARD ACCEPT
# iptables -F -t filter
# iptables -F -t nat
# iptables -F -t mangle
Sauvegarder les règles courantes dans un fichier, réinitialiser les règles à partir d’un fichier :
# iptables-save > /tmp/IPT
# ip6tables-save > /tmp/IP6T
# iptables-restore /tmp/IPT
# ip6tables-restore /tmp/IP6T
minifirewall
minifirewall est un script Shell permettant une gestion simple d’un firewall local, idéal pour un serveur dédié (virtualisé ou non).
À partir de la version 22.03
, minifirewall dispose d’une
configuration principale /etc/default/minifirewall
uniquement déclarative et d’un système d’inclusion automatique de ce qui
est dans /etc/minifirewall.d/
Installation minifirewall
# [ -e /etc/default/minifirewall ] && cp /etc/default/minifirewall /etc/default/minifirewall.old
# wget https://gitea.evolix.org/evolix/minifirewall/raw/branch/master/minifirewall.conf -O /etc/default/minifirewall
# wget https://gitea.evolix.org/evolix/minifirewall/raw/branch/master/minifirewall -O /etc/init.d/minifirewall
# chmod 700 /etc/init.d/minifirewall
# chmod 600 /etc/default/minifirewall
# mkdir /etc/minifirewall.d
# chmod 700 /etc/minifirewall.d
# insserv /etc/init.d/minifirewall
Configuration déclarative
On peut gérer la configuration via le fichier
/etc/default/minifirewall
.
- Il faut adapter la variable
INT
avec l’interface réseau principale
Pour les règles en entrée, tout est coupé et il faut définir :
- les variables
SERVICESTCP1
etSERVICESUDP1
pour les ports publics en IPv4 et IPv6 - 2 catégories d’adresses IPv4 : les adresses de confiance
TRUSTEDIPS
et les adresses privilégiéesPRIVILEGIEDIPS
- pour les ports semi-publics, on utilise
SERVICESTCP2
etSERVICESUDP2
ouverts aux adressesPRIVILEGIEDIPS
- pour les ports privés, on utilise
SERVICESTCP3
etSERVICESUDP3
ouverts aux adressesTRUSTEDIPS
- les variables
SERVICESTCP1p
etSERVICESUDP1p
ne servent pas, sauf si on a créé une chaîne IPTables spécifiqueNEEDRESTRICT
Pour les règles en sortie, tout est coupé et on peut autoriser facilement les ports les plus classiques :
DNSSERVEURS='0.0.0.0/0 ::/0'
pour autoriser toutes les requêtes DNS (en IPv4 et IPv6)HTTPSITES='0.0.0.0/0 ::/0'
pour autoriser toutes les requêtes en sortie vers le port 80 (en IPv4 et IPv6)- …
Il est possible de configurer un proxy http sortant (par exemple
Squid). La variable PROXY
active ou désactibe le mécanisme
depuis le firewall. PROXYPORT
indique le port à utiliser
pour le forward des paquets. PROXYBYPASS
indique la liste
des IP de destination pour lesquelles on ne passe pas par le proxy.
# Proxy (Squid)
PROXY='on'
# (proxy port)
PROXYPORT='8888'
# (destinations that bypass the proxy)
PROXYBYPASS="${INTLAN} 127.0.0.0/8"
Il est possible de configurer les autorisations pour les serveurs de
backup, en ajoutant des couples IP:PORT
dans la variable
BACKUPSERVERS
.
Plusieurs autres variables sont disponibles pour affiner la
configuration kernel (via sysctl
) :
# In most cases, the default values set by minifirewall are good.
# If you really know what you are doing,
# you can uncomment some lines and customize the value.
# Set 1 to ignore broadcast pings (default)
# SYSCTL_ICMP_ECHO_IGNORE_BROADCASTS='1'
# Set 1 to ignore bogus ICMP responses (default)
# SYSCTL_ICMP_IGNORE_BOGUS_ERROR_RESPONSES='1'
# Set 0 to disable source routing (default)
# SYSCTL_ACCEPT_SOURCE_ROUTE='0'
# Set 1 to enable TCP SYN cookies (default)
# SYSCTL_TCP_SYNCOOKIES='1'
# Set 0 to disable ICMP redirects
# SYSCTL_ACCEPT_REDIRECTS='0'
# SYSCTL_SEND_REDIRECTS='0'
# Set 1 to enable Reverse Path filtering (default)
# Set 0 if VRRP is used
# SYSCTL_RP_FILTER='1'
# Set 1 to log packets with inconsistent address (default)
# SYSCTL_LOG_MARTIANS='1'
Ces variables sont en général à laisser telles-quelles. Attention
cependant à SYSCTL_RP_FILTER
qu’il faut mettre à
0
lorsqu’on utilise VRRP.
Configuration par inclusion
Tous les fichiers présents dans /etc/minifirewall.d
sont
automatiquement inclus et exécutés au démarrage de minifirewall, par
ordre alphanumérique, en excluant les fichiers contenant des points dans
leur nom.
On peut y mettre des commandes shell arbitraires pour appliquer des règles spécifiques.
Quelques fonctions shell sont disponibles :
# Within included files, you can use those helper functions :
# * is_ipv6_enabled: returns true if IPv6 is enabled, or false
# * is_docker_enabled: returns true if Docker mode is eabled, or false
# * is_proxy_enabled: returns true if Proxy mode is enabled , or false
- Pour autoriser toutes les requêtes en sortie vers le port 80 en IPv6
:
ip6tables -A INPUT -i $INT -p tcp --sport 80 --match state --state ESTABLISHED -j ACCEPT
- Pour autoriser en sortie vers le port 636 d’un serveur extérieur :
iptables -A INPUT -p tcp --sport 636 --dport 1024:65535 -s ssl.evolix.net -m state --state ESTABLISHED -j ACCEPT
Note : pour autoriser un port en entrée, on n’utilisera pas de règle spécifique mais les variables dédiées dans la configuration principale
Note : pour autoriser un port en sortie, la règle à ajouter paraît contre-intuitive car les paquets en sortie sont déjà tous autorisés (sauf en UDP) : on autorise en fait les paquets de retour (
INPUT
) qui correspondent à des paquets déjà émis en sortie (--state ESTABLISHED
)
IPv6
Si on souhaite désactiver le support IPv6, ça se fait via la variable
IPv6=off
Dans tous les cas il est conseillé de mettre les IPv4 et IPv6 dans les variables concernées. Les IPv6 seront ignorées si c’est désactivé.
En IPv4, l’autorisation globale est 0.0.0.0/0
.
l’équivalent en IPv6 est ::/0
.
Docker
Général
La variable générale DOCKER=on
permet de définir si
minifirewall doit tenir compte de Docker. C’est surtout pour éviter de
purger les règles injectées par Docker (dans les tables de NAT) lors de
l’arrêt ou du redémarrage de minifirewall. Dans le cas contraire un
redémarrage de minifirewall va casser la connectivité des containers de
l’hôte.
Important : Ça veut aussi dire qu’il est préférable de ne pas utiliser le filtrage du trafic HTTP sortant avec squid, car ce système s’appuie sur des règles de NAT qui ne seront pas retirées lors de l’arrêt de minifirewall. ( mettre proxy à off dans minifirewall, et dans l’ordre, éteindre docker, redémarrer minifirewall, rallumer docker )
Pour comment gerer les plages d’ips utilisé par docker, se referer à
la documentation
HowtoDocker#changer-les-plages-dips-utilis%C3%A9-par-docker
Communication service dans Docker → host
Si on souhaite autoriser des conteneurs à communiquer via le réseau avec d’autres processus hors Docker sur le serveur, il faut autoriser ces communications de manière explicite :
# On autorise les paquets de retour des conteneur
/sbin/iptables -A INPUT -p tcp --dport 1024:65535 -s 172.16.0.0/12 -j ACCEPT
/sbin/iptables -A INPUT -p tcp --dport 1024:65535 -s 192.168.0.0/20 -j ACCEPT
/sbin/iptables -A INPUT -p tcp --dport 1024:65535 -s 10.0.0.0/8 -j ACCEPT
# On autorise plus finement que sur le port de postgres, et uniquement pour la gateway de docker0 ( sur laquel postgres écoute )
/sbin/iptables -I INPUT -s 172.16.0.0/12 -d 172.17.0.1 -p tcp --dport 5432 -j ACCEPT
Attention à bien autoriser uniquement des plages privées ( RFC 1819 ) et de vérifier de ne pas autoriser sur d’autres réseaux lans !
Communication * → Service dans docker
En mode DOCKER=on
, les règles d’ouverture de ports
public/privilégié/privés fonctionnent aussi pour les services
containerisés que l’on souhaite exposer.
A partir de la version 24.07
, il y a une chaine spéciale
(MINIFW-DOCKER-INPUT-MANUAL
) pour permettre la
configuration de règles de filtrages spéciales. Il est ainsi possible de
faire des règles personalisées pour des services exposés depuis docker,
de la même manière que des services de l’hôte
Il y a deux nuances importantes à prendre en compte :
- Les acceptations doivent être faites avec
RETURN
et nonACCEPT
- Les règles doivent être injectées avec
-I
(INSERT) et non-A
(APPEND), si non elles ne seront pas évaluées.
Exemples :
/sbin/iptables -I MINIFW-DOCKER-INPUT-MANUAL -p tcp -s 192.0.2.22 --dport 5670 -m comment --comment "Accept 5670/tcp from 192.0.2.22" -j RETURN
/sbin/iptables -I MINIFW-DOCKER-INPUT-MANUAL -p tcp -s 192.0.2.33 --dport 443 -m comment --comment "Drop 443/tcp from abusive 192.0.2.33" -j DROP
Docker Swarm
Si on utilise Docker Swarm sur plusieurs serveurs, il faut une configuration particulière pour que la communication inter-nœuds se fasse bien.
# interface par laquelle les nœuds communiquent
SWARM_INT=eno10
## Pour chaque autre serveur de la Swarm (192.0.2.11, 192.0.2.12…) :
NODE_IP=192.0.2.11
/sbin/iptables -I INPUT -i ${SWARM_INT} -p tcp -s ${NODE_IP} --dport 2377 -m comment --comment "Allow incomming docker swarm (management TCP) from ${NODE_IP}" -j ACCEPT
/sbin/iptables -I INPUT -i ${SWARM_INT} -p tcp -s ${NODE_IP} --sport 2377 -m state --state ESTABLISHED -m comment --comment "Allow outgoing docker swarm (management TCP) to ${NODE_IP}" -j ACCEPT
/sbin/iptables -I INPUT -i ${SWARM_INT} -p tcp -s ${NODE_IP} --dport 7946 -m comment --comment "Allow incomming docker swarm (inter-node TCP) from ${NODE_IP}" -j ACCEPT
/sbin/iptables -I INPUT -i ${SWARM_INT} -p tcp -s ${NODE_IP} --sport 7946 -m state --state ESTABLISHED -m comment --comment "Allow outgoing docker swarm (inter-node TCP) to ${NODE_IP}" -j ACCEPT
/sbin/iptables -I INPUT -i ${SWARM_INT} -p udp -s ${NODE_IP} --dport 7946 -m comment --comment "Allow incomming docker swarm (inter-node UDP) from ${NODE_IP}" -j ACCEPT
/sbin/iptables -I INPUT -i ${SWARM_INT} -p udp -s ${NODE_IP} --sport 7946 -m state --state ESTABLISHED -m comment --comment "Allow outgoing docker swarm (inter-node UDP) to ${NODE_IP}" -j ACCEPT
/sbin/iptables -I INPUT -i ${SWARM_INT} -p udp -s ${NODE_IP} --dport 4789 -m comment --comment "Allow incomming docker swarm (network UDP) from ${NODE_IP}" -j ACCEPT
/sbin/iptables -I INPUT -i ${SWARM_INT} -p udp -s ${NODE_IP} --sport 4789 -m state --state ESTABLISHED -m comment --comment "Allow outgoing docker swarm (network UDP) to ${NODE_IP}" -j ACCEPT
## Une seule fois
/sbin/iptables -I OUTPUT -o ${SWARM_INT} -p udp --dport 4789 -m comment --comment "Allow outgoing docker swarm (network UDP)" -j ACCEPT
/sbin/iptables -I OUTPUT -o ${SWARM_INT} -p udp --dport 7946 -m comment --comment "Allow outgoing docker swarm (inter-node UDP)" -j ACCEPT
Commandes (start, stop…)
# /etc/init.d/minifirewall help
minifirewall Firewall designed for standalone server
Usage: minifirewall [COMMAND]
Commands
start Start minifirewall
safe-start Start minifirewall, with baground safety checks
stop Stop minifirewall
restart Stop then start minifirewall
safe-restart Restart minifirewall, with background safety checks
status Print minifirewall status
reset Reset iptables tables
check-active-config Check if active config is up-to-date with stored config
version Print version and exit
help Print this message and exit
Il est possible de redémarrer le firewall en mode interactif avec une protection contre le blocage :
# /etc/init.d/minifirewall safe-restart
sourcing `/etc/default/minifirewall'
WARNING: current state is different than persisted state, check /var/run/minifirewall_state_diff
minifirewall stopping
flushing all rules and accepting everything
minifirewall stopped
minifirewall starting
sourcing `/etc/minifirewall.d/zzz-custom'
sourcing `/etc/minifirewall.d/zzzz-ban'
minifirewall started
INFO: rules have changed since latest start, check /var/run/minifirewall_state_diff
Minifirewall will be stopped in 30 seconds if you do nothing.
Remove `/var/run/minifirewall_safety.lock' or type anything to keep minifirewall started:
Il faut alors aller retirer le fichier de lock (par n’importe quel moyen) ou saisir et valider n’importe quoi au clavier.
Si on ne le fait pas dans les 30 secondes, le firewall sera stoppé pour redonner la main à l’utilisateur.
FAQ
Bannir une adresse IP
En cas de besoin de bannir temporairement une adresse IP 192.0.2.66 :
# iptables -I INPUT -s 192.0.2.66 -j DROP
NAT bidirectionnel
# iptables -t nat -A PREROUTING -i eth0 -d IP_publique -j DNAT --to-destination 192.0.2.1
# iptables -t nat -A POSTROUTING -o eth0 -s 192.0.2.1 -j SNAT --to-source IP_publique
Autoriser toute une interface
Lorsqu’un serveur dispose de plusieurs interfaces, dont une pour un réseau local privé, on peut autoriser tout le trafic sur cette interface (ici “eth1”) :
# iptables -A INPUT -i eth1 -j ACCEPT
Autoriser la sortie sur un port
Exemple si on a un serveur Munin centralisé, il a besoin de joindre les munin-node :
# iptables -A INPUT -p tcp --sport 4949 --dport 1024:65535 -m state --state ESTABLISHED -j ACCEPT
Redirection du port 80 en sortie vers un proxy local
# iptables -t nat -A OUTPUT -p tcp --dport 80 -m owner --uid-owner proxy -j ACCEPT
# iptables -t nat -A OUTPUT -p tcp --dport 80 -d 192.0.2.42 -j ACCEPT
# iptables -t nat -A OUTPUT -p tcp --dport 80 -d 127.0.0.1 -j ACCEPT
# iptables -t nat -A OUTPUT -p tcp --dport 80 -j REDIRECT --to-port 3128
FTP Passif
# iptables -A INPUT -p tcp --dport 60000:61000 -m state --state NEW,ESTABLISHED -j ACCEPT
Test des ports TCP
Pour tester l’ouverture d’un port TCP en sortie, on peut utiliser http://portquiz.net/ qui écoute sur tous les ports TCP possibles :
$ telnet portquiz.net 42
Trying 52.47.209.216...
Connected to portquiz.net.
Escape character is '^]'.
Tout autoriser en sortie (IPv4)
Si l’on veut tout autoriser en sortie (TCP, UDP), voici la règle à ajouter :
# iptables -A INPUT -m state --state ESTABLISHED -j ACCEPT
Logger les packets dropped
On peut facilement activer les logs pour les packets droppé dans INPUT et FORWARD notamment,
Attention 1 : si docker est activé, il faut sy prendre differement pour la table forward, voir plus bas
Attention 2 : il ne faut pas mettre ces regles dans /etc/minifirewall.d/* ; celle-ci serait ajouté trop tôt et vous dropperez presque tous les accès du server !
Il faut jouer manuellement (aprés que minifirewall ait été démarré) :
# iptables -A INPUT -j LOG_DROP
# iptables -A FORWARD -j LOG_DROP
### Suivre les logs
# tail -f /var/log/syslog | grep "IPTABLES DROP"
On peut ensuite les enlever manuellement ou bien restart minifirewall :
# iptables -D INPUT -j LOG_DROP
# iptables -D FORWARD -j LOG_DROP
Cas docker ON :
# iptables -A INPUT -j LOG_DROP
# iptables -A MINIFW-DOCKER-TRUSTED -j LOG_DROP
# iptables -D MINIFW-DOCKER-TRUSTED -j DROP
### Suivre les logs
# tail -f /var/log/syslog | grep "IPTABLES DROP"
On peut ensuite les enlever manuellement ( OBLIGATOIRE pour la chaine MINIFW-DOCKER-TRUSTED) :
# iptables -D INPUT -j LOG_DROP
# iptables -A MINIFW-DOCKER-TRUSTED -j DROP
# iptables -D MINIFW-DOCKER-TRUSTED -j LOG_DROP
Remarque, minifirewall ne modifie pas la chaine forward quand docker est à ON
ipset
ipset
permet de géré des, mal només, ensemble d’IP (ip
sets) mais ces ensemeble peuvent contenir plus que des IP, comme : des
adresses MAC, des ports, des sous réseaux et des interfaces
Créer deux ipset contenant l’une table de hachage d’adresses IP et l’autre de port :
# ipset create foo hash:ip
# ipset create bar hash:port
Supprimer un ipset :
# ipset destroy foo
Ajouter une adresse IP à un ipset :
# ipset add foo 192.0.2.42
Supprimer une adresse IP d’un ipset :
# ipset del foo 192.0.2.42
Sauvegarder un ipset :
# ipset save foo > foo.ipset
Utiliser un ipset dans une règle iptables :
# iptables -I INPUT -m set --match-set foo src -j DROP