Dia 9: Firewalls Linux — iptables, nftables, UFW e firewalld

No Dia 1 dominámos o terminal; no Dia 2 o sistema de ficheiros; no Dia 3 os utilizadores. Hoje, Dia 9, entramos num dos temas mais críticos da administração de sistemas: firewalls em Linux — a camada que decide que tráfego entra, sai ou é encaminhado pelo teu servidor.

O Linux oferece não uma mas três camadas de gestão de firewall: o motor netfilter no kernel, as ferramentas clássicas iptables e nftables, e dois frontends de alto nível — UFW (Ubuntu/Debian) e firewalld (RHEL/Fedora). Ao fim deste artigo consegues escolher a ferramenta certa, escrever regras para SSH, HTTP/HTTPS, aplicar rate limiting e ativar logging — sem trancar o teu próprio acesso.

Neste artigo

Arquitetura Netfilter — O Motor no Kernel

Todas as ferramentas de firewall em Linux — iptables, nftables, UFW, firewalld — assentam no mesmo motor: Netfilter, um subsistema do kernel Linux introduzido em 2001 (kernel 2.4) que intercepta pacotes de rede em pontos estratégicos do stack TCP/IP. O Netfilter é o árbitro final: nada entra, sai ou atravessa o sistema sem passar pelos seus ganchos (documentação oficial netfilter.org).

O Netfilter expõe cinco hooks (pontos de interceção) no caminho de cada pacote. Cada ferramenta de userspace regista regras nestes hooks; o kernel executa-as em ordem:

Hook (NF_INET_*) Quando executa Chain iptables equivalente
PRE_ROUTING Antes do routing — pacote acabou de chegar à interface PREROUTING (raw, mangle, nat)
LOCAL_IN Pacote destinado a este próprio host INPUT (filter, mangle, nat)
FORWARD Pacote a atravessar o host (router/gateway) FORWARD (filter, mangle)
LOCAL_OUT Pacote gerado localmente, antes do routing OUTPUT (raw, mangle, nat, filter)
POST_ROUTING Após routing, antes de sair pela interface POSTROUTING (mangle, nat)
Nota: O Netfilter está sempre presente no kernel — não é um serviço que se arranca ou para. O que controlamos com systemctl stop é o frontend (firewalld, ufw), não o motor. As regras continuam ativas no kernel até serem explicitamente removidas ou o sistema reiniciar.

Chains e Tables — Anatomia do iptables

O iptables organiza as regras em duas dimensões: tables (que tipo de operação) e chains (quando aplicar). Cada table contém chains pré-definidas; cada chain contém regras sequenciais avaliadas de cima para baixo até à primeira correspondência (man 8 iptables).

As 5 Tables do iptables

Table Função Chains disponíveis
filter Filtragem padrão — aceitar, largar ou rejeitar pacotes INPUT, FORWARD, OUTPUT
nat Network Address Translation — alterar origem/destino PREROUTING, OUTPUT, POSTROUTING
mangle Modificar campos do pacote (TOS, TTL, mark) Todas as 5 chains
raw Antes do connection tracking — isentar pacotes PREROUTING, OUTPUT
security SELinux — raramente usada manualmente INPUT, FORWARD, OUTPUT

Targets — O que acontece quando uma regra corresponde

Target Comportamento
ACCEPT Permite o pacote — sai da chain atual
DROP Descarta silenciosamente — sem resposta ao remetente
REJECT Descarta e envia ICMP/port-unreachable ao remetente
LOG Regista no syslog — não termina a avaliação
DNAT / SNAT / MASQUERADE Altera destino/origem (table nat)
RETURN Sai da chain atual e volta à chain chamadora
Atenção: DROP vs REJECT: DROP descarta silenciosamente — o atacante não sabe se o host existe (mais seguro contra reconnaissance). REJECT envia resposta — útil para diagnóstico interno mas revela que o host está ativo. Em servidores expostos, prefere DROP.

iptables — Sintaxe e Regras Clássicas

O iptables é a ferramenta clássica para manipular o Netfilter, presente desde o kernel 2.4. Cada invocação adiciona, remove ou lista uma regra na chain indicada. A sintaxe segue o padrão iptables -t TABLE -A CHAIN [match] -j TARGET [target opts] (projecto iptables em netfilter.org).

Ver regras ativas

# Lista todas as regras de filter (padrão)
iptables -L -n -v --line-numbers

# Lista com contadores de bytes/pacotes
iptables -L -v -n

# Lista a table nat
iptables -t nat -L -n -v

# Lista uma chain específica
iptables -L INPUT -n --line-numbers

Regras básicas de filtragem

# Policy padrão: largar tudo o que não for explicitamente permitido
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

# Permitir tráfego de loopback (localhost)
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT

# Permitir ligações já estabelecidas (respostas a tráfego iniciado por nós)
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# Permitir SSH (porta 22)
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# Permitir HTTP (80) e HTTPS (443)
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# Permitir ICMP (ping) — útil para diagnóstico
iptables -A INPUT -p icmp -j ACCEPT

# Regra catch-all no fim: DROP implícito pela policy, mas explícito é mais claro
iptables -A INPUT -j DROP

Persistir regras iptables

As regras adicionadas com iptables vivem na memória do kernel — perdem-se num reboot. Para persistir:

# Ubuntu/Debian — instalar iptables-persistent
apt install iptables-persistent
# As regras atuais são guardadas em /etc/iptables/rules.v4 e rules.v6

# Guardar manualmente após alterações
netfilter-persistent save

# RHEL/Fedora/Rocky/Alma — iptables-services
dnf install iptables-services
service iptables save    # guarda em /etc/sysconfig/iptables
systemctl enable iptables

NAT com iptables — mascarar tráfego para a Internet

# Gateway com IP dinâmico (DHCP) — MASQUERADE
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

# Port forwarding: SSH externo na 2222 → interno na 22
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 2222 \
  -j DNAT --to-destination 192.168.1.100:22

# Ativar IP forwarding no kernel
echo 1 > /proc/sys/net/ipv4/ip_forward
# Persistente: net.ipv4.ip_forward=1 em /etc/sysctl.conf
sysctl -p

nftables — A Nova Geração

O nftables é o sucessor do iptables, introduzido no kernel 3.13 (2014) e consolidado como padrão em Debian 10+, RHEL 8+, Ubuntu 20.04+. Resolve três limitações do iptables: (1) sintaxe dispersa — cada ferramenta (iptables, ip6tables, arptables, ebtables) tinha a sua própria sintaxe; (2) sem suporte nativo para conjuntos (sets) de IPs; (3) atomicidade — no iptables cada regra é uma chamada syscall separada (wiki nftables, Arch Wiki nftables).

Comparação de sintaxe — iptables vs nftables

Operação iptables nftables
Permitir SSH iptables -A INPUT -p tcp --dport 22 -j ACCEPT nft add rule inet filter input tcp dport 22 accept
Policy DROP iptables -P INPUT DROP nft chain inet filter input { policy drop; }
Lista regras iptables -L -n nft list ruleset
Apagar tudo iptables -F && iptables -X nft flush ruleset
IPv4 + IPv6 Dois comandos: iptables + ip6tables Um só: inet (família unificada)

Configuração nftables em ficheiro

A grande vantagem do nftables é poder carregar um ruleset completo de um ficheiro de configuração, de forma atómica. O ficheiro padrão fica em /etc/nftables.conf:

#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;

        # Loopback
        iif "lo" accept

        # Ligações estabelecidas
        ct state established,related accept

        # SSH com rate limiting (max 5 ligações/minuto por IP)
        tcp dport 22 ct state new limit rate 5/minute accept

        # HTTP e HTTPS
        tcp dport { 80, 443 } accept

        # ICMP (ping)
        icmp type echo-request accept
        ip6 nexthdr icmpv6 accept

        # Log do que foi largado
        limit rate 10/second log prefix "nft-drop: " level warn
    }

    chain forward {
        type filter hook forward priority 0; policy drop;
    }

    chain output {
        type filter hook output priority 0; policy accept;
    }
}
# Aplicar o ruleset
nft -f /etc/nftables.conf

# Ver regras ativas
nft list ruleset

# Ativar o serviço para arrancar no boot
systemctl enable nftables
systemctl start nftables

# Verificar estado
systemctl status nftables
Nota: A família inet do nftables processa simultaneamente IPv4 e IPv6 — uma só regra cobre ambos os protocolos. No iptables tinhas de duplicar tudo em iptables e ip6tables.

UFW — Firewall Simplificado (Ubuntu/Debian)

O UFW (Uncomplicated Firewall) é o frontend padrão do Ubuntu, desenvolvido pela Canonical. Gera regras iptables (ou nftables em sistemas recentes) a partir de comandos simples — ideal para servidores individuais onde precisas de proteção básica sem lidar com sintaxe complexa (Launchpad UFW, Arch Wiki UFW).

Instalação e comandos básicos

# Instalar (vem pré-instalado no Ubuntu Server, mas desativado)
apt install ufw

# Ver estado
ufw status verbose

# Definir policies padrão
ufw default deny incoming
ufw default allow outgoing

# Permitir SSH
ufw allow 22/tcp
ufw allow ssh          # alternativa: usa o nome do serviço em /etc/services

# Permitir HTTP e HTTPS
ufw allow 80/tcp
ufw allow 443/tcp

# Permitir por IP específico
ufw allow from 192.168.1.50 to any port 22

# Permitir range de portas
ufw allow 3000:4000/tcp

# Apagar regra
ufw delete allow 80/tcp

# Ativar (pede confirmação por causa do SSH)
ufw enable

# Desativar
ufw disable

# Reset completo
ufw reset

UFW com rate limiting para SSH

# limit: bloqueia IP que tenta 6 ligações em 30 segundos
ufw limit 22/tcp

# Ver regras numeradas (para apagar por número)
ufw status numbered
Atenção: Antes de ufw enable, garante que permitiste o SSH (ufw allow 22/tcp). Caso contrário, perdes o acesso ao servidor remoto — o UFW bloqueia tudo por defeito.

UFW com perfis de aplicação

Algumas aplicações instalam perfis UFW em /etc/ufw/applications.d/ que agrupam múltiplas portas:

# Listar perfis disponíveis
ufw app list

# Ver detalhes de um perfil
ufw app info OpenSSH

# Permitir um perfil completo
ufw allow 'Nginx Full'    # abre 80 e 443

firewalld — Zones e Serviços (RHEL/Fedora)

O firewalld é o frontend dinâmico padrão de RHEL 7+, CentOS, Fedora, Rocky Linux e AlmaLinux. Diferente do UFW (que pensa em portas individuais), o firewalld organiza a segurança em zones (níveis de confiança por interface de rede) e serviços (grupos de portas pré-definidos). Suporta alterações em runtime sem perder o estado das ligações — uma vantagem sobre o iptables clássico (firewalld.org, Arch Wiki firewalld).

Zones — níveis de confiança

Zone Nível de confiança Uso típico
drop Mínimo — descarta tudo (sem resposta ICMP) Rede hostil, bloco de IP
block Rejeita com ICMP — só ligações iniciadas por dentro Servidor exposto
public Baixo — só serviços explicitamente permitidos Interface pública (Internet) — padrão
external Baixo + masquerade ativo Router/gateway para a Internet
dmz Médio — só serviços selecionados DMZ — hosts isolados acessíveis da Internet
work Médio-alto — confiança parcial Rede corporativa
home Alto — confiança na rede local Rede doméstica
internal Alto — confiança total na rede Rede interna atrás do router
trusted Máximo — tudo permitido NUNCA usar em interface pública

Comandos firewalld essenciais

# Instalar (vem pré-instalado em RHEL/Rocky/Alma/Fedora)
dnf install firewalld
systemctl enable --now firewalld

# Ver estado
firewall-cmd --state

# Ver zone ativa e interfaces
firewall-cmd --get-active-zones

# Ver todos os serviços disponíveis
firewall-cmd --get-services

# Ver serviços ativos na zone atual
firewall-cmd --list-all

# Atribuir interface à zone public
firewall-cmd --zone=public --change-interface=eth0

# Permitir serviço (runtime — perde-se no reload)
firewall-cmd --zone=public --add-service=ssh

# Permitir serviço (permanente — sobrevive a reboot)
firewall-cmd --zone=public --add-service=ssh --permanent

# Permitir HTTP e HTTPS permanentemente
firewall-cmd --zone=public --add-service=http --permanent
firewall-cmd --zone=public --add-service=https --permanent

# Abrir porta específica (sem serviço pré-definido)
firewall-cmd --zone=public --add-port=8080/tcp --permanent

# Permitir por IP/fonte
firewall-cmd --zone=public --add-source=192.168.1.50 --permanent

# Aplicar alterações permanentes ao runtime
firewall-cmd --reload

# Remover serviço
firewall-cmd --zone=public --remove-service=http --permanent
firewall-cmd --reload
Atenção: A distinção runtime vs permanent é a armadilha nº1 do firewalld. Sem --permanent, a regra perde-se no próximo reload/reboot. Sem --reload, a regra permanente não fica ativa imediatamente. Padrão seguro: adicionar com --permanent e fazer --reload no fim.

Regras Comuns — SSH, HTTP/HTTPS e Rate Limiting

Independente da ferramenta, certas regras aparecem em quase todos os servidores. Aqui estão as mais comuns, nas três ferramentas, lado a lado.

1. Permitir SSH (porta 22)

# iptables
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# nftables
nft add rule inet filter input tcp dport 22 accept

# UFW
ufw allow 22/tcp

# firewalld
firewall-cmd --zone=public --add-service=ssh --permanent
firewall-cmd --reload

2. Permitir HTTP (80) e HTTPS (443)

# iptables
iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT

# nftables (uma só regra para ambos, usando set)
nft add rule inet filter input tcp dport { 80, 443 } accept

# UFW
ufw allow 80/tcp
ufw allow 443/tcp
# ou usando perfil da aplicação:
ufw allow 'Nginx Full'

# firewalld
firewall-cmd --zone=public --add-service=http --permanent
firewall-cmd --zone=public --add-service=https --permanent
firewall-cmd --reload

3. Rate limiting — limitar tentativas de SSH

Rate limiting protege contra brute-force: permite ligações legítimas mas bloqueia quem tenta demasiadas vezes em pouco tempo. Usa o módulo limit ou recent do Netfilter:

# iptables — método 1: limit (máx 5/minuto)
iptables -A INPUT -p tcp --dport 22 -m limit --limit 5/minute --limit-burst 10 -j ACCEPT

# iptables — método 2: recent (bloqueia após 4 tentativas em 60s)
iptables -A INPUT -p tcp --dport 22 -m recent --set --name SSH
iptables -A INPUT -p tcp --dport 22 -m recent --update --seconds 60 --hitcount 4 --name SSH -j DROP
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# nftables — limit rate no nft
nft add rule inet filter input tcp dport 22 ct state new limit rate 5/minute accept

# UFW — limit (bloqueia após 6 tentativas em 30s)
ufw limit 22/tcp

# firewalld — rich rule para rate limiting
firewall-cmd --zone=public --add-rich-rule='rule service name=ssh limit value=5/m accept' --permanent
firewall-cmd --reload

4. Bloquear um IP específico

# iptables — bloquear IP (antes das regras ACCEPT)
iptables -I INPUT 1 -s 203.0.113.50 -j DROP

# nftables
nft add rule inet filter input ip saddr 203.0.113.50 drop

# UFW
ufw deny from 203.0.113.50

# firewalld — rich rule
firewall-cmd --zone=public --add-rich-rule='rule source address=203.0.113.50 reject' --permanent
firewall-cmd --reload

Logging de Firewall — Registar Pacotes

Sem logging, um firewall é uma caixa preta — não sabes que tráfego foi bloqueado, nem se há tentativas de intrusão. O Netfilter tem um target LOG que envia pacotes para o syslog (kernel facility) sem interromper a avaliação da chain.

iptables — ativar LOG com rate limiting

# Registar pacotes descartados (máx 10/segundo para não encher o disco)
iptables -A INPUT -m limit --limit 10/second -j LOG \
  --log-prefix "iptables-drop: " --log-level 4

# Depois vem a regra de DROP
iptables -A INPUT -j DROP

nftables — logging integrado

# Dentro da chain input (antes do policy drop)
nft add rule inet filter input limit rate 10/second log prefix "nft-drop: " level warn

firewalld — rich rules com log

# Registar e rejeitar tráfego de um IP
firewall-cmd --zone=public --add-rich-rule='rule source address=203.0.113.50 log prefix="fw-block: " level=notice limit value="5/m" reject' --permanent
firewall-cmd --reload

Onde ver os logs

# Mensagens de firewall vão para o kernel log
journalctl -k | grep -E "iptables|nft|fw-"

# Em tempo real
journalctl -fk | grep -E "drop|reject|block"

# Em sistemas sem journald
tail -f /var/log/messages | grep "iptables-drop:"
tail -f /var/log/syslog | grep "nft-drop:"

# Contar pacotes descartados na última hora
journalctl --since "1 hour ago" -k | grep -c "drop"
Atenção: SEMPRE aplicar rate limiting ao target LOG. Sem limite, um flood de pacotes pode gerar milhares de linhas por segundo no syslog, esgotando o espaço em disco e degradando o sistema. O padrão seguro: --limit 10/second ou limit rate 10/second.

Ordem das Regras e Prioridades

A regra de ouro do iptables/nftables: as regras são avaliadas de cima para baixo, e a primeira correspondência vence. A ordem importa tanto quanto as próprias regras — uma regra permissiva antes de uma restritiva torna a restrição inútil.

Fluxo de um pacote através das tables

Quando um pacote chega ao host, atravessa as tables numa ordem fixa, determinada pelas prioridades do Netfilter. Compreender esta ordem é essencial para saber onde colocar cada regra:

Pacote a chegar (INPUT):
  raw.PREROUTING → mangle.PREROUTING → nat.PREROUTING
  → mangle.INPUT → filter.INPUT → [entrega à aplicação local]

Pacote a sair (OUTPUT):
  raw.OUTPUT → mangle.OUTPUT → nat.OUTPUT
  → filter.OUTPUT → mangle.POSTROUTING → nat.POSTROUTING

Pacote a atravessar (FORWARD):
  raw.PREROUTING → mangle.PREROUTING → nat.PREROUTING
  → mangle.FORWARD → filter.FORWARD
  → mangle.POSTROUTING → nat.POSTROUTING

Prioridades no nftables

No nftables, cada chain declara a sua priority — um inteiro que determina a ordem de execução. Valores mais baixos executam primeiro:

Priority Equivalente iptables Uso
-300 raw Antes do conntrack — isentar pacotes
-150 mangle (antes) Modificar campos do pacote cedo
-100 dstnat (DNAT) Alterar destino — port forwarding
0 filter Filtragem padrão — a maioria das regras
100 srcnat (SNAT) Alterar origem — masquerade
300 mangle (depois) Modificar campos tardiamente

Boa prática — ordem recomendada das regras

# 1. Loopback (sempre primeiro)
iptables -A INPUT -i lo -j ACCEPT

# 2. Ligações estabelecidas (respostas a tráfego nosso)
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# 3. Tráfego legítimo que queremos permitir (SSH, HTTP, etc.)
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT

# 4. Rate limiting / bloqueios específicos (IPs problemáticos)
iptables -A INPUT -s 203.0.113.50 -j DROP

# 5. LOG (com rate limit) para diagnóstico
iptables -A INPUT -m limit --limit 10/second -j LOG --log-prefix "drop: "

# 6. Catch-all (DROP implícito pela policy, ou explícito)
iptables -A INPUT -j DROP
Nota: A regra ESTABLISHED,RELATED é obrigatória em qualquer firewall stateful. Sem ela, o servidor aceita pedidos de entrada mas as respostas a ligações iniciadas por ele próprio são descartadas — o que quebra DNS, atualizações de pacotes e tudo o que sai do servidor.

Erros Comuns

Problema Causa Solução
Perdi acesso SSH após ativar firewall Ativaste UFW/firewalld sem permitir a porta 22 primeiro Aceder por consola (VNC/IPMI); adicionar ufw allow 22/tcp antes de ufw enable
Regras firewalld desaparecem após reboot Adicionaste sem --permanent Re-adicionar com --permanent e fazer firewall-cmd --reload
Regras iptables perdem-se no reboot iptables não persiste regras automaticamente apt install iptables-persistent (Debian) ou service iptables save (RHEL)
Servidor não consegue resolver DNS Falta a regra ESTABLISHED,RELATED ou OUTPUT está em DROP Garantir -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT e -P OUTPUT ACCEPT
Disco enche com logs de firewall Target LOG sem rate limiting Adicionar --limit 10/second ao target LOG
firewalld e iptables entram em conflito Ambos tentam controlar o Netfilter simultaneamente Usar um só: systemctl stop iptables && systemctl mask iptables ou desativar firewalld
nftables e iptables-backend entram em conflito Em RHEL 8+, iptables é wrapper de nftables; misturar ambos gera duplicação Escolher um: ou nftables nativo ou iptables-nft (não ambos manualmente)
Ping funciona mas SSH não ICMP permitido mas porta 22 não, ou rate limiting demasiado agressivo Verificar iptables -L INPUT -n; ajustar --limit-burst se necessário

Checklist de Configuração de Firewall

  1. Escolher uma ferramenta: UFW em Ubuntu/Debian, firewalld em RHEL/Fedora, nftables para controlo total — nunca misturar duas ativas.
  2. Definir policy padrão DROP para INPUT e FORWARD, ACCEPT para OUTPUT.
  3. Permitir loopback: iptables -A INPUT -i lo -j ACCEPT.
  4. Permitir ligações estabelecidas: -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT.
  5. Permitir SSH antes de ativar o firewall: ufw allow 22/tcp ou equivalente.
  6. Aplicar rate limiting ao SSH: ufw limit 22/tcp ou rich rule no firewalld.
  7. Permitir serviços necessários: HTTP/HTTPS, SMTP, etc. — só o que é preciso, nada mais.
  8. Ativar logging com rate limiting: --limit 10/second -j LOG.
  9. Persistir as regras: netfilter-persistent save, --permanent + --reload, ou nft -f /etc/nftables.conf.
  10. Ativar o serviço no boot: systemctl enable ufw / firewalld / nftables.
  11. Verificar com ufw status / firewall-cmd --list-all / nft list ruleset.
  12. Testar acesso SSH a partir de outra máquina antes de fechar a consola de emergência.
  13. Considerar fail2ban (ver Dia 27 — Segurança e Hardening) para proteção adicional contra brute-force.

Artigos Relacionados