Aqui temos um exemplo de configuração bem básica do arquivo nftables.conf para workstation (desktop, PC, estação de trabalho, etc):
#!/sbin/nft -f
# clear rules (limpa as regras)
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
# Aceite todo o tráfego localhost
iif lo accept
# Aceite o tráfego estabelecido e relatado originado de localhost
ct state established,related accept
# Aceite a descoberta de neighbor (vizinho mais próximo) IpV6, caso contrário a conectividade será interrompida
icmpv6 type { nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept
}
}
Aqui temos exemplo bem básico para servidor: nftables.conf
#!/sbin/nft -f
# clear rules (limpa as regras)
flush ruleset
table inet firewall {
chain inbound_ipv4 {
# Aceite ping (icmp-echo-request) para diagnósticos.
# No entanto, também permite que descubram que esse host está ativo.
# Este exemplo aceita dentro de um determinado limite de taxa.
# Basta descomentar a linha abaixo.
# icmp type echo-request limit rate 5/second accept
}
chain inbound_ipv6 {
# Aceite a descoberta de neighbor (vizinho mais próximo) IpV6, caso contrário a conectividade será interrompida
icmpv6 type { nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept
# Aceite ping (icmp-echo-request) para diagnósticos.
# No entanto, também permite que descubram que esse host está ativo.
# Este exemplo aceita dentro de um determinado limite de taxa.
# Basta descomentar a linha abaixo.
# icmpv6 type echo-request limit rate 5/second accept
}
chain inbound {
# Por padrão descarte todo o tráfego, a menos que atenda ao filtro
# dos critérios especificados pelas regras a seguir.
type filter hook input priority 0; policy drop;
# Permitir o tráfego de pacotes estabelecidos e relacionados, dropar inválidos
ct state vmap { established : accept, related : accept, invalid : drop }
# Permitir tráfego de loopback.
iifname lo accept
# Salta para a cadeia de acordo com o protocolo da camada 3 usando um vmap
meta protocol vmap { ip : jump inbound_ipv4, ip6 : jump inbound_ipv6 }
# Aceite SSH na porta TCP/22 e aceite HTTP(S) TCP/80 e TCP/443
# para IPv4 e IPv6.
tcp dport { 22, 80, 443} accept
# Remova o comentário para habilitar o log do tráfego de entrada negado
# log prefix "[nftables] Inbound Denied: " counter drop
}
chain forward {
# Drope tudo (assume que este dispositivo não é um roteador [não faz NAT])
type filter hook forward priority 0; policy drop;
}
# Não precisa definir a chain output, pois a política padrão é accept se uma chain ficar indefinida.
}
As regras Nftables são bem específicas e de sintaxe simples. O Nftables é um framework e lê todo o script para depois executá-lo.
Você pode adicionar comentários iniciando com #, você pode criar variáveis no script nftables.conf, você pode incluir outros arquivos, etc, sendo que já dava para fazer no Iptables.
O script nftables.conf sempre deve iniciar com a linha:
#!/usr/sbin/nft -f
ou
#!/sbin/nft -f
ou
#!/usr/bin/nft -f
ou equivalente dependendo do caminho do nft no seu sistema.
Se você omitir o parâmetro -f o utilitário nft não lerá o script e exibirá "Error: syntax error, unexpected newline, expecting string" ou então ao reiniciar o Nftables ele dará erro que você poderá ver com o comando systemctl status nftables ou systemctl status nftables.service, no Debian e derivados e em distribuições que tenham o famigerado Systemd.
Os símbolos #! também devem estar sempre presentes no início da linha, pois trata-se de um arquivo de script (bash, sh, etc).
Para listar regras com a posição dentro do arquivo:
# nft -n -a list table filter
* a opção -a acrescenta o "handle" na saída do comando, é o identificador, a posição na qual a regra será manipulada.
table filter {
chain output {
type filter hook output priority 0;
ip protocol tcp counter packets 82 bytes 9680 # handle 8
ip saddr 127.0.0.1 ip daddr 127.0.0.6 drop # handle 7
}
}
# nft -n -a list ruleset
ou
# nft --handle list ruleset
table inet filter { # handle 19
chain input { # handle 1
type filter hook input priority 0; policy accept;
ct state vmap { 0x1 : drop, 0x2 : accept, 0x4 : accept } # handle 5
}
chain forward { # handle 2
type filter hook forward priority 0; policy accept;
}
chain output { # handle 3
type filter hook output priority 0; policy accept;
}
}
Percebam que nas regras acima a opção priority é 0 (a mesma) para as três chains, então o nft organiza o handle de cima para baixo de acordo com a sua posição no script.
Percebam que "table inet filter {" saiu com o handle de número 19, sendo que sua posição está no início do script.
Lembrando que uma tabela é um contêiner para chains, vmaps, etc e dentro de uma chain pode ter rules, maps, vmaps, etc.
Coloquei somente uma parte da saída do comando nft -n -a list ruleset, pois ela é um tanto extensa e não vem ao propósito colocar todo o script, mas já dá para ter uma boa idéia de como o utilitário nft trabalha o script.
Observem que o Nftables não traz chains pré-definidas e você pode nomear as chains e criar quantas quiser, o que também dá para fazer no Iptables.
O Iptables lê as regras de cima para baixo e da esquerda para a direita de acordo com cada tabela, ou seja, se uma regra bloquear tudo numa determinada tabela, as exceções (liberações) devem ser colocadas acima desta regra. Caso duas regras entrarem em contradição valerá a regra que está na posição anterior, a que vem por primeiro no script é a que valerá.
No Nftables esse problema ficou mais amenizado, senão quase sumiu.
Por exemplo, caso tivermos uma regra com o handle 3 e outra com handle 9 e as duas estiverem em contradição, a regra handle 3 é a que valerá mesmo que a regra handle 9 esteja dentro do script numa posição acima da handle 3. Várias expressões são avaliadas linearmente da esquerda para a direita: se a primeira expressão corresponder, a próxima expressão será avaliada e assim por diante.
Quando o nft atinge a expressão final, ou seja, o fim da regra (não o fim do script), se o pacote corresponder a todas as expressões da regra, então as instruções da regra serão executadas e o pacote será filtrado.
Uma coisa é o nft atingir o fim da regra, outra coisa é o nft atingir o fim do script.
As regras são avaliadas da esquerda para a direita e o script como um todo é avaliado de cima para baixo.
Com a sintaxe simplificada do Nftables tornou-se difícil o autor do script enganar-se nas regras como acontecia com o Iptables onde o script ficava aquela bagunça que nem o próprio autor entendia mais; e quando precisava acrescentar ou deletar uma regra dava calafrios, suor e passava mal só em pensar em alterar o script. Depois de pronto e funcionando redondo o script do Iptables o autor sequer respirava muito forte perto dele para não bagunçar as regras. E se tinha o Squid em conjunto, aí então a coisa ficava pior ainda.
Boa parte desse problema de script Iptables bagunçado era devido à falta de conhecimento do autor do script e porque o Iptables tem essa falha, mas apesar disso tudo, o Iptables deixará saudades.
Caso você queira continuar utilizando o Iptables ele ainda não foi descontinuado e acredito que isso demorará para acontecer.
No Nftables podemos também utilizar index e handle:
# nft insert rule inet my_table my_filter_chain index 1 tcp dport nfs accept
# nft list ruleset
table inet my_table {
chain my_filter_chain {
type filter hook input priority 0; policy accept;
tcp dport http accept
tcp dport nfs accept
tcp dport ssh accept
}
}
# nft add rule inet my_table my_filter_chain handle 3 tcp dport 1234 accept
# nft insert rule inet my_table my_filter_chain handle 2 tcp dport nfs accept
# nft --handle list ruleset
table inet my_table { # handle 21
chain my_filter_chain { # handle 1
type filter hook input priority 0; policy accept;
tcp dport http accept # handle 3
tcp dport 1234 accept # handle 8
tcp dport nfs accept # handle 7
tcp dport ssh accept # handle 2
}
}
O parâmetro index serve para especificar um índice na lista de regras.
O parâmetro add inserirá a nova regra após a regra no índice fornecido.
O parâmetro insert inserirá a nova regra antes da regra no índice fornecido.
Com index os valores começam em 0.
O index deve se referir a uma regra existente. Isso significa que o comando "nft insert rule ... index 0" dará erro se for em uma chain vazia.
Podemos utilizar os parâmetros handle e delete para deletar uma regra em específico.
Primeiro listamos para saber a regra:
# nft --handle list ruleset
table inet minha_tabela { # handle 21
chain chain_meu_filtro { # handle 1
type filter hook input priority 0; policy accept;
tcp dport http accept # handle 3
tcp dport 1234 accept # handle 8
tcp dport nfs accept # handle 7
tcp dport ssh accept # handle 2
}
}
Depois deletamos a regra com handle 8:
# nft delete rule inet minha_tabela chain_meu_filtro handle 8
# nft --handle list ruleset
table inet minha_tabela { # handle 21
chain chain_meu_filtro { # handle 1
type filter hook input priority 0; policy accept;
tcp dport http accept # handle 3
tcp dport nfs accept # handle 7
tcp dport ssh accept # handle 2
}
}