Dia 28: Firewall com nftables — Regras, Sets, Chains e NAT
O nftables é o sucessor oficial do iptables no kernel Linux, disponível por predefinição desde o kernel 3.13 e adoptado como backend principal em Debian, Ubuntu, RHEL, Fedora e Arch Linux. Este artigo explora em profundidade a sintaxe, sets, chains, NAT, logging e optimização — tudo o que precisas para substituir o iptables com confiança.
ℹ O nftables unifica iptables, ip6tables, arptables e ebtables numa única ferramenta — sintaxe mais limpa e performance superior.
Neste artigo
- 1. Introdução ao nftables
- 2. Sintaxe e Estrutura
- 3. Sets e Maps
- 4. Chains e Prioridades
- 5. NAT com nftables
- 6. Logging e Optimização
- 7. Erros Comuns e Lista de Verificação
1. Introdução ao nftables
O nftables foi desenhado para resolver as limitações do iptables: sintaxe fragmentada (iptables, ip6tables, arptables, ebtables — cada um um binário separado), impossibilidade de reordenar regras sem remover e re-adicionar, falta de estruturas de dados nativas (sets, maps) e performance sub-óptima em conjunto de regras extensas.
O kernel Linux 5.15+ já não carrega os módulos de compatibilidade do iptables por predefinição em muitas distribuições. O comando iptables-translate converte regras antigas para a sintaxe nftables, facilitando a migração.
Arquitectura: Netfilter + nf_tables
O nftables assenta no subsistema Netfilter do kernel, que fornece os hooks de rede (prerouting, input, forward, saída, postrouting). A diferença é que o nftables usa uma máquina virtual byte-code no kernel (nf_tables) em vez dos matches/targets compilados do iptables. Isto significa que novas funcionalidades não exigem compilar módulos do kernel — basta actualizar o utilitário nft no espaço de utilizador.
| Feature | iptables | nftables |
|---|---|---|
| Famílias | Separadas (ip/ip6/arp/eb) | Unificadas (inet/ip/ip6/arp/bridge/netdev) |
| Sets/Maps | ipset (externo) | Nativo na linguagem |
| Reordenar regras | Não (delete + insert) | Sim (insert at position) |
| Performance | Linear em chains longas | Sets com lookup O(1) ou O(log n) |
| Sintaxe | -extensões por match | Linguagem consistente |
Para mais detalhes sobre a arquitectura Netfilter, consulta a wiki oficial do nftables ou o guia do Arch Wiki sobre nftables.
2. Sintaxe e Estrutura
A estrutura do nftables segue uma hierarquia clara: table → chain → rule. Uma tabela pertence a uma família (inet, ip, ip6, arp, bridge, netdev), contém chains, e cada chain contém regras.
Famílias disponíveis
| Família | Âmbito |
|---|---|
inet |
IPv4 + IPv6 (recomendada para a maioria dos casos) |
ip |
Apenas IPv4 (equivalente ao iptables) |
ip6 |
Apenas IPv6 (equivalente ao ip6tables) |
arp |
Tráfego ARP (equivalente ao arptables) |
bridge |
Tráfego na bridge (equivalente ao ebtables) |
netdev |
Por interface (ingress only, muito cedo no pipeline) |
A família inet é a recomendada para firewalls de hospedeiro porque processa simultaneamente IPv4 e IPv6, eliminando a duplicação de regras que era inevitável com iptables + ip6tables.
Tabela base de filtro
O exemplo seguinte mostra um firewall de host completo com política DROP na chain input, que aceita tráfego estabelecido, loopback, SSH, HTTP/HTTPS e ICMPv6 essencial:
# Tabela base de filtro
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iif lo accept
tcp dport { 22, 80, 443 } accept
icmpv6 type { echo-request, nd-neighbor-solicit } accept
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}
Cada chain base declara três elementos obrigatórios: o tipo (type filter), o hook onde se liga no Netfilter (hook input) e a política predefinida quando nenhuma regra faz match (policy drop).
⚠ A política predefinida deve ser DROP — um firewall com política ACCEPT é quase o mesmo que não ter firewall.
Comandos essenciais
# Carregar regras
nft -f /etc/nftables.conf
nft list ruleset
# Adicionar tabela
nft add table inet filter
# Adicionar chain
nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }'
# Adicionar regra individual
nft add rule inet filter input tcp dport 22 accept
# Apagar regra por handle (primeiro descobrir o handle)
nft -a list chain inet filter input
nft delete rule inet filter input handle 15
# Flush (limpar todas as regras)
nft flush ruleset
O -a (alias --handle) mostra o identificador interno de cada regra — necessário para a apagar individualmente. Sem -a, não há forma de referenciar uma regra específica.
3. Sets e Maps
Os sets são akiller feature do nftables — permitem agrupar endereços, portos, redes ou outros valores numa estrutura de dados nativa com lookup eficiente. No iptables, isto exigia o ipset como ferramenta externa; no nftables é parte da linguagem.
Sets simples
# Sets
table inet filter {
set blacklist { type ipv4_addr; flags interval; }
chain input {
ip saddr @blacklist drop
}
}
A flag interval permite adicionar intervalos CIDR (ex: 192.168.0.0/16) e não apenas endereços individuais. Para adicionar elementos dinamicamente:
# Adicionar endereços ao set
nft add element inet filter blacklist { 203.0.113.50 }
nft add element inet filter blacklist { 198.51.100.0/24 }
# Remover endereço
nft delete element inet filter blacklist { 203.0.113.50 }
# Listar conteúdo do set
nft list set inet filter blacklist
Sets anónimos inline
Para grupos pequenos que não mudam, usa-se um set anónimo directamente na regra — sem declarar um set nomeado. A sintaxe usa chavetas:
# Set anónimo de portos
tcp dport { 22, 80, 443, 8080-8090 } accept
# Set anónimo de endereços
ip saddr { 10.0.0.1, 10.0.0.2, 192.168.1.0/24 } accept
# Set anónimo de tipos ICMPv6
icmpv6 type { echo-request, nd-neighbor-solicit, nd-router-advert } accept
Maps (dicionários chave-valor)
Os maps são sets que mapeiam uma chave para um valor. Permitem, por exemplo, definir o porto de destino consoante o endereço de origem — numa única regra em vez de múltiplas regras encadeadas:
# Map: endereço -> porto
table inet filter {
map portforward {
type ipv4_addr : inet_service
elements = { 192.168.1.100 : 8080,
192.168.1.101 : 8443 }
}
chain prerouting {
type nat hook prerouting priority -100; policy accept;
ip daddr 203.0.113.1 dnat tcp dport map @portforward
}
}
Isto substitui dezenas de regras DNAT individuais por uma única regra que consulta o map. A performance é superior porque o kernel faz um único lookup em vez de avaliar cada regra sequencialmente.
Sets com timeout e contadores
Os sets podem ter timeout (útil para banir IPs temporariamente) e contadores (para monitorizar tráfego por set). A flag timeout remove automaticamente elementos após o período definido:
# Set com timeout de 1 hora e contador
table inet filter {
set banned {
type ipv4_addr
flags interval, timeout
timeout 1h
counter
}
chain input {
# Adicionar IP dinamicamente com timeout customizado
# nft add element inet filter banned { 1.2.3.4 timeout 30m }
ip saddr @banned drop
}
}
4. Chains e Prioridades
O nftables tem dois tipos de chains: base chains (ligadas a um hook do Netfilter — processam tráfego real) e regular chains (não ligadas a hooks — usadas como sub-rotinas chamadas por jump ou goto).
Hooks e prioridades
O parâmetro priority determina a ordem de execução quando múltiplas base chains se ligam ao mesmo hook. Prioridades mais baixas executam primeiro. Os valores padrão do Netfilter são:
| Hook | Prioridade filter | Prioridade nat | Prioridade mangle |
|---|---|---|---|
| prerouting | 0 (filter) | -100 (dstnat) | -150 (mangle) |
| input | 0 (filter) | 100 (srcnat) | -150 (mangle) |
| forward | 0 (filter) | 100 (srcnat) | -150 (mangle) |
| output | 0 (filter) | -100 (dstnat) | -150 (mangle) |
| postrouting | — | 100 (srcnat) | -150 (mangle) |
A ordem correcta é crítica: o NAT de destino (dstnat, prioridade -100) executa antes do filtro (prioridade 0), para que o filtro veja o endereço já traduzido. O NAT de origem (srcnat, prioridade 100) executa depois do filtro, para que o filtro veja o endereço original.
Jump vs Goto
jump chama outra chain e retorna à chain original após o processamento. goto chama outra chain mas não retorna — se a chain chamada não aceitar/dropar, a execução termina. Usa goto para chains finais e jump para chains que podem aceitar ou continuar:
# Regular chains para organizar lógica
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iif lo accept
jump incoming-traffic
}
chain incoming-traffic {
# Se aceitar aqui, retorna à chain input
tcp dport { 22, 80, 443 } accept
# Se não fizer match, retorna à input e aplica policy drop
}
}
5. NAT com nftables
O NAT (Network Address Translation) no nftables usa chains do tipo nat nos hooks prerouting (DNAT) e postrouting (SNAT). Para uma revisão dos conceitos fundamentais de NAT (SNAT, DNAT, PAT), consulta o Dia 10 do curso de redes.
Masquerade (SNAT dinâmico)
O masquerade é usado quando o endereço de saída é dinâmico (DHCP, PPP). Ele detecta automaticamente o endereço da interface de saída. Para um IP fixo, usa-se snat to que é mais eficiente:
# NAT
table ip nat {
chain postrouting {
type nat hook postrouting priority 100; policy accept;
oif eth0 masquerade
}
chain prerouting {
type nat hook prerouting priority -100; policy accept;
tcp dport 80 dnat to 192.168.1.10:8080
}
}
SNAT com IP fixo
# SNAT com IP fixo (mais eficiente que masquerade)
table ip nat {
chain postrouting {
type nat hook postrouting priority 100; policy accept;
oif eth0 snat to 203.0.113.1
}
}
# SNAT com pool de IPs (round-robin)
table ip nat {
chain postrouting {
type nat hook postrouting priority 100; policy accept;
oif eth0 snat to 203.0.113.1-203.0.113.5
}
}
DNAT com redirecionamento de porto
O redirect é uma forma especial de DNAT que redireciona para a própria máquina (útil para proxies transparentes). O dnat to redireciona para outra máquina:
# Redirect local (proxy transparente)
table ip nat {
chain prerouting {
type nat hook prerouting priority -100; policy accept;
tcp dport 80 redirect to 3128
}
}
# DNAT para servidor interno
table ip nat {
chain prerouting {
type nat hook prerouting priority -100; policy accept;
iif eth0 tcp dport 443 dnat to 192.168.1.50:443
}
}
Para uma visão geral dos diferentes tipos de firewalls de rede (incluindo uma comparação iptables vs nftables vs pfSense vs OPNSense), consulta o Dia 11: Firewalls de Rede.
6. Logging e Optimização
Log de tráfego
O nftables usa a acção log para registar pacotes no log do kernel (visível via dmesg ou journalctl -k). Pode-se definir prefixo, nível e grupo:
# Log de pacotes dropados
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iif lo accept
# Log antes de dropar (limitar a 10/min para não flooding)
log prefix "nft-drop: " level warn group 0 limit rate 10/minute
drop
}
}
# Log + accept (para auditoria)
log prefix "nft-accept-ssh: " level info accept
O limit rate é essencial — sem ele, um ataque de flood pode encher o log do kernel em segundos. O prefixo permite filtrar facilmente com journalctl -k | grep nft-drop.
Contadores por regra
Adicionar counter a uma regra conta pacotes e bytes que fazem match. Isto é útil para diagnóstico e para identificar regras inativas:
# Contador em regras individuais
tcp dport 22 counter accept
tcp dport { 80, 443 } counter accept
# Verificar contadores
nft list ruleset -a
# Output:
# tcp dport 22 counter packets 1453 bytes 89247 accept
# tcp dport { 80, 443 } counter packets 0 bytes 0 accept
Optimização de performance
Para firewalls com milhares de regras ou tráfego elevado, a optimização faz diferença entre um firewall que processa 1 Gbps e um que processa 10 Gbps:
| Recomendação | Impacto |
|---|---|
| Usar sets em vez de múltiplas regras | Lookup O(1) em vez de O(n) linear |
| Regras mais frequentes primeiro | Reduz avaliações desnecessárias |
ct state established,related accept no topo |
95%+ do tráfego corresponde aqui |
| Família inet (unifica IPv4+IPv6) | Menos chains, menos avaliações |
| Maps para DNAT/SNAT dinâmico | Uma regra em vez de centenas |
Limitar log com limit rate |
Evita degradação por I/O de log |
7. Erros Comuns e Lista de Verificação
Erros comuns
| Problema | Causa | Solução |
|---|---|---|
| Bloqueado fora do SSH | Esqueceu ct state established,related accept |
Adicionar como primeira regra da chain input |
| NAT não funciona | Falta sysctl net.ipv4.ip_forward=1 |
Activar forwarding no kernel |
| Regras não persistem após reboot | Não activou o serviço systemd | systemctl enable nftables |
| ICMPv6 bloqueado | Esqueceu ND/RA essential | Aceitar nd-neighbor-solicit, nd-router-advert |
| DNS não resolve | Chain output com policy drop sem regra DNS | Aceitar udp dport 53 na output |
| Prioridade NAT errada | DNAT com prioridade positiva | DNAT usa prioridade -100; SNAT usa 100 |
Lista de verificação pré-produção
Antes de colocar um firewall nftables em produção, verifica cada ponto:
- ✓ ct state established,related accept é a primeira regra da chain input
- ✓ Loopback (
iif lo accept) permitido na input e output - ✓ ICMPv6 essencial aceito (echo-request, nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert)
- ✓ Política DROP na input e forward
- ✓ SSH permitido (idealmente com limit rate para prevenir brute-force)
- ✓ Forwarding activado no kernel se a máquina faz routing/NAT (
net.ipv4.ip_forward=1) - ✓ Serviço nftables activado (
systemctl enable nftables) para persistência após reboot - ✓ Cópia de segurança do ficheiro
/etc/nftables.confantes de alterações - ✓ Testado com timeout — se SSH remoto, usar
atousystemd-tmpfilespara reverter automaticamente - ✓ Log de pacotes dropados com
limit ratepara auditoria
Migração do iptables
Para migrar regras existentes, usa-se o iptables-translate e ip6tables-translate — ferramentas que convertem regras iptables para a sintaxe nftables automaticamente:
# Converter regras iptables para nftables
iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPT
# Output: nft add rule inet filter input tcp dport 22 accept
ip6tables-translate -A INPUT -p tcp --dport 22 -j ACCEPT
# Output: nft add rule ip6 filter input tcp dport 22 accept
# Converter um ficheiro completo
iptables-restore-translate -f regras-iptables.save > regras.nft
# Ver as regras actualmente carregadas no iptables
iptables-save > regras-iptables.save
A conversão automática cobre a maioria dos casos, mas vale a pena revisar o resultado — algumas construções avançadas do iptables (especialmente com módulos pouco comuns) podem não ter equivalente directo. Para comandos básicos de rede Linux que complementam o firewall, consulta o Dia 8: Redes Linux — ip, ss, nmcli e DNS.