Linux: Escrevendo scripts de firewall, parte 3

Clique aqui para ler a segunda parte

Na primeira parte do tutorial, geramos um script de firewall simples, destinado a compartilhar a conexão e bloquear as conexões de entrada. Na segunda parte, estudamos o uso de regras adicionais, destinadas ao uso em um servidor de rede local, configurado como gateway da rede. Nessa terceira e última parte, estudaremos sobre a configuração do firewall em servidores de Internet.

Ao configurar um servidor dedicado, o firewall é ainda mais importante, já que por possuir não apenas um endereço IP fixo, mas também ser acessível através de um domínio potencialmente bem conhecido, o servidor será alvo de ataques contínuos. Por um lado, a configuração é mais simples, já que você não precisará se preocupar com regras de roteamento e de encaminhamento de pacotes, como ao configurar o firewall do gateway da rede, mas, por outro, existe um conjunto de cuidados adicionais a tomar.

Um exemplo básico de script de firewall para uso em um servidor web seria:

#!/bin/bash

iniciar(){

# Abre para a interface de loopback:
iptables -A INPUT -p tcp -i lo -j ACCEPT

# Bloqueia um determinado IP. Use para bloquear hosts específicos:
#iptables -A INPUT -p ALL -s 88.191.79.206 -j DROP

# Abre as portas referentes aos serviços usados:

# SSH:
iptables -A INPUT -p tcp –dport 22 -j ACCEPT

# DNS:
iptables -A INPUT -p tcp –dport 53 -j ACCEPT
iptables -A INPUT -p udp –dport 53 -j ACCEPT

# HTTP e HTTPS:
iptables -A INPUT -p tcp –dport 80 -j ACCEPT
iptables -A INPUT -p tcp –dport 443 -j ACCEPT

# Bloqueia conexões nas demais portas:
iptables -A INPUT -p tcp –syn -j DROP

# Garante que o firewall permitirá pacotes de conexões já iniciadas:
iptables -A INPUT -m state –state ESTABLISHED,RELATED -j ACCEPT

# Bloqueia as portas UDP de 0 a 1023 (com exceção das abertas acima):
iptables -A INPUT -p udp –dport 0:1023 -j DROP

}
parar(){
iptables -F
iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT
}

case “$1” in
“start”) iniciar ;;
“stop”) parar ;;
“restart”) parar; iniciar ;;
*) echo “Use os parâmetros start ou stop”
esac

Se você está configurando um servidor dedicado remotamente, é importante que você teste o script antes de configurar o sistema para executá-lo automaticamente durante o boot. O motivo é simples: se houver alguma regra incorreta no script, que bloqueie seu acesso ao servidor, você poderá solicitar um reboot do servidor para que a configuração seja removida e você recupere o acesso. Entretanto, se o sistema for configurado para carregar o script durante o boot, o reboot não resolverá e você precisará abrir uma chamada de suporte, solicitando que um técnico se logue localmente no servidor e desative seu script (o que provavelmente resultará em uma taxa adicional).

Para evitar a possibilidade de precisar reiniciar o servidor para recuperar o acesso depois de uma configuração de firewall mal-sucedida, você pode usar um script simples, contendo as regras de firewall desejadas, seguidas de um sleep e comandos para limpar a configuração do firewall.

Presumindo que você não esteja usando regras de roteamento (da tabela NAT), um exemplo de bloco de comandos para incluir no final do script seria:

sleep 120
iptables -F
iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT
iptables -X

Já conhecemos o “iptables -F”, que limpa as regras do firewall. As opções “iptables -P INPUT ACCEPT” e “iptables -P OUTPUT ACCEPT” alteram a política padrão do firewall (caso por ventura ela tenha sido alterada anteriormente), fazendo com que ele reverta para o default, que é aceitar todos os pacotes, tanto de entrada quanto de saída. O “iptables -X” elimina qualquer tabela personalizada que tenha sido adicionada, garantindo que qualquer regra oculta de configuração seja eliminada.

O “sleep 120” é um contador, que faz com que o sistema espere dois minutos antes de continuar. Colocando os comandos no final do script de firewall, o sistema executaria os comandos que ativam o firewall, esperaria dois minutos e em seguida executaria os comandos que o desativam.

