Login Logout

Howto netfilter

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 et SERVICESUDP1 pour les ports publics en IPv4 et IPv6
  • 2 catégories d’adresses IPv4 : les adresses de confiance TRUSTEDIPS et les adresses privilégiées PRIVILEGIEDIPS
  • pour les ports semi-publics, on utilise SERVICESTCP2 et SERVICESUDP2 ouverts aux adresses PRIVILEGIEDIPS
  • pour les ports privés, on utilise SERVICESTCP3 et SERVICESUDP3 ouverts aux adresses TRUSTEDIPS
  • les variables SERVICESTCP1p et SERVICESUDP1p ne servent pas, sauf si on a créé une chaîne IPTables spécifique NEEDRESTRICT

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.

# Backup servers
BACKUPSERVERS='192.0.2.123:1234 203.0.113.42:5678'

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 non ACCEPT
  • 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