Nftables en firewall en remplacement d'iptables

Bonjour,

Je viens de me porter acquéreur du FriendlyElec R6C : cela ressemble à Raspeberry 5 avec 8Go de RAM + 32 emmc + une interface M2 PCIe pour y mettre un SSD et surtout 2 interfaces RJ45 ce qui est parfait pour faire, entre autre, un routeur.

Ayant lu à plusieurs reprises que nftables était dessiné à remplacer iptables, je me suis lancé dans l’exercice de transposer des règles iptables existantes, mises en place de la cadre de clients OpenVPN par exemple, en nftables.

Actuellement chez Free, j’envisage de passer ma Freebox en mode bridge, mais avant de traiter le sujet spécifique à la Freebox : ipv6, tunnelling ipv4 dans ipv6, next hop, router advertissement et autres joyeusetés, je souhaitais dans un premier temps voir avec vos avis éclairés, la construction des règles ci-après.

J’avoue au passage d’être à un niveau juste au-dessus de la quiche et de la buse dans ce domaine, donc tout bon conseil sera le bienvenu.

Quelques explications / indications :

  • wan0 est l’interface vers internet

  • lan0 est l’interface vers le réseau local

  • Mise en place de règles anti-DDOS reprises de règles iptables

  • On accepte les connexions entrantes pour de l’https et d’autres ports qui seront pour certains redirigés vers un haproxy et d’autres vers des @ip avec port

  • On masque le NetBios et les adresses du lan en sortie

  • On ne filtre rien en sortie pas top en termes de sécurité mais la mise en place d’un proxy n’est pas à l’ordre du jour d’une part et j’ai aussi par ailleurs des services de téléphonie sur @IP, donc avant de filter en sortie il y aurait un taff d’inventaire à faire (…)

Rien de plus pour le moment, je voudrais juste passer ma Freebox et que le R6C fasse le job de filtrage et de routage :smirk:

#!/usr/sbin/nft -f
flush ruleset

