Nftables
Les exemples de configuration doivent être validés par des personnes experimentées, je ne suis pas sûre de moi.
Passer de iptables à nftables
Déjà, sur toute nouvelle version de Debian, par défaut c'est nftables derrière (commande nft
). Ensuite, même si on installe le paquet “iptables” en vrai c'est une surcouche à présent, qui traduira les règles de façon invisible pour l'utilisatrice. Ce qui permet de continuer à utiliser ses config iptables sans rien changer quand on n'a pas le temps, et c'est cool. Mais il faut aussi varier ;)
Installer le paquet iptables permet d'avoir accès à un outil intéressant : iptables-translate. Cette commande nous permet de savoir comment une règle iptable se traduit en terme de syntaxe pour nftables. Cela permet d'adapter les anciens tutoriels plus rapidement…
Plus d'infos ici : https://wiki.nftables.org/wiki-nftables/index.php/Moving_from_iptables_to_nftables
nftables.conf
Ce fichier /etc/nftables.conf
est un peu l'équivalent du fichier de conf de “iptable-persistant”. Il faut quand même déclarer qu'il faut le charger automatiquement, mais une fois fait, il va lire les règles là lors du redémarrage du serveur.
Fichier par défaut et fonctionnement
Fichier par défaut sur ma Debian :
#!/usr/sbin/nft -f flush ruleset table inet filter { chain input1 { type filter hook input priority filter; } chain forward1 { type filter hook forward priority filter; } chain output1 { type filter hook output priority filter; } }
Organisation de ce fichier :
- Il doit commencer par
#!/usr/sbin/nft -f
qui précise le programme à exécuter - On peut mettre une commande directement (pas n'importe quoi bien sûr). Généralement on commence avec
flush ruleset
qui purge tout vieux truc précédent. table
: c'est là qu'on déclare les tables, c'est à dire une catégorie pour organiser des lots de règles.inet
: famille d'adresses ipv4 ET ipv6. On peut aussi déclarer juste pour ipv4 avecip
et ipv6 avecip6
.- Ici “filter” est le nom de la table. On peut donner le nom qu'on veut à une table.
- Les chaines sont une suite de règles à appliquer dans un contexte précis. Ici, les noms “input1, forward1 et output1” sont juste des conventions (auquel j'ai rajouté “1” pour éviter toute confusion), on peut aussi les appeler comme on veut. Là où elles agissent est défini ensuite avec
type filter hook input
(et là, par contre, les mots-clés sont importants et figés).- Les type peuvent être :
filter
,route
,nat
. - Les hook peuvent être :
prerouting
,input
,forward
,output
,postrouting
. - S'il n'y a pas la règle
type [x] hook [y]
, la chaine ne sera pas interprétée, sauf à y faire appel dans une autre chaine (cf détail de syntaxe plus bas).
priority filter
: quand tu ne sais pas, tu met filter, mais y'a d'autres options.filter
ça précise les règles qui vont suivrent et qui ici sont donc de filtrer… Sinon il y a des chiffres (voir la doc) pour préciser dans quel ordre tout va s'appliquer.- Peu importe l'ordre des
chain
et destable
. Par contre il faut déclarer le type de filtre dans les chaines, même si d'autres type de règles sont possibles.
Ça c'est donc la version de base et terriblement trop permissive ; suivant les cas d'usages on va choisir diverses options.
Quelque soit la version, je préfère une politique où je refuse tout, en dehors de ce qui est explicitement autorisé.
Options
Je ne vais pas tout décrire, y'a la doc pour ça, juste ce que j'ai trouvé à utiliser et pourquoi (quand ça ne me semblait pas explicite avec un petit commentaire dans la configuration).
IPv6
Sur la partie ipv6, j'ai ajouté la ligne suivante sans trop comprendre1) :
meta nfproto ipv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-reply, echo-request, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, 148, 149 } accept
meta nfproto ipv6
: Vérifie la famille de protocole et l'applique uniquement sur IPv6.icmpv6 type {…} accept
: Filtre les types ICMPv6 spécifiques et les accepte. Ces types ont à la fois des “noms” (ci-dessous) et des chiffres (entre parenthèses) pour les identifier, on peut utiliser l'un ou l'autre indifférement.destination-unreachable
(1) : hôte ou réseau inaccessible.packet-too-big
(2) : “paquet trop grand”, essentiel pour un truc appelé PMTUD (Path MTU Discovery).time-exceeded
(3) : le réassemblage d’un paquet fragmenté échoue ou le TTL arrive à zéro, utilisé par Traceroute.parameter-problem
(4) : erreur dans l'en-tête du paquet IPv6, évite que des paquets malformés circulent.echo-request
(128) etecho-reply
(129) : un ping entrant et sortantnd-router-solicit
(133) : Neighbour Discovery (ND). Demande aux routeurs IPv6 présents sur le réseau de s'annoncer.nd-router-advert
(134) est la réponse à la demande.nd-neighbor-solicit
(135) : Permet de savoir quelle machine possède une adresse IPv6 donnée.nd-neighbor-advert
(136) est la réponse.148, 149
: fait référence aux types “ICMPv6 MLD Report (Multicast Listener Report)” et “ICMPv6 MLD Done (Multicast Listener Done)”, le premier gérant les groupes multicast IPv6 et le second pour indiquer qu'un hôte ne veut plus écouter un groupe multicast2).
Ces paquets sont nécessaires au bon fonctionnement d'IPv6 et doivent donc être acceptés, au risque sinon de casser le réseau, d'avoir une mauvaise connectivité ou d'empêcher des fonctionnalités. Certains vont êtres limités (comme le ping) et d'autres simplement acceptés sans restrictions.
ip6 saddr fe80::/10 icmpv6 type { 130, 131, 132, 143, 151, 152, 153 } accept
Cette règle protège contre certaines attaques de spoofing en s'assurant que ces messages viennent bien d’un hôte local.
ip6 saddr fe80::/10
: adresses utilisées en local (sans routeur). On s'évite des soucis en évitant de les bloquer trop.icmpv6 type { 130, 131, 132, 143, 151, 152, 153 }
: Accepte uniquement certains types ICMPv6, liés principalement à la gestion du multicast et de la découverte du réseau. Plus précisément :130
, MLD Query : Demande aux hôtes IPv6 quels groupes multicast ils écoutent131
, MLD Report v1 : Un hôte répond pour dire qu’il écoute un groupe multicast132
,MLD Done : Un hôte indique qu’il quitte un groupe multicast143
MLD Report v2 : Version améliorée du MLD Report (avec plusieurs groupes)151
,MLD Multicast Router Advertisement : Indique la présence d’un routeur multicast152
MLD Multicast Router Solicitation : Un routeur demande aux autres s’ils sont multicast routers153
MLD Multicast Router Termination : Un routeur annonce qu’il ne relaie plus le multicast
Smurf Attack
C'est une attaque assez sournoise. En français “Attaque par rebond” mais c'est moins drôle comme nom. C’est une attaque par amplification où un attaquant envoie des paquets ICMP Echo Request (ping) avec une IP source usurpée (celle de la victime) à une adresse de diffusion (broadcast), souvent 255.255.255.255 (broadcast limité) ou 224.0.0.1 (multicast “all hosts”). Tous les hôtes qui reçoivent ça répondent à l’IP usurpée, il y a saturation du réseau et DoS ciblé.
Il y a deux cas de figures.
On est sur un dédié, pas de réseau sous forme de bridge, on met ces lignes :
# Protection contre le Smurf Attack ip daddr 255.255.255.255 drop ip daddr 224.0.0.1 drop
Mais si on utilise un bridge réseau (cas classique quand on a un hyperviseur), on risque de mettre un sacré boxon dans le réseau interne, et il faut être plus fin.
La solution basique consiste à utiliser le nom du réseau (via un define IFACES_LAN = { “lo”, “xenbr0” }
3) en début de table en général) et à utiliser la règle suivante : on bloque tout broadcast qui ne vient pas des interfaces internes, mais on autorise explicitement ARP4) et DHCP5) à fonctionner en interne sinon on va être coincé.
# Autoriser ARP (sinon les VMs se retrouvent isolées) meta iifname $IFACES_LAN ether type arp accept # Autoriser DHCP (indispensable avec des VMs clients DHCP) meta iifname $IFACES_LAN ip protocol udp udp dport { 67, 68 } accept # Bloquer le broadcast limité uniquement sur le WAN ip daddr { 255.255.255.255, 224.0.0.1 } iifname != $IFACES_LAN drop
L'adaptation des règles dépend très fortement du type de pont. N'utilisez cette règle qu'en vérifiant son effet.
Pour ipv6, ça a encore l'air d'être une autre affaire. Je ne maitrise pas.
Différence entre saddr et daddr
- saddr : “Source address” soit l'ip source, permet de filtrer en fonction de l'expéditeur.
- daddr : “Destination address” soit l'ip de destination, permet de filtrer en fonction du destinataire.
Autres éléments de syntaxe
Je copie des “hook” pour comprendre ce que chaque petit morceau fait.
Il est possible de déclarer en une ligne plusieurs redirections du même type. Par exemple si suivant les ports tcp redirigent vers diverses ip internes (ou externes !), ça donne ça (exemple issu de /usr/share/doc/nftables/examples/nat.nft
:
table ip nat { chain prerouting { type nat hook prerouting priority 0; #Thanks to nftables maps, if you have a previous iptables NAT (destination NAT) ruleset like this: # % iptables -t nat -A PREROUTING -p tcp --dport 1000 -j DNAT --to-destination 1.1.1.1:1234 # % iptables -t nat -A PREROUTING -p udp --dport 2000 -j DNAT --to-destination 2.2.2.2:2345 # % iptables -t nat -A PREROUTING -p tcp --dport 3000 -j DNAT --to-destination 3.3.3.3:3456 # It can be easily translated to nftables in a single line: dnat tcp dport map { 1000 : 1.1.1.1, 2000 : 2.2.2.2, 3000 : 3.3.3.3} : tcp dport map { 1000 : 1234, 2000 : 2345, 3000 : 3456 } } chain postrouting { type nat hook postrouting priority 0; #Likewise, in iptables NAT (source NAT): # % iptables -t nat -A POSTROUTING -s 192.168.1.1 -j SNAT --to-source 1.1.1.1 # % iptables -t nat -A POSTROUTING -s 192.168.2.2 -j SNAT --to-source 2.2.2.2 # % iptables -t nat -A POSTROUTING -s 192.168.3.3 -j SNAT --to-source 3.3.3.3 # Translated to a nftables one-liner: snat ip saddr map { 192.168.1.1 : 1.1.1.1, 192.168.2.2 : 2.2.2.2, 192.168.3.3 : 3.3.3.3 } } }
On peut aussi définir des variables avec define
(au début du fichier plutôt, mais dans la table) et ensuite les appeler.
table inet firewall { define tcp_ports = {80, 443} chain inbound { type filter hook input priority 0; policy drop; tcp dport { $tcp_ports } ct state new accept } }
Modulariser : une ou plusieurs chaines/fichiers ?
Comme expliqué en intro, on peut donner n'importe quel nom à une chaine, mais il y a des conditions pour qu'elle soit exécutée.
- Une chaine est exécutée si elle a l'élément
type [x] hook [y]
- Ou si elle a été appelé depuis une autre chaine avec
jump machaine
.
Par exemple :
table inet firewall { chain inbound { type filter hook input priority 0; policy drop; jump machinssh } chain machinssh { counter comment "Compteur SSH" } }
Pourquoi faire “jump” et pas simplement tout mettre dans la même chaine ?
- Quand une règle doit être appelée plusieurs fois (par exemple pour input ET output)
- pour déclarer
- Pour clarifier les blocs et potentiellement n'intervenir que sur un bout. Cela pourrait être des fichiers séparés, modifiés par Ansible par exemple.
- Il suffit de commenter un “jump” pour désactiver tout ce qui ne va pas avec ;)
Exemple d'architecture avec des fichiers séparés :
/etc/nftables.d/ ├── 00-definitions.nft ├── 10-sets.nft ├── 20-input.nft ├── 30-fight.nft ├── 40-confperso.nft └── 99-main.nft
Le nftables.conf ressemblera alors à ceci :
- nftables.conf
#!/usr/sbin/nft -f flush ruleset include "/etc/nftables.d/00-definitions.nft" include "/etc/nftables.d/10-sets.nft" include "/etc/nftables.d/20-input.nft" include "/etc/nftables.d/30-fight.nft" include "/etc/nftables.d/40-confperso.nft"
Cela permet alors de ne recharger/modifier qu'un seul des fichiers. Ils peuvent même être générés dynamiquement (par des scripts, Fail2ban ou Reaction).
Ips martiennes
Quand j'ai lu dans une doc de pare-feu “pensez aussi aux plages privées et martiennes”, j'ai cru à une blague. Mais on dirait que non. Ce sont des adresses réservées à certains usages et qui viennent d'interfaces précises ou alors… c'est du spoofing. Donc, dans les règles nftables, on vérifie si l'usage de ces ip arrivent du bon endroit et sinon on interdit.
Plage IPv4 | Description |
---|---|
'0.0.0.0/8' | Adresses “This network” (source invalide) |
'10.0.0.0/8' | Adresse privées réseau interne (RFC1918) |
'100.64.0.0/10' | Carrier-grade NAT (RFC6598), non publique |
'127.0.0.0/8' | Loopback (localhost) |
'169.254.0.0/16' | Link-local (APIPA) |
'172.16.0.0/12' | Adresse privées réseau interne (RFC1918) |
'192.0.0.0/24' | IETF Protocol Assignments |
'192.0.2.0/24' | TEST-NET-1 (documentation et test) |
'192.88.99.0/24' | 6to4 Relay Anycast |
'192.168.0.0/16' | Adresse privées réseau interne (RFC1918) |
'198.18.0.0/15' | Benchmarking (RFC2544) |
'198.51.100.0/24' | TEST-NET-2 (documentation et test) |
'203.0.113.0/24' | TEST-NET-3 (documentation et test) |
'224.0.0.0/4' | Multicast |
'240.0.0.0/4' | Adresses réservées pour futur usage |
'255.255.255.255/32' | Broadcast limité |
Plage IPv6 | Description |
---|---|
'::1/128' | Loopback |
'::/128' | Adresse non spécifiée (unspecified) |
'::ffff:0:0/96' | IPv4-mapped IPv6 addresses |
'64:ff9b::/96' | IPv4/IPv6 translation (NAT64) |
'100::/64' | Discard prefix (RFC6666) |
'2001::/23' | IETF protocol assignments |
'2001:db8::/32' | Documentation prefix |
'2002::/16' | 6to4 tunneling |
'fc00::/7' | Unique local addresses (ULA) |
'fe80::/10' | Link-local addresses |
'ff00::/8' | Multicast |
Maintenant, on bloque quoi et quand ? Ça me donne mal au crâne, parce que bloquer le mauvais truc peut créer des bugs bizarres.
On commence par “ip a” pour voir les interfaces actives sur notre serveur, qu'on déclarera avec “define” dans nftables.
Un exemple sur un hyperviseur xen :
lo
(loopback) associé aux ip du type127.0.0.1
/::1
: cela ne doit concerner que le local.enp2s0
: l'interface physique, mais aucune ip directe ne transitera par là dans notre cas, cela sera via le bridge.xenbr0
: le bridge. Indique l'ip officielle de l'hyperviseur.vif1.0
,vif2.0
: interface des VM, aucun impact en terme d'ip par rapport au nftables de l'hyperviseur.
Exemples de fichiers suivant les cas
Sur hyperviseur exposé sur internet
C'est abondamment commenté donc ça devrait permettre de s'y retrouver.
Il faut évidement relire, adapter les ip et les ports.
J'ai mis une option : si l'hyperviseur est derrière une box, on peut permettre ssh en direct sur le réseau local (adresses en 192.168.1.*). Mais il ne faut pas permettre de le faire depuis les VM (10.0.0.*)…
Ça compile ne génère pas d'erreur avec sudo nft -cof nftables.conf
. Ça ne veux pas dire que tout va marcher. J'ai pas encore assez testé certaines des règles.
- /etc/nftables.conf
#!/usr/sbin/nft -f flush ruleset # Table sur le trafic de type input/output/forward. # De base on rejette tout, sauf ce qui est explicitement autorisé. # Séparer ipv4 et ipv6 simplifie le travail... table inet firewall { # ------------------------------ # Define : des trucs à déclarer une fois ici # ------------------------------ # Le nom de nos interfaces define IFACES_LAN = { "lo", "xenbr0" } define IFACES_WAN = { "enp2s0" } # ------------------------------ # SETS - alimenter des listes suivant des critères précis # ------------------------------ set allowlistsys { # Les ip autorisées à faire des trucs sur le réseau # Ip internes et ip fixes de sysadmin type ipv4_addr flags interval elements = { 109.190.202.194, 192.168.1.0/24 } } # Blocklist : ce qui est bloqué durablement. Alimenté par Reaction/Fail2ban. # Interval : permet de bloquer des plages d'ip. set blocklist_v4 { type ipv4_addr flags interval } set blocklist_v6 { type ipv6_addr flags interval } # blocage temporaire à court terme en cas d'excès, timeout court (10s). # Sert à "tamponner". Et à alimenter Blocklist au besoin. set tempblock_v4 { type ipv4_addr flags timeout timeout 10s size 1024 } set tempblock_v6 { type ipv6_addr flags timeout timeout 10s size 1024 } set martienne_v4 { # Adresses non-routables ou réservées, si ça ne vient pas du bon port, c'est une attaque. type ipv4_addr flags interval elements = { 0.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.0.0.0/24, 192.0.2.0/24, 192.88.99.0/24, 192.168.0.0/16, 198.18.0.0/15, 198.51.100.0/24, 203.0.113.0/24, 224.0.0.0/4, 240.0.0.0/4 } } set martienne_v6 { type ipv6_addr flags interval elements = { ::/128, ::1/128, ::ffff:0:0/96, 64:ff9b::/96, 100::/64, 2001::/23, 2001:db8::/32, 2002::/16, fc00::/7, fe80::/10, ff00::/8 } } # ------------------------------ # Autorisations explicites # TODO : Adapter au réel ;) # ------------------------------ chain confperso { # Autoriser SSH si : # - source = Bastion1 (externe). mettre son ip, c'est pas 101.1.1.1 # - OU source dans le LAN 192.168.1.0/24 (UNIQUEMENT quand on est derrière une box) # SSH ne sera sans doute pas sur le port 22, adapter... ip saddr @allowlistsys tcp dport 22 ct state new accept # Autoriser certains ports quand les services liés sont actifs, ici le web tcp dport { 80, 443 } ct state new accept # Proxmox : autoriser l’accès à l’interface web uniquement depuis certaines ip (fixes, perso) ip saddr @allowlistsys tcp dport 8006 accept tcp dport 8006 drop # Refuser tout le reste } # ------------------------------ # Règles génériques entrantes # ------------------------------ chain input_all { # Par défaut, on rejette tout à moins de suivre une des règles ci-dessous. type filter hook input priority 0; policy drop; # Vérifie si l'IP est bannie durablement ip saddr @blocklist_v4 drop ip6 saddr @blocklist_v6 drop # Ou dans la liste temporaire ip saddr @tempblock_v4 drop ip6 saddr @tempblock_v6 drop # Autoriser le trafic de paquets "etablished" et associés, supprimer les paquets non valides ct state vmap { established : accept, related : accept, invalid : drop } # Autorise le loopback iifname "lo" accept # Spécial ipv6 # Paquets ICMPv6 qui ne doivent pas être rejetés, voir https://tools.ietf.org/html/rfc4890#section-4.4.1 et https://wiki.nftables.org/wiki-nftables/index.php/Simple_ruleset_for_a_server ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-reply, echo-request, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, 148, 149 } accept # Protège contre certaines attaques de spoofing en s'assurant que ces messages viennent bien d’un hôte local. meta iifname $IFACES_LAN ip6 saddr fe80::/10 icmpv6 type { 130, 131, 132, 143, 151, 152, 153 } accept # Appelle les chaines utiles jump confperso jump fight } # ------------------------------ # Mesures pour limiter diverses attaques # ------------------------------ chain fight { # Empêcher le loopback reçu ailleurs que sur lo ip saddr 127.0.0.0/8 iifname != "lo" drop ip6 saddr ::1 iifname != "lo" drop # Bloquer les IP martiennes si elles ne viennent pas de LAN ip saddr @martienne_v4 iifname != $IFACES_LAN drop ip6 saddr @martienne_v6 iifname != $IFACES_LAN drop # Attention suivant le réseau local, il faut peut-être autoriser des ip explicitement (définir un set au besoin) # Autorise le ping avec une limite pour les abus / Protection contre ICMP Flood (Ping Flood) # Ping normal : 1/s. Attention des outils de monitorings peuvent être plus rapides (jusque 20/s). # Ping IPv4 ip protocol icmp icmp type echo-request limit rate 10/second accept ip protocol icmp icmp type echo-request limit rate over 10/second add @tempblock_v4 { ip saddr } counter log prefix "[TEMPBLOCK PING]" drop # Ping IPv6 ip6 nexthdr icmpv6 icmpv6 type echo-request limit rate 10/second accept ip6 nexthdr icmpv6 icmpv6 type echo-request limit rate over 10/second add @tempblock_v6 { ip6 saddr } counter log prefix "[TEMPBLOCK PING6]" drop # Protection contre UDP Flood ip protocol udp limit rate over 200/second add @tempblock_v4 { ip saddr } counter log prefix "[TEMPBLOCK UDP]" drop ip6 nexthdr udp limit rate over 200/second add @tempblock_v6 { ip6 saddr } counter log prefix "[TEMPBLOCK UDP6]" drop # Protection contre le DNS Query Flood ip protocol udp th dport 53 limit rate over 50/second add @tempblock_v4 { ip saddr } counter log prefix "[TEMPBLOCK DNS]" drop # Protection contre le SYN Flood, ACK Flood & RST Flood # Mais on ne rejette pas tout, faut bien que les serveurs puissent se saluer # Les attaques sur syn sont plus fréquentes, valeur entre 50 et 100, rst répond donc idem, # mais ack bénéficie de valeurs plus hautes (jusque 200) # Bref à adapter au trafic réel (=> monitoring !) tcp flags syn,rst limit rate over 100/second add @tempblock_v4 { ip saddr } counter log prefix "[TEMPBLOCK SYN]" drop tcp flags ack limit rate over 100/second add @tempblock_v4 { ip saddr } counter log prefix "[TEMPBLOCK ACK]" drop ip6 nexthdr tcp tcp flags syn,rst limit rate over 50/second add @tempblock_v6 { ip6 saddr } counter log prefix "[TEMPBLOCK SYN6]" drop ip6 nexthdr tcp tcp flags ack limit rate over 100/second add @tempblock_v6 { ip6 saddr } counter log prefix "[TEMPBLOCK ACK6]" drop # Protection contre le Smurf Attack # Autoriser ARP (sinon les VMs se retrouvent isolées) meta iifname $IFACES_LAN ether type arp accept # Autoriser DHCP (indispensable avec des VMs clients DHCP) meta iifname $IFACES_LAN ip protocol udp udp dport { 67, 68 } accept # Bloquer le broadcast limité uniquement sur le WAN ip daddr { 255.255.255.255, 224.0.0.1 } iifname != $IFACES_LAN drop # Protection contre le SMTP/Email Bombing ip protocol tcp th dport 25 limit rate over 10/second add @tempblock_v4 { ip saddr } counter log prefix "[TEMPBLOCK SMTP]" drop # Protection contre les botnets sur les ports fréquemment ciblés (peuvent servir de honeypot) # tcp : telnet (23, 2323), UPnP (7547) # udp : Memcached (11211), SSDP (1900), LDAP (389), NTP (123), IoT (48101) ip protocol tcp th dport { 23, 2323, 7547 } add @tempblock_v4 { ip saddr } counter log prefix "[TEMPBLOCK BADPORT]" drop ip protocol udp th dport { 11211, 1900, 389, 123, 48101 } add @tempblock_v4 { ip saddr } counter log prefix "[TEMPBLOCK BADPORT]" drop # Bloquer les scans de ports en cas de tentative de connexion rapide sur des ports multiples ip protocol tcp limit rate over 50/second add @tempblock_v4 { ip saddr } counter log prefix "[TEMPBLOCK SCAN]" drop ip protocol udp limit rate over 50/second add @tempblock_v4 { ip saddr } counter log prefix "[TEMPBLOCK SCAN]" drop } # ------------------------------ # Règles génériques sortantes # ------------------------------ chain output { # Par défaut, on rejette tout à moins de suivre une des règles ci-dessous. type filter hook output priority 0; policy drop; # Autoriser le trafic de paquets "etablished" et associés, supprimer les paquets non valides ct state established,related accept # Autorise le loopback sortant oifname "lo" accept # Autorise le ping sortant ip protocol icmp accept ip6 nexthdr icmpv6 accept # Autoriser d'aller sur internet et de contacter les dns (domain) tcp dport { http, https } accept udp dport domain accept tcp dport domain accept # Autoriser msmtp à envoyer ses mails tcp dport { 25, 465, 587 } ct state new,established accept # Autoriser NTP udp dport { 53, 123 } ct state new,established accept # Log de ce qui est bloqué log prefix "[NFT OUTPUT DROP] " flags all drop } # ------------------------------ # Règles génériques "forward" (ce qui circule vers les autres adresses routées) # ------------------------------ chain forward { # Par défaut, on rejette tout à moins de suivre une des règles ci-dessous. type filter hook forward priority 0; policy drop; # Autoriser le trafic de paquets "etablished" et associés, supprimer les paquets non valides ct state established accept } }
Ports suivant les services
Si serveur mail : remplacer
# Autoriser certains ports quand les services liés sont actifs (donc à adapter), ici le web et ssh tcp dport { 22, 80, 443 } ct state new accept
Par
# Autoriser certains ports quand les services liés sont actifs : web (80/443), ssh (22), # smtp (25), SMTP TLS (587), IMAPS (993), Sieve (4190) tcp dport { 22, 80, 443, 25, 587, 993, 4190 } ct state new accept
Todo
Faire plus modulaire. Adapter quand c'est pour une VM, un dédié… Mais la config de l'hyperviseur doit être une bonne base
Vérification et mise en place
Pour vérifier ce fichier une fois modifié, avant de le mettre en place :
sudo nft -cof /etc/nftables.conf
La commande va donner des informations si y'a besoin.
- -c : vérifier s'il y a des erreurs syntaxes
- -o : optimiser les règles
- -f : indique le fichier à lire (ici l'emplacement par défaut sur debian).
Une fois qu'on a configuré son fichier :
sudo systemctl enable nftables.service sudo service nftables start
Pour lister les règles en place :
sudo nft list ruleset
Pour recharger le fichier de configuration :
nft -f /etc/nftables.conf
Admirer en temps réel les bans ? Pour admirer le contenu d'un set (par exemple tempblock_v4), c'est
sudo watch -n 1 'nft list set inet firewall tempblock_v4'
Et pour les logs, si on a activé les logs lisibles (sinon, débrouillez vous avec journalctl) :
tail -f /var/log/kern.log
Sources
- https://wiki.nftables.org et en particulier