# cat /etc/nftables.conf !/usr/sbin/nft -f flush ruleset # On crée une première table inet, ce qui signifie qu'on va traiter des paquets réseaux table inet my_table { # On donne un alias sur deux set d'IP (v4 et v6) set ipv4_local_addr { # Le type de l'IP (v4 ou v6) type ipv4_addr # Ici ce n'est pas pertinent, mais cela permet d'ajouter des IPs au fil de l'eau flags interval elements = { 10.0.10.200, 169.254.0.200, 80.67.181.0, 62.112.29.67, 127.0.0.0/8 } } set ipv6_local_addr { type ipv6_addr flags interval elements = { 2001:913:1000:10::200, fe80:cafe::200, 2001:913:1000:100::200, 2a00:1528:3201:5::22, ::1 } } # Les chain sont des endroits particuliers où on va pouvoir appliquer certains types de règles. C'est toute la partie entre ingress et egress. Le cas est atypique dans Neutrinet : on dit que les IP qi sont les nôtres ne sont pas trackées. chain my_prerouting { # On est dans la partie prerouting et on accepte tout par défaut type filter hook prerouting priority raw; policy accept; # Les IP internes à Neutrinet on les acceptes et on s'arrête ip daddr @ipv4_local_addr accept ip6 daddr @ipv6_local_addr accept # Tout le reste on ne les track pas # Cela veut dire qu'on ne fait que du forward sans appliquer de règles de firewall, et donc le kernel fait vraiment le minimum (on désactive en quelque sorte netfilter) # La raison, c'est que cette VM doit faire passe-plat pour les IP de nos membres, donc on désactive un maximum de trucs notrack } chain my_input { # On est dans la partie input et par défaut on drop tout type filter hook input priority 0; policy drop; # iif veut dire "input interface" (pour "output interface", c'est oif). Si elle n'existe pas, nftables va râler. Il existe une directive qui permet d'utiliser iif ou oif si l'interface n'existe pas encore. # lo et routing ce sont les interfaces de loopback, donc on accepte tout car c'est la communication interne à la VM iif lo accept comment "Accept any localhost traffic" iif routing accept comment "Accept any localhost traffic" # ct = connection tracking # on drop tout ce qui est invalide ct state invalid drop comment "Drop invalid connections" # et on accepte déjà toutes les connexions qu'on avait déjà commencé et qui sont encore en cours (c'est une optimisation) ct state established,related accept comment "Accept traffic originated from us" # Limit ping requests. Il y a plein de types dans icmp, et nous on veut juste permettre de pinguer les IP de la VM (puisqu'on est dans input) ip protocol icmp icmp type echo-request limit rate over 1/second burst 5 packets drop ip6 nexthdr icmpv6 icmpv6 type echo-request limit rate over 1/second burst 5 packets drop # On enlève plein de paquets qu'on n'est pas censés recevoir et qui doivent donc être considérés comme invalides # Drop all fragments. ip frag-off & 0x1fff != 0 counter drop # Force SYN checks. tcp flags & (fin|syn|rst|ack) != syn ct state new counter drop # Drop XMAS packets. tcp flags & (fin|syn|rst|psh|ack|urg) == fin|syn|rst|psh|ack|urg counter drop # Drop NULL packets. tcp flags & (fin|syn|rst|psh|ack|urg) == 0x0 counter drop # On accepte plein de grands classiques du réseau # Allow certain inbound ICMP types (ping, traceroute). # With these allowed you are a good network citizen. ip protocol icmp icmp type { destination-unreachable, echo-reply, echo-request, source-quench, time-exceeded } accept # Without the nd-* ones ipv6 will not work. ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, echo-reply, echo-request, nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert, packet-too-big, parameter-problem, time-exceeded } accept # Allow IPv6 multicast listener discovery on link-local ip6 nexthdr icmpv6 icmpv6 type {mld-listener-query, mld-listener-report, mld-listener-reduction, mld2-listener-report } ip6 saddr fe80::/10 accept # Cette règle ne marche pas, mais l'idée était de permettre en UDP certains ports pour le traceroute (sinon on a des petites étoiles et c'est triste) udp dport 33434-33523 accept comment "Accept Tracroute" # On permet à librenms d'accéder à la VM en snmp # saddr = source addresse # dport = destination port # Ici snmp est un protocole connu de nftables, mais sinon il faut mettre le numéro du port udp dport snmp ip saddr 10.0.11.109 accept comment "Accept SNMP on port snmp" udp dport snmp ip6 saddr 2001:913:1000:11::109 accept comment "Accept SNMP on port snmp" tcp dport ssh accept comment "Accept SSH on port 22" # iifname = c'est la fameuse directive qui permet de dire que si l'interface n'existe pas, on peut quand même la définir dans nftables (qui va créer la règle dans le kernel) # Donc ici on accepte sur certaines interfaces de parler en BGP sur certaines ip destination # Cela permet de limiter le plus possible qui peut nous parler en BGP # Les IP ce sont celles portées par la VM, c'est pour ça que ce sont celles de destination tcp dport bgp iifname "ens20" ip6 daddr {2a00:1528:3201:5::22} accept comment "Accept BGP on port 179" tcp dport bgp iifname "ens20" ip daddr {62.112.29.67} accept comment "Accept BGP on port 179" tcp dport bgp iifname "ens19" ip6 daddr {fe80:cafe::200} accept comment "Accept BGP on port 179" udp dport 3784 iifname "ens20" ip6 daddr {2a00:1528:3201:5::22} accept comment "Accept BFD on port 3784" udp dport 3784 iifname "ens20" ip daddr {62.112.29.67} accept comment "Accept BFD on port 3784" udp dport 3784 iifname "ens19" ip6 daddr {fe80:cafe::200} accept comment "Accept BFD on port 3784" # Customization des logs et permet d'avoir un compteur de paquets log prefix "[nftables] Input Denied: " counter drop } chain my_forward { # On est dans la partie forward, qui est après le routing. On drop tout par défaut. type filter hook forward priority 0; policy drop; # Ici on utilise routing parce qu'on est dans un VRF (Virtual routing forward, voir glossaire). Sinon, "routing" est une interface de loopback comme vu plus haut. # Normalement on aurait mis des interfaces physiques (ens19 et ens20 pour l'input interface) # Mais ici, on reste dans le VRF et donc on est toujours dans la loopback routing iifname "routing" oifname "ens19" accept comment "Accept Internet to Neutrinet" iifname "routing" oifname "ens20" accept comment "Accept Neutrinet to Internet" log prefix "[nftables] Forward Denied: " counter drop } chain my_output { # On est dans la partie output et on accepte tout. type filter hook output priority 0; policy accept; } }