add table filter
table net filter {

  chain input {
    type filter hook input priority 0; # Hook for incoming packets

    # Accept established and related connections
    ct state established,related accept

    # Accept loopback interface traffic
    iifname "lo" accept

    # ICMP rate limiting to prevent DDoS
    icmp type echo-request limit rate 10/second burst 5 packets accept

    # Filtrer les dapeaux TCP bidons
    tcp flags & (fin|syn|rst|psh|ack|urg) == fin|syn drop
    tcp flags & (fin|syn|rst|psh|ack|urg) == syn|rst drop
    tcp flags & (fin|syn|rst|psh|ack|urg) == fin|rst drop
    tcp flags & (fin|syn|rst|psh|ack|urg) == fin|ack drop
    tcp flags & (fin|syn|rst|psh|ack|urg) == urg drop
    tcp flags & (fin|syn|rst|psh|ack|urg) == psh drop
    tcp flags & (fin|syn|rst|psh|ack|urg) == none drop

    # Limite le nombre de nouvelles connexions TCP
    tcp flags & (fin|syn|rst|psh|ack|urg) == syn ct state new limit rate 60/second burst 20 accept
    tcp flags & (fin|syn|rst|psh|ack|urg) == syn ct state new drop

    # Limiter les TCP RST
    tcp flags & (fin|syn|rst|psh|ack|urg) == rst limit rate 2/second burst 2 accept
    tcp flags & (fin|syn|rst|psh|ack|urg) == rst drop        


    # Limite le nombre de nouvelles connexions TCP
    tcp flags & (fin|syn|rst|psh|ack|urg) == syn ct state new limit rate 60/second burst 20 accept
    tcp flags & (fin|syn|rst|psh|ack|urg) == syn ct state new drop

    # Limiter les TCP RST
    tcp flags & (fin|syn|rst|psh|ack|urg) == rst limit rate 2/second burst 2 accept
    tcp flags & (fin|syn|rst|psh|ack|urg) == rst drop

    # Bloquer les connexions entrantes sur wan0 avec des @ip invalides
    iifname "wan0" ip saddr { 224.0.0.0/4, 192.168.0.0/16, 192.0.2.0/24, 172.16.0.0/12, 10.0.0.0/8, 169.254.0.0/16 } drop

    # Atténuation des attaques SYN Flood (méthodes avec SYNPROXY)
    tcp flags & (fin|syn|rst|psh|ack|urg) == syn ct state new tcp option maxseg size set 1460 hashseed mask 0xff,0x00 counter packets 0 bytes 0 accept
    tcp flags & (fin|syn|rst|psh|ack|urg) == syn ct state new tcp option maxseg size set 1460 hashseed mask 0xff,0x00 tcp flags & (fin|syn|rst|psh|ack|urg) == syn ct state new tcp flags & (fin|syn|rst|psh|ack|urg) == syn hashseed 0x00,0x00 counter packets 0 bytes 0 accept

    # Rejeter les paquets ct state invalides et logguer
    ct state invalid log prefix "nftables_ct.state.invalid : " drop

    # Rejeter les paquets non autorisés et logguer
    log prefix "nftables_Dropped.Packet : " drop
  }

  chain prerouting {
    type nat hook prerouting priority 0; # Hook for incoming packets

    # Redirection des ports
    tcp dport { 443, 65327 } dnat to 192.168.1.170
    tdp dport 1413 dnat to 192.168.1.16:22
    tcp dport 1405 dnat to 192.168.1.15:22

    # Log accepted HTTPS connections
    tcp dport 443 accept log prefix "nftables_https: "

    # log ssh connexions
    tcp dport {1413, 1405} accept log prefix "nftables_ssh: "

    # log sftp connexions
    tcp dport 65327 accept log prefix "nftables_sftp: "

  }

  chain forward {
      type filter hook forward priority 0; policy accept;

      # Autoriser le trafic sortant du réseau local vers Internet
      ip saddr 192.168.1.0/24 oif wan0 ct state new,related,established accept

  }

  chain output {
    type filter hook output priority 0; policy accept;

    # Bloquer les paquets sortants NetBIOS
    tcp dport { 137, 138, 139 } drop
    udp dport { 137, 138, 139 } drop

  }

  chain postrouting {
    type nat hook postrouting priority 0; policy accept;
    ip saddr 192.168.1.0/24 oif "wan0" masquerade
  }

}

Fait un groupe RFC1918 et tu interdit le groupe entier.
Car les réseaux invalides en provenance de l’extérieur au LAN sont 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12.

Autre remarque toujours loguer les paquets DROP ou REJECT.

Freebox en bridge, laquelle? Car suivant le modèle ce n’est pas la même conséquence. Dans tous les cas, tu vas perfdre la TV il me semble (si tu la regardes :wink: )

Ok, merci pour tes conseils car effectivement je m’apprétais à blacklister la plage d’adresse pouvant servir au multicast pouvant serveur au streaming : la plage 224.0.0.0/4. Bien vu ! :grin:

En revanche et si j’en comprends sa spécificité, la classe d’adresse 169.254.0.0/16 semble être définie comme suit : « utilisée pour l’adressage automatique du protocole IP (APIPA) lorsque le serveur DHCP n’est pas disponible (…) Cependant, cette plage d’adresses n’est ni routable sur Internet ni ailleurs »

D’où ma question : pourquoi devrais-je garder cette plage active ? Juste pour s’assurer de bien recevoir mes adresses IPV4 et IPV6 de la part de Free ?

Concernant ma box, j’ai une Freebox PoP et le player qui va avec. J’ouvrirai un nouveau post sur le sujet car les informations que j’ai pu capter sur ce site ou sur lafibre.info sont :

  • d’une part assez anciennes,
  • et principalement avec des exemples / confs portés par OpenWrt

Pour ma part je n’ai pas pris l’option OpenWrt car, outre le fait que je préfère gérer moi-même les composants et leur configuration, je souhaitais installer une couche de virtualisation (avec 8Go de RAM + le SSD en PCIe, c’est assez confortable …;-)) me permettant d’héberger quelques services dockerisés.

De même, je n’ai pas pris l’option OpenMediaVault pour les mêmes raisons qu’OpenWrt car dans le fond, je préfère de loin partir sur une base Debian à laquelle j’ajouterais les services qui me conviennent.

Mais bon, je ne critique pas, disons que c’est aussi par expérience ; être dépendant d’une solution tout en un, m’a déjà causé quelques déboires notamment quand la solution en question ne supporte pas (complètement) une architecture matérielle : il faut attendre la prochaine version (…) :sleepy: ou quand la nouvelle version apporte des nouveautés attendues tout en n’en cassant d’autres :woozy_face:

Bref, avec une solution montée soit même on a toujours la possibilité de revenir en arrière sur un composant ou de le bloquer sa MAJ. Ce qui est du reste le cas de mon R6C qui ne supporte, pour le moment du moins, un kernel au-delà du nanopi-r6c 6.7.0-rc5-5-arm64.

Mais bon, je disgresse ! :sweat_smile:

Par defaut je considère tout flux extérieur interdit.
Seul les flux qui me sont utiles sont autorisés (SSH par exemple, certain type de flux ICMP).

L’idée est de travailler en mode white list et non blzaklist.
En whitelist cela signifie que tout est bloqué, sauf ce qui est autorisé. Et il est plus facile de savoir ce qu’on a besoin que ce qu’on a pas besoin.

Alors qu’en blacklist tout est autorisé sauf ce qui est interdit. IL est donc nécessaire de savoir tout ce qui devrait interdit ce qui est nettement plus compliqué.

Après je ne connais pas cette box en particulier. Je suis sous Orange dont la livebox est dans un tiroir depuis 6 ans, remplacée par un pc industriel fanless sous OPNSense.

La config initiale quelque peu modifiée car : erreur de syntaxe lors du lancement de certaines règles.

Prochaine étape test en mode bridge avec la Freebox !

flush ruleset
add table filter

table inet filter {

  set my_ssh_meter {
                type ipv4_addr
                size 65535
                flags dynamic
      }

  
  chain input {
type filter hook input priority 0; # Hook for incoming packets

# Accept loopback interface traffic
iifname "lo" accept

# Accept established and related connections
ct state established, related accept
ct state invalid limit rate 4/second burst 5 packets  log prefix "NFTables invalid_input :" drop

# ICMP rate limiting to prevent DDoS
icmp type echo-request limit rate 10/second burst 5 packets accept

# Filtrer les dapeaux TCP bidons
tcp flags & (fin|syn|rst|psh|ack|urg) == fin|syn log prefix "NFTables drop tcp flags & (fin|syn|rst|psh|ack|urg) == fin|syn : " drop
tcp flags & (fin|syn|rst|psh|ack|urg) == syn|rst log prefix "NFTables drop tcp flags & (fin|syn|rst|psh|ack|urg) == syn|rst : " drop
tcp flags & (fin|syn|rst|psh|ack|urg) == fin|ack log prefix "NFTables drop tcp flags & (fin|syn|rst|psh|ack|urg) == fin|ack : " drop
tcp flags & (fin|syn|rst|psh|ack|urg) == urg log prefix "NFTables drop tcp flags & (fin|syn|rst|psh|ack|urg) == urg : " drop
tcp flags & (fin|syn|rst|psh|ack|urg) == psh log prefix "NFTables drop tcp flags & (fin|syn|rst|psh|ack|urg) == psh : " drop
tcp flags & (fin|syn|rst|psh|ack|urg) == 0   log prefix "NFTables drop tcp flags & (fin|syn|rst|psh|ack|urg) == 0 : " drop

tcp dport 22 ct state new add @my_ssh_meter { ip saddr limit rate 10/second } accept
tcp dport 22 ct state new log prefix "NFTables ssh > 10 sec. :" drop

# Limite le nombre de nouvelles connexions TCP
tcp flags & (fin|syn|rst|psh|ack|urg) == syn ct state new limit rate 60/second accept
tcp flags & (fin|syn|rst|psh|ack|urg) == syn ct state new log prefix "nftables drop limit new cnx : " drop
 
# Limiter les TCP RST
tcp flags & (fin|syn|rst|psh|ack|urg) == rst limit rate 2/second burst 2 packets  log prefix "NFTables invalid_input :" drop

# Bloquer les connexions entrantes sur wan0 avec des @ip invalides
iifname "wan0" ip saddr { 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8, 169.254.0.0/16 } log prefix "NFTables drop RFC1918 : " drop 

# Rejeter les paquets non autorisés et logguer
log prefix "NFTables drop INPUT : " drop

  }

  chain forward {
  type filter hook forward priority 0; policy accept;

  # Autoriser le trafic sortant du réseau local vers Internet
  ip saddr 192.168.1.0/24 oif wan0 ct state new,related,established accept
  }

  chain output {
type filter hook output priority 0; policy accept;

# Bloquer les paquets sortants NetBIOS
tcp dport { 137, 138, 139 } log prefix "nftables drop TCP NetBios " drop
udp dport { 137, 138, 139 } log prefix "nftables drop UDP NetBios " drop
  } 

  chain postrouting {
type nat hook postrouting priority 0; policy accept;
ip saddr 192.168.1.0/24 oif "wan0" masquerade
  }

}

table ip filter {
 
  chain prerouting {
type nat hook prerouting priority 0; # Hook for incoming packets

# Redirection des ports
tcp dport { 443, 65327 } dnat to 192.168.1.170
tcp dport 1413 dnat to 192.168.1.16:22
tcp dport 1405 dnat to 192.168.1.15:22

# Log accepted HTTPS connections
tcp dport 443 log prefix "nftables accept https: " accept 

# log ssh connexions
tcp dport {1213, 1705} log prefix "nftables accept ssh: " accept

# log sftp connexions
tcp dport 65327 log prefix "nftables accept sftp: " accept

}

}

Actuellement ça marche avec ton wan0 connecté à la freebox?
Quelle est son adresse?

Non, pas connectée à la Freebox en mode bridge pour le moment. Disons qu’avant de passer le Frebbox en mode bridge, j’ai souhaité tester ces règles via un sudo nft -f my_rules, et j’ai vu des erreurs apparaître. Erreurs que j’ai traitées une par une.

J’ai aussi apporté des modifications dans le fichier /etc/systctl.conf pour les problématiques syn flood, que je publierai plus tard.

De même, j’ai installé le service radvd et modifié le fichier /etc/radvd.conf notamment pour lui indiquer la valeur du « Next Hop » de la Freebox pour l’interface lan0.

Théoriquement, je ne devrais pas avoir besoin de mettre en œuvre l’IPv6 sur mon Lan, la FreePlayer devrait, comme aujourd’hui du reste, continuer à « capter » ce dont elle a besoin. Mais bon entre la théorie et la pratique… :thinking: Aujourd’hui, je n’utilise pas le DHCP de la FreeBox et mon dnsmasq ne distribue pas d’adresses en IPv6, ce qui n’empêche pas la FreePlayer de parfaitement fonctionner

Si tout va bien, je devrais être en mesure de tester tout ça ce WE, car j’ai des personnes qui bossent avec de la téléphonie sur @IP OVH et autres blagues du même genre (…) ce qui fait je ne puis me permettre du tout saboter en pleine journée ;-))

je ne v ois pas le rapport avec la telephonie sur IP OVH?

Concernant la configuration actuelle : aucun tu as raison, je dis simplement que je ne peux pas faire de tests dans la journée pour passer ma box en Bridge et couper le service.

Ni plus, ni moins :grin: