Firewall scripting com iptables e nftables

1. Fundamentos de Firewall em Shell Script

Firewalls baseados em Linux operam através de regras organizadas em estruturas hierárquicas. No iptables, as regras são agrupadas em chains (INPUT, OUTPUT, FORWARD) dentro de tables (filter, nat, mangle). Cada chain possui uma política padrão (ACCEPT, DROP, REJECT) que determina o destino de pacotes que não correspondem a nenhuma regra.

O nftables substitui essa arquitetura por um design mais unificado: tabelas contêm chains, que contêm regras. Não há divisão fixa entre tables — o administrador define nomes e tipos livremente.

Principais diferenças conceituais:
- iptables usa protocolo IPv4 e IPv6 separadamente (ip6tables); nftables trata ambos na mesma sintaxe com famílias de endereço (ip, ip6, inet)
- nftables elimina a necessidade de módulos de match separados (como state, conntrack, recent)
- iptables depende de extensões de kernel carregadas dinamicamente; nftables tem suporte nativo a sets, maps e expressões

Antes de qualquer script, verifique a disponibilidade:

#!/bin/bash
# Verificação de compatibilidade
if command -v iptables &>/dev/null; then
    echo "iptables disponível"
fi
if command -v nft &>/dev/null; && nft --version &>/dev/null; then
    echo "nftables disponível"
fi

2. Scripts Básicos com iptables

Um script de reset e configuração inicial segue esta estrutura:

#!/bin/bash
IPT="/sbin/iptables"
IP6T="/sbin/ip6tables"
IFACE="eth0"
SSH_PORT=22
HTTP_PORT=80
HTTPS_PORT=443

# Resetar todas as regras
$IPT -F
$IPT -X
$IPT -t nat -F
$IPT -t mangle -F

# Políticas padrão: DROP para INPUT/FORWARD, ACCEPT para OUTPUT
$IPT -P INPUT DROP
$IPT -P FORWARD DROP
$IPT -P OUTPUT ACCEPT

# Permitir tráfego loopback
$IPT -A INPUT -i lo -j ACCEPT

# Permitir conexões estabelecidas e relacionadas
$IPT -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# Serviços específicos
$IPT -A INPUT -i $IFACE -p tcp --dport $SSH_PORT -j ACCEPT
$IPT -A INPUT -i $IFACE -p tcp --dport $HTTP_PORT -j ACCEPT
$IPT -A INPUT -i $IFACE -p tcp --dport $HTTPS_PORT -j ACCEPT

# Log de pacotes rejeitados (opcional)
$IPT -A INPUT -j LOG --log-prefix "IPTABLES-INPUT-DROP: " --log-level 4

# Regras IPv6 equivalentes (desabilitar se não usado)
$IP6T -P INPUT DROP
$IP6T -P FORWARD DROP
$IP6T -P OUTPUT ACCEPT

Variáveis facilitam manutenção: altere SSH_PORT em um único local para refletir em todas as regras.

3. Scripts Básicos com nftables

nftables utiliza uma sintaxe mais limpa e hierárquica:

#!/bin/bash
NFT="/usr/sbin/nft"

# Remover tabelas existentes
$NFT flush ruleset

# Criar tabela e chains
$NFT add table inet filter
$NFT add chain inet filter input   { type filter hook input priority 0 \; policy drop \; }
$NFT add chain inet filter output  { type filter hook output priority 0 \; policy accept \; }
$NFT add chain inet filter forward { type filter hook forward priority 0 \; policy drop \; }

# Regras: loopback e conexões estabelecidas
$NFT add rule inet filter input iif lo accept
$NFT add rule inet filter input ct state established,related accept

# Serviços
$NFT add rule inet filter input tcp dport 22 accept
$NFT add rule inet filter input tcp dport {80, 443} accept

# Logging de pacotes rejeitados
$NFT add rule inet filter input log prefix "NFTABLES-INPUT-DROP: " level info

Para otimização com sets:

#!/bin/bash
NFT="/usr/sbin/nft"

# Criar set de portas permitidas
$NFT add set inet filter allowed_ports { type inet_service \; }
$NFT add element inet filter allowed_ports { 22, 80, 443, 8080 }

# Regra usando o set
$NFT add rule inet filter input tcp dport @allowed_ports accept

Sets reduzem o número de regras e melhoram desempenho em firewalls com muitas portas.

4. Automação de Regras com Loops e Condicionais

Laços for permitem adicionar múltiplas portas ou IPs em lote:

#!/bin/bash
IPT="/sbin/iptables"
INTERNAL_NET="192.168.1.0/24"
PORTS=(22 80 443 3306 8080)

# Permitir portas específicas apenas da rede interna
for port in "${PORTS[@]}"; do
    $IPT -A INPUT -s $INTERNAL_NET -p tcp --dport $port -j ACCEPT
done

# Bloquear IPs maliciosos
BLOCKED_IPS=("10.0.0.5" "192.168.2.100" "172.16.0.50")
for ip in "${BLOCKED_IPS[@]}"; do
    $IPT -A INPUT -s $ip -j DROP
done

Condicionais if aplicam regras baseadas em interfaces:

#!/bin/bash
WAN_IF="eth0"
LAN_IF="eth1"

if [[ "$WAN_IF" != "$LAN_IF" ]]; then
    # Regras específicas para WAN
    iptables -A FORWARD -i $WAN_IF -o $LAN_IF -m state --state ESTABLISHED,RELATED -j ACCEPT
    iptables -A FORWARD -i $LAN_IF -o $WAN_IF -j ACCEPT
fi

Funções reutilizam blocos comuns:

#!/bin/bash
allow_service() {
    local port=$1
    local proto=${2:-tcp}
    local iface=${3:-eth0}
    iptables -A INPUT -i $iface -p $proto --dport $port -j ACCEPT
}

allow_service 22
allow_service 80
allow_service 443
allow_service 53 udp

5. Persistência e Gerenciamento de Regras

Para iptables, salve e restaure com:

#!/bin/bash
# Salvar regras atuais
iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6

# Restaurar
iptables-restore < /etc/iptables/rules.v4
ip6tables-restore < /etc/iptables/rules.v6

Em nftables, o processo é:

#!/bin/bash
# Salvar ruleset
nft list ruleset > /etc/nftables.conf

# Restaurar
nft -f /etc/nftables.conf

Script de inicialização via systemd:

#!/bin/bash
# /etc/systemd/system/firewall.service
[Unit]
Description=Firewall Script
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/firewall.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

Ative com:

systemctl enable firewall.service
systemctl start firewall.service

6. Logging e Monitoramento de Tráfego

Regras de log no iptables:

#!/bin/bash
# Log de pacotes SYN (tentativas de conexão)
iptables -A INPUT -p tcp --syn -j LOG --log-prefix "SYN-ATTEMPT: " --log-level 4

# Log de pacotes ICMP (ping)
iptables -A INPUT -p icmp -j LOG --log-prefix "ICMP-PACKET: " --log-level 4

Em nftables:

#!/bin/bash
nft add rule inet filter input tcp flags syn log prefix "SYN-ATTEMPT: " level info
nft add rule inet filter input icmp type echo-request log prefix "PING-DETECTED: " level info

Script de análise simples:

#!/bin/bash
LOG_FILE="/var/log/kern.log"
ALERT_THRESHOLD=100

# Contar tentativas SYN por IP
tail -n 10000 $LOG_FILE | grep "SYN-ATTEMPT" | awk '{print $NF}' | sort | uniq -c | while read count ip; do
    if [[ $count -gt $ALERT_THRESHOLD ]]; then
        echo "ALERTA: IP $ip fez $count tentativas SYN"
        # Opcional: adicionar regra de bloqueio
        iptables -A INPUT -s $ip -j DROP
    fi
done

7. Migração e Compatibilidade entre Sistemas

Script de conversão de iptables para nftables:

#!/bin/bash
# Converter regras iptables existentes
iptables-save > /tmp/iptables.rules
iptables-restore-translate -f /tmp/iptables.rules > /tmp/nftables.rules

# Aplicar regras convertidas
nft -f /tmp/nftables.rules

# Teste de validação
if nft -c -f /tmp/nftables.rules 2>/dev/null; then
    echo "Regras válidas"
else
    echo "Erro de validação - realizando rollback"
    nft flush ruleset
    iptables-restore < /tmp/iptables.rules
fi

Diferenças comuns na sintaxe:
- iptables: -m state --state NEW → nftables: ct state new
- iptables: -m multiport --dports 80,443 → nftables: tcp dport {80, 443}
- iptables: -j LOG --log-prefix "DROP: " → nftables: log prefix "DROP: "

Script de rollback automático:

#!/bin/bash
backup_rules() {
    iptables-save > /tmp/iptables.backup.$(date +%Y%m%d_%H%M%S)
}

rollback() {
    echo "Rollback iniciado"
    nft flush ruleset
    iptables-restore < /tmp/iptables.backup.$(ls -t /tmp/iptables.backup.* | head -1 | xargs basename)
}

# Tentar aplicar novas regras
backup_rules
nft -f /etc/nftables.conf || rollback

Referências