Com isso, as regras são desativadas automaticamente depois de alguns minutos; se você acabar trancado do lado de fora, vai precisar apenas esperar o tempo especificado e se conectar novamente. Só depois de testar o script e ter certeza de que ele está mesmo fazendo apenas o que deseja, você removeria os comandos de desativação e configuraria o sistema para ativá-lo automaticamente durante o boot.

Regras adicionais de segurança: Em um servidor dedicado, não faz muito sentido bloquear a resposta a pings, já que, de qualquer forma, ele precisará ficar com um conjunto de portas abertas. Ao invés de bloquear os pings, que afinal podem ser úteis em algumas situações, você pode limitar as respostas a apenas uma por segundo, evitando que alguém de má fé possa enviar um grande volume de pings (como ao usar o comando “ping -f”), como parte de um ataque DoS. Nesse caso, a regra seria:

iptables -A INPUT -p icmp –icmp-type echo-request -m limit –limit 1/s -j ACCEPT

Se seu servidor não irá atuar como um roteador, é prudente desativar o suporte a ICMP redirects. Este é um recurso utilizado por roteadores para alertar outros de que existe um melhor caminho para um determinado endereço ou rede, mas não tem uso legítimo em um servidor que não roteia pacotes:

echo 0 > /proc/sys/net/ipv4/conf/all/accept_redirects

Outra configuração desejada é desativar o suporte a ping broadcasts, um recurso que tem poucos usos legítimos e pode ser usado para fazer com que servidores participem involuntariamente de ataques DoS, enviando grandes quantidades de pings a outros servidores dentro da mesma faixa de endereços. Ele já vem desativado em quase todas as distribuições atuais, mas não custa verificar:

echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts

Mais uma opção que é importante manter desativada é o suporte ao source routing. Este é um recurso usado para testes de roteadores, que permite ao emissor especificar qual o caminho que o pacote tomará até o destino e também o caminho de volta. Ele é perigoso, pois permite falsear pacotes, fazendo com que eles pareçam vir de outro endereço e, ao mesmo tempo, fazer com que as respostas realmente sejam recebidas, permitindo abrir a conexão e transferir dados. Em outras palavras, se você incluiu regras que permitem o acesso de terminados endereços e esqueceu o suporte ao source routing ativo, um atacante que soubesse quais são os endereços autorizados poderia abrir conexões com o seu servidor se fazendo passar por um deles, um risco que você com certeza gostaria de evitar. Como o recurso não possui outros usos legítimos, é fortemente recomendável que você o mantenha desativado:

echo 0 > /proc/sys/net/ipv4/conf/all/accept_source_route

Diferente do servidor de rede local que compartilha a conexão, o servidor não deverá compartilhar a conexão, nem encaminhar pacotes de outros hosts. Você pode deixar isso explicito desativando o suporte a ip_forward:

echo 0 > /proc/sys/net/ipv4/ip_forward

Concluindo, temos o suporte a SYN cookies. Um dos tipos mais comuns de ataque DoS e também um dos mais efetivos é o SYN Flood. Este tipo de ataque consiste em enviar um grande volume de pacotes SYN até o alvo, sem nunca efetivamente abrir a conexão. Como os pacotes SYN possuem alguns poucos bytes, o ataque pode ser feito mesmo a partir de uma conexão doméstica.

Uma conexão TCP é iniciada através da troca de três pacotes entre o emissor e o destinatário, o famoso “three-way handshake”. O emissor envia um pacote SYN, o destinatário responde com um pacote SYN/ACK e o emissor confirma, também enviando um pacote ACK. A conexão TCP fica então aberta por um certo tempo, até que a requisição da página, download do arquivo, ou outra operação em questão seja concluída.

Se o servidor recebe um pacote SYN solicitando a abertura da conexão, mas não recebe o pacote ACK de resposta, ele é obrigado a esperar até que o tempo limite seja atingindo, mantendo a conexão aberta. Como existe um limite de conexões TCP que o servidor pode manter ativas simultaneamente, um grande volume de pacotes SYN podem estourar o limite de conexões, fazendo com que o servidor deixe de responder a novas conexões, mesmo que exista banda disponível.

No Linux, isso pode ser evitado de forma bastante simples, ativando o uso de SYN Cookies, um recurso oferecido diretamente pelo Kernel, o que é feito com o comando abaixo, que pode ser incluído no seu script de firewall:

echo 1 > /proc/sys/net/ipv4/tcp_syncookies

Ao ativar o recurso, o sistema passa a responder ao pacote SYN inicial com um cookie, que identifica o cliente. Com isso, o sistema aloca espaço para a conexão apenas após receber o pacote ACK de resposta, tornando o ataque inefetivo. O atacante ainda pode consumir um pouco de banda, obrigando o servidor a enviar um grande volume de SYN Cookies de resposta, mas o efeito sobre o servidor será mínimo.

Concluindo, adicione também as regras que ativam o uso do rp_filter, de forma que o firewall sempre responda aos pacotes na mesma interface da qual eles foram originados, o que previne ataques diversos que tentem tirar proveito da regra que permite conexões na interface de loopback. Outra opção interessante é o bloqueio de pacotes inválidos, o que também melhora a segurança contra ataques diversos, incluindo pacotes enviados sem serem precedidos pelo envio do pacote SYN e da abertura da conexão:

echo 1 > /proc/sys/net/ipv4/conf/default/rp_filter
iptables -A INPUT -m state –state INVALID -j DROP

Juntando tudo, teríamos:

iptables -A INPUT -p icmp –icmp-type echo-request -m limit –limit 1/s -j ACCEPT
echo 0 > /proc/sys/net/ipv4/conf/all/accept_redirects
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts
echo 0 > /proc/sys/net/ipv4/conf/all/accept_source_route
echo 0 > /proc/sys/net/ipv4/ip_forward
echo 1 > /proc/sys/net/ipv4/tcp_syncookies
echo 1 > /proc/sys/net/ipv4/conf/default/rp_filter
iptables -A INPUT -m state –state INVALID -j DROP

Essas regras devem ser adicionadas logo no início do script, de forma que sejam carregadas antes de qualquer regra que abra portas ou permita o acesso de endereços ou faixa de endereços. Com isso, garantimos que elas serão aplicadas a todas as conexões, reduzindo a quantidade de buracos no firewall.


Resumo das regras do Iptables
: Depois desta rodada de exemplos, nada melhor do que um guia mais detalhado dos parâmetros suportados pelo Iptables. Escrever regras de firewall é quase como aprender um novo dialeto. Existem muitas combinações possíveis entre os parâmetros disponíveis e “regras de concordância”, que determinam o que funciona e o que não. Imagine que ao escrever uma nova regra, você está explicando uma idéia. Tente ser claro para que seja entendido ;).

Parâmetros do Iptables:

-A INPUT: Especifica que a regra se aplica a pacotes de entrada, ou seja, pacotes recebidos pelo servidor, em qualquer interface.

-A OUTPUT: A regra se aplica a pacotes de saída, transmitidos pelo próprio servidor.

-A FORWARD: Este parâmetro é usado ao compartilhar a conexão com a Internet, permitindo que os micros da rede local acessem através do servidor. Os pacotes de outros micros, encaminhados pelo servidor, são tratados como “FORWARD”, diferentemente dos pacotes transmitidos pelo próprio servidor, que são tratados como “OUTPUT”. Você pode definir regras diferentes para cada situação.

-p tcp: Especifica que a regra se aplica a pacotes TCP, o que é o mais comum.

-p udp: Alguns serviços usam também portas UDP. Um bom exemplo são os servidores DNS, que escutam tanto na porta 53 TCP, quanto na 53 UDP. Este parâmetro permite definir regras que se aplicam a estes casos, abrindo ou fechando as portas UDP, como em:

iptables -A INPUT -p udp –dport 53 -j ACCEPT

A maioria das regras do Iptables exigem que você especifique o protocolo, fazendo com que você tenha que repetir a regra caso queira abrir uma porta simultaneamente para TCP e UDP. Ao executar algo como “iptables -A INPUT –dport 53 -j ACCEPT” (sem especificar o protocolo), você receberá um erro como:

iptables v1.3.3: Unknown arg `–dport’
Try `iptables -h’ or ‘iptables –help’ for more information.

Nesses casos, você pode usar o parâmetro “-p ALL”, que se aplica simultaneamente aos três protocolos (TCP, UDP e ICMP), sem que você precise incluir uma regra separada para cada um, como no exemplo da regra que bloqueia qualquer tipo de conexão proveniente de um determinado endereço:

iptables -A INPUT -p ALL -s 88.191.79.206 -j DROP

Como as portas UDP também são usadas por alguns serviços, é muito comum bloquear as portas de 0 a 1023 UDP, autorizando apenas as portas que realmente devem ficar abertas, como em:

iptables -A INPUT -p udp –dport 53 -j ACCEPT
iptables -A INPUT -p udp –dport 0:1023 -j DROP

Note que você nunca deve fechar todas as portas UDP, pois as portas altas são usadas aleatoriamente para pacotes de resposta para DNS e diversos outros protocolos. Alguns administradores mais paranóicos fecham todas as portas UDP até a 32000, por exemplo. Não existem muitos problemas em fechar uma faixa maior de portas, desde que você sempre deixe uma larga faixa de portas altas abertas, de forma a receber os pacotes de resposta.

Ao contrário do TCP, não é possível criar uma regra genérica para permitir todos os pacotes de resposta UDP (como a “iptables -A INPUT -p tcp –syn -j DROP”), pois no UDP não são abertas conexões. Os pacotes são simplesmente transmitidos diretamente, sem aviso prévio.

-p icmp: Além do TCP e UDP, existe o protocolo ICMP, usado para pacotes de controle, pings e envio de mensagens. Um exemplo de uso é a regra para desativar a resposta a pings que vimos há pouco. Na verdade, ela atua bloqueando o pedido de ping antes que ele seja repassado ao sistema:

iptables -A INPUT -p icmp –icmp-type echo-request -j DROP

-i eth0: A opção “-i” permite definir a interface onde os pacotes devem ser recebidos ou enviados. Por exemplo, usar uma regra como:

iptables -A INPUT -p tcp -j REJECT

… simplesmente desconectaria seu micro da rede, pois bloquearia comunicações em qualquer interface. Porém, se você especificasse a interface, ele bloquearia apenas pacotes recebidos através dela, como em:

iptables -A INPUT -i eth2 -p tcp -j REJECT

O mesmo se aplica quando você quer permitir conexões em determinadas portas, mas apenas a partir da placa de rede local. Para permitir conexões via SSH apenas a partir da placa eth0, você poderia usar:

iptables -A INPUT -i eth0 -p tcp –dport 22 -j ACCEPT

-o eth0: É similar ao parâmetro “-i”, mas especifica uma interface de saída. Este parâmetro é menos usado, pois normalmente nos preocupamos em impedir que o firewall aceite conexões em determinadas portas, em vez de tentar interceptar as respostas. Entretanto, esta opção pode ser útil em casos em que você precisa fechar uma porta de saída apenas para determinada interface. Este é um exemplo de uso, onde bloqueio pacotes de saída na porta 1863 apenas para a placa eth1:

iptables -A OUTPUT -p tcp -o eth1 –dport 1863 -j DROP

–dport ou –destination-port: Especifica uma porta. O uso mais comum para esta opção é para abrir portas de entrada (e depois aplicar uma regra que fecha as demais), como na regra que abre para conexões na porta 22, que mostrei no exemplo anterior.

-s (source): O parâmetro “-s” permite especificar um endereço IP ou domínio de origem, de forma a aceitar ou recusar as conexões. Embora seja muito fácil forjar endereços IP dentro da rede local, as coisas são muito mais complicadas na Internet. Permitir o acesso a determinadas portas (como a do SSH) apenas para determinados endereços, ou faixas de endereços, é uma medida de segurança interessante em muitas situações.

Este é um exemplo de regra, que abre a porta 631 apenas para hosts dentro da faixa e máscara especificada:

iptables -A INPUT -p tcp -s 72.232.35.0/255.255.255.248 –dport 631 -j ACCEPT

-d (destiny): Destinado ao endereço IP ou domínio citado. É muito usado ao bloquear o acesso a determinados sites a partir dos micros da rede local como, por exemplo:

iptables -A FORWARD -d torrentreactor.net -j REJECT

-m mac –mac-source 00:11:D8:76:59:2E: Esta é a regra que permite especificar endereços MAC dentro de regras do Iptables que vimos há pouco. Ela é uma forma de dificultar o uso de endereços IP falseados para ganhar acesso ao servidor, pois permite relacionar o IP ao endereço MAC da placa instalada. Lembre-se, porém, que ela só pode ser usada em rede local e que os endereços MAC são quase tão fáceis de falsear quanto os endereços IP. Um exemplo de uso seria:

iptables -A INPUT –dport 22 -m mac –mac-source 00:11:D8:76:59:2E -j ACCEPT

–syn: Cria uma regra válida apenas para novas conexões, não impedindo que o outro micro responda a conexões iniciadas pelo servidor, como em:

iptables -A INPUT -p tcp –syn -j DROP

-j: É usado no final de cada regra, especificando uma ação, que pode ser:

-j ACCEPT : Aceita o pacote. Ele é encaminhado ao destino sem passar pelas demais regras.

-j REJECT : Rejeita educadamente o pacote, enviando um pacote de resposta ao emissor. Quando uma porta está fechada em modo reject, o emissor recebe rapidamente uma resposta como “connect to host 192.168.1.1 port 22: Connection refused”.

-j DROP: O DROP é mais enfático. O pacote é simplesmente descartado, sem aviso. O emissor fica um longo tempo esperando, até que eventualmente recebe um erro de time-out.

-j LOG: Este último parâmetro permite logar conexões. É interessante usar esta regra principalmente em portas muito visadas, como a do SSH, pois assim você tem uma lista de todos os endereços que acessaram seu servidor na porta especificada. Para ativar o log, você deve duplicar a regra que abre a porta, usando a opção “-j LOG” na primeira e “-j ACCEPT” na segunda, como em:

iptables -A INPUT -p tcp –dport 22 -j LOG
iptables -A INPUT -p tcp –dport 22 -j ACCEPT

As mensagens são gravadas no arquivo “/var/log/messages” de forma bastante detalhada, incluindo a data e hora da conexão, o IP e MAC do micro que fez a conexão (SRC), além da porta (DPT). Você pode ver o mesmo log, porém com as entradas escritas de forma resumida, usando o comando “dmesg”.

Jun 29 15:49:46 spartacus kernel: IN=eth0 OUT= MAC=00:e0:7d:9b:f8:01:00:15:00:4b:68:db:08:00 SRC=192.168.1.2 DST=192.168.0.1 LEN=52 TOS=0x00 PREC=0x00 TTL=64 ID=32704 DF PROTO=TCP SPT=56848 DPT=22 WINDOW=2164 RES=0x00 ACK URGP=0

Não se assuste com o volume de informações, pois o log inclui todas as tentativas de conexão. O fato de um determinado IP ter aberto uma conexão com a porta 22 do seu servidor, não significa que o usuário realmente obteve acesso ao SSH. Ele pode ter recebido o prompt para digitar a senha, mas isso não significa que ele realmente conseguiu fazer login.

Note que alterar a ordem das regras altera o resultado. Em caso de duas regras conflitantes, vale a que vem primeiro. Por exemplo, ao usar:

iptables -A INPUT -p tcp -j REJECT
iptables -A INPUT -p tcp –dport 22 -j ACCEPT

… a porta 22 permanecerá fechada, pois os pacotes serão descartados pela primeira regra, sem terem chance de serem autorizados pela segunda. É justamente por isso que é sempre necessário colocar as regras menos restritivas primeiro, declarando as portas autorizadas, para só então fechar as demais.

O mesmo se aplica ao logar transmissões. Se você usar algo como:

iptables -A INPUT -p tcp –dport 22 -j ACCEPT
iptables -A INPUT -p tcp –dport 22 -j LOG

… o log não funcionará, pois os pacotes destinados à porta 22 serão aceitos pela primeira regra e não passarão pela segunda, que faz o log. As regras que fazem log devem sempre vir antes das regras que autorizam as conexões.

Sobre o Autor

Redes Sociais:

Deixe seu comentário

X