#!/usr/sbin/nft -f # ============================================================== # nftables.conf — server-mv # # Substitui a parte de firewall do rc.local antigo (iptables/ip6tables, # antes espalhada entre rc.local e test.sh). Regras funcionalmente # equivalentes, com os bugs já corrigidos: # - sem "-j DROP" solto no meio da chain (o catch-all é só a policy) # - sem duplicação de regra de porta única + multiport # - anti-spoof de 192.168.0.0/16 restrito à interface WAN # - chain PORTSCAN removida (nunca era populada, sempre morta) # # Vantagens sobre iptables/ip6tables para este caso: # - um ruleset só cobre IPv4 e IPv6 (table inet), então não existe # mais o risco de esquecer de espelhar uma regra do v4 pro v6 # - "flush ruleset" no topo faz o carregamento ser atômico: se o # arquivo tiver erro de sintaxe, o `nft -f` falha ANTES de aplicar # qualquer coisa — você nunca fica com um firewall pela metade # (iptables -F seguido de regras que podem falhar no meio é o # oposto disso) # - meters (rate-limit por IP de origem) substituem o módulo # "recent" do iptables com sintaxe mais legível # # Aplicar: # nft -c -f nftables.conf # valida sintaxe sem aplicar # nft -f nftables.conf # aplica # systemctl enable --now nftables # persistência no boot # # Depois de migrar, pare de carregar iptables/ip6tables no boot # (remova essa parte do rc.local — ver rc.local.new) para não ter # duas ferramentas de firewall competindo pelo mesmo netfilter. # ============================================================== flush ruleset table inet filter { # --- Meters (rate limit com estado por IP de origem) --- # (declarados via "meter" inline nas regras que os usam, mais abaixo) chain input { type filter hook input priority filter; policy drop; iifname "lo" accept ct state established,related accept # ICMP/ICMPv6: 2º maior volume medido no host, avaliado cedo # de propósito (dados reais: ~66k pacotes/dia, ficava por # último na versão antiga em iptables). ip protocol icmp jump icmpv4_in meta l4proto icmpv6 jump icmpv6_in # Pacotes TCP malformados / scanners óbvios tcp flags & (fin|syn|rst|psh|ack|urg) == (fin|syn|rst|psh|ack|urg) \ meter bad_packets { ip saddr timeout 5m limit rate over 5/minute } drop tcp flags & (fin|syn|rst|psh|ack|urg) == 0x0 \ meter bad_packets6 { ip6 saddr timeout 5m limit rate over 5/minute } drop ip frag-off & 0x1fff != 0 drop # fragmentos IPv4 (equivalente a "iptables -f") # Anti-spoof — só faz sentido vindo da WAN iifname "enp0s6" ip saddr 192.168.0.0/16 drop iifname "enp0s6" ip6 saddr fe80::/10 drop iifname "enp0s6" ip6 saddr fd00::/8 drop ip6 daddr ff00::/8 drop # SYN flood (limite global, equivalente ao antigo limit 10/sec burst 24) tcp flags syn limit rate 10/second burst 24 packets accept tcp flags syn drop # Rate limit de conexões novas na porta SSH (8117): no máx. 3 # tentativas novas por IP a cada 60s tcp dport 8117 ct state new \ meter ssh_meter { ip saddr timeout 60s limit rate 3/minute } accept tcp dport 8117 ct state new meter ssh_meter6 { ip6 saddr timeout 60s limit rate 3/minute } accept tcp dport 8117 ct state new drop # Túneis / VPN iifname "wg0" accept iifname "tun0" accept udp dport 1194 accept # Serviços expostos na WAN iifname "enp0s6" tcp dport { 80, 443 } accept iifname "enp0s6" udp dport { 32100, 32101, 32110, 32400 } accept # DHCPv6 client udp dport 546 accept } chain forward { type filter hook forward priority filter; policy accept; ct state established,related accept iifname "tun0" accept iifname "wg0" accept ip daddr 10.15.0.45 tcp dport { 80, 443 } accept ip6 daddr 2001:db8:15::45 tcp dport { 80, 443 } accept } chain output { type filter hook output priority filter; policy accept; } chain icmpv4_in { icmp type echo-reply accept icmp type echo-request limit rate 1/second burst 3 packets accept icmp type echo-request drop drop } chain icmpv6_in { icmpv6 type destination-unreachable accept icmpv6 type packet-too-big limit rate 5/minute accept icmpv6 type time-exceeded limit rate 5/minute accept icmpv6 type echo-request limit rate 1/second burst 3 packets accept icmpv6 type echo-request drop icmpv6 type echo-reply accept icmpv6 type nd-router-solicit accept icmpv6 type nd-router-advert accept icmpv6 type nd-neighbor-solicit accept icmpv6 type nd-neighbor-advert accept drop } } table ip nat { chain prerouting { type nat hook prerouting priority dstnat; iifname "enp0s6" tcp dport 80 dnat to 10.15.0.45:80 iifname "enp0s6" tcp dport 443 dnat to 10.15.0.45:443 } chain postrouting { type nat hook postrouting priority srcnat; ip saddr 10.15.0.0/16 masquerade ip saddr 172.17.0.0/16 masquerade oifname "wg3" masquerade # Hairpin NAT (clientes locais acessando o IP público) oifname "enp0s6" ip daddr 10.15.0.45 tcp dport { 80, 443 } masquerade } } table ip6 nat { chain prerouting { type nat hook prerouting priority dstnat; iifname "enp0s6" tcp dport 80 dnat to [2001:db8:15::45]:80 iifname "enp0s6" tcp dport 443 dnat to [2001:db8:15::45]:443 } chain postrouting { type nat hook postrouting priority srcnat; ip6 saddr 2001:db8:15::/48 masquerade oifname "enp0s6" ip6 daddr 2001:db8:15::45 tcp dport { 80, 443 } masquerade } }