Firewalls iptables dinâmicos

Segurança de redes flexível (e divertida)

Por Daniel Robbins Precidente e CEO da Gentoo Technologies, Inc. - Abril de 2001

Firewalls são bons e divertidos, mas o que você faz quando precisa fazer alterações rápidas e complexas nas suas regras de firewall? Fácil, Use os scripts de firewall dinâmico de Daniel Robbins, que são demonstrados neste artigo. Você pode usar estes scripts para melhorar a sua segurança de rede e responsividade, e para inspirar seus próprios projetos criativos.

A melhor forma de ver os benefícios dos scripts de firewall dinâmico é vê-los em ação. Para isto, vamos imaginar que sou um sysadmin em um ISP, e recentemente configurei um firewall baseado no Linux para proteger meus clientes e sistemas internos de usuários mailiciosos na Internet. Para isto, meu firewall utiliza a nova funcionalidade de iptables stateful do Linux 2.4 para permitir que novas conexões partindo da rede sejam estabelecidas pelos meus clientes e servidores, e também para permitir novas conexões entrantes, mas somente para serviços "públicos", como web, ftp, ssh e SMTP. Como eu uso um projeto deny-by-default, qualquer conexão da Internet para serviços não públicos, como o proxy Squid ou servidor Samba são automaticamente rejeitadas. Desta forma, eu tenhum um firewall decente que oferece um bom nível de proteção para todo mundo em meu ISP.

Nas preiras semanas, o firewall funcionou bem, mas então algo ruim aconteceu: Bob, meu arqui-nêmesis (que trabalha para um ISP concorrente), decide que quer inundar minha rede com pacotes em uma tentativa de negar os serviços aos meus clientes. Infelizmente, Bob estudou cuidadosamente meu firewall e enquanto eu estava protegendo muitos serviços internos, as portas 25 e 80 devem ter acesso público para qe eu possa receber email e servir solicitações HTTP. Bob decidiu aproveitar-se deste fato lançando um ataque que consome largura de banda contra meu servidor de mail e web.

Cerca de um minuto após Bob iniciar seu ataque, eu notei que meus links começaram a ficar saturados com pacotes. Depois de dar uma olhada na situação com o tcpdump, pude determinar que este era outro ataque de Bob, e descobri quais os endereços IP que ele estava usando para lançar o mesmo. Com esta informação, tudo que eu precisava era bloquear estes endereços IP, e isto deveria resolver o problema -- uma solução simples, ou pelo menos assim eu pensei.

Respondendo a um ataque

Eu rapidamente carreguei meu script de configuração de firewall no vi e comecei a haquear minhas regras de iptables, modificando meu firewall de forma que ele bloqueasse aqueles pacotes maus do Bob. Depois de um minuto mais ou menos, eu encontrei o lugar exato para acrescentar as regras DROP apropriadas, e acrescentei elas. A seguir, eu reiniciei o firewall... oooops, cometi um pequeno erro quando acrescentei as regras. Carreguei os scripts de firewall novamente, corrigi o problema, e trinta segundos depois o firewall estava preparado para bloquear o ataque do mês de Bob. No início, parecia que eu tinha frustrado o ataque... até que os telefones do helpdesk começaram a tocar. Aparentemente, Bob conseguiu causar uma disrupção em minha rede por cerca de 10 minutos, e agora meus clientes estavam ligadno para descobrir o que estava acontecendo. Pior, após alguns minutos, eu notei que os links começaram a ficar saturados. Desta vez, Bob parecia estar usando um novo conjunto de endereços IP para seu ataque. Em resposta, Comecei a haquear febrilmente os scripts de firewall, exceto que desta vez eu estava entrando em pânico -- talvez minha solução não era tão boa afinal de contas.

Aqui está o problema com o cenário acima. Apesar de ter um firewall decente e também de ter identificado rapidamente a causa do problema de rede, eu não conseguia modificar o comportamento do firewall para responder às ameaças em tempo. Quando sua rede está sob ataque, você quer poder responder imediatamente, e ser forçado a haquear seu script de firewall principal quando em pânico não só é estressante, como bastante ineficiente.

ipdrop

Seria bem melhor se eu tivesse um script especial de nome "ipdrop" que fosse especialmente escrito para apenas inserir as regras necessárias para bloquear os endereços IP que eu especificasse. Com um script assim, bloquear um firewall não é mais uma tarefa de dois minutos, mas de cinco segundos. E desde que o script me protege da tarefa de editar as regras à mão, ele elimina uma grande fonte de erros. E tudo que eu preciso fazer é determinar qual endereços Ip que desjo bloquear, e escrever:

# ipdrop 129.24.8.1 on
IP 129.24.8.1 drop on.

Imediatamente, o script ipdrop irá bloquear 129.24.8.1, o IP do mal desta semana usado por Bob. Este script aumentou dramaticamente nossas defesas, por que agora um bloqueio de IP não exige esforço. Vamos dar uma olhada na minha implementação do script ipdrop:

Script bash ipdrop

#!/bin/bash

source /usr/local/share/dynfw.sh

args 2 $# "${0} IPADDR {on/off}" "Drops packets to/from IPADDR. Good for obnoxious
				  networks/hosts/DoS"

if [ "$2" == "on" ]
then
	#rules will be appended or inserted as normal
	APPEND="-A"
	INSERT="-I"
	rec_check ipdrop $1 "$1 already blocked" on
	record ipdrop $1
elif [ $2" == "off" ]
then
	#rules will be deleted instead
	APPEND="-D"
	INSERT="-D"
	rec_check ipdrop $1 "$1 not currently blocked" off
	unrecord ipdrop $1
else
	echo "Error: \"off\" or \"on\" expected as second argument"
	exit 1
fi

#block outside IP addresss that's causing problems
#attacker's incoming TCP connections will take a minute or so to time out,
#reducing DoS effectiveness.

iptables $INSERT INPUT   -s $1 -j DROP
iptables $INSERT OUTPUT  -d $1 -j DROP
iptables $INSERT FORWARD -d $1 -j DROP
iptables $INSERT FORWARD -s $1 -j DROP

echo "IP ${1} drop ${2}."

ipdrop: a explicação

Se você olhar as quatro últimas linhas em destaque, você verá os comandos que inserem as regras apropriadas nas tabelas do firewall. Como pode ser visto, a definição da variável de ambiente $INSERT varia, dependendo se estamos rodando o script no modo "on" ou "off". Quando as linhas do iptable são executadas, as regras em particular serão inseridas ou apagadas apropriadamente.

Vamos agora dar uma olhada na função das regras em si, que devem trabalhar perfeitamente com qualquer tipo de firewall existente, ou mesmo em um sistema sem firewall -- tudo que é necessário é suporte ao iptables no seu kernel 2.4. Nós bloqueamos os pacotes que chegam do IP ruim (a primeira linha iptables), bloquear pacotes destinados ao IP ruim (linha iptables seguinte), e então bloquear o repasse de pacotes em qualquer direção para este endereço IP em particular (as duas últimas linhas iptables. Uma vez que estas regras tenham sido acrescentadas, seu sistema simplesmente irá descartar os pacotes que estiverem em qualquer uma destas categorias.

Outra nota rápida: você deve ter notado chamadas a "rec_check", "unrecord", "record", e "args". Estas são funções bash especiais de suporte definidas em "dynfw.sh". A função "record" registra o ip bloqueado no arquivo /root/.dynfw-ipdrop, enquanto a função "unrecord" remove a entrada de /root/.dynfw-ipdrop. A função "rec_check" é usada para abortar o script com uma mensagem de erro se você tentar re-bloquear um endereço IP já bloqueado, ou desbloquear um endereço IP que não está sendo bloqueado. A função "args" verifica se recebemos o número correto de argumentos, e também trata da impressão de alguma informação de ajuda. Eu criei um arquivo dynfw-1.0.tar.gz que contém todas estas ferramentas. Veja a sessão Recursos no fim deste artigo para mais informações.

tcplimit

O próximo script de firewall dinâmico é útil se você precisa limitar o uso de um serviço de rede TCP em particular, possibelmente algo que gere uma carga pesada de CPU no seu lado. Com o nome de "tcplimit", este script pega uma porta TCP, uma taxa, uma escala, e "on" ou "off" como argumentos:

# tcplimit 873 5 minute on
Port 873 new connection limit (5/minute, burst=5) on.

O script tcplimit utiliza o novo módulo iptables "state" (certifique-se de ter habilitado o mesmo no kernel ou de ter carregado o módulo) para permitir somente um certo número de novas conexões entrantes em um período específico de tempo. Neste exemplo, o firewall irá permitir somente cinco novas conexões ao meu servidor rsync (porta 873) por minuto -- e é possível especificar o número desejado de conexões por segundo/minuto/hora ou dia, conforme necessário.

O tcplimit oferece uma boa forma de limitar serviços não-essenciais -- desta forma uma inundação de tráfego a um serviço não-essencial não irá interromper sua rede ou servidor. No meu caso, eu usei tcplimit para configurar um limite superior máximo para o uso do rsync para evitar que minha linha DSL fique saturada com muitas conexões rsync. Serviços limitados por conexão são registrados em /root/.dynfw-tcplimit, e se queremos desligar o novo limite de conexão, podemos simplesmente escrever:

# tcplimit 873 5 minute off
Port 873 new connection limit off.

O tcplimit funciona criando uma nova linha da tabela "filter". Esta nova linha irá rejeitar todos os pacotes que excedam nosso limite especificado. A seguir, uma regra será inserida na linha INPUT que redireciona todas as NOVAS conexões destinada a porta em questão (873 neste caso) para esta linha especial, efetivamente colocando um limite em novas conexões entrantes, ao mesmo tempo sem afetar os pacotes que são parte de alguma conexão já estabelecida.

Quando o tcplimit é desligado, a regra e a linha INPUT especiais são excluídas. Este é o tipo de coisa legal que realmente destaca a importância de ter um script bem testado e confiável para administrar as regras de firewall para você. Da mesma forma que com o ipblock, o script tcplimit deve ser compatível com qualquer tpo de firewall, e até mesmo para quem não está usando firwal, desde que a funcionalidade iptables apropriada esteja habilitada no kernel.

host-tcplimit

O host-tcplimit é bastante parecido com o tcplimit, mas ele limita novas conexões TCP que venham de um endereço IP em particular destinadas a uma porta TCP em particular do seu servidor (ou servidores). O host-tcplimit é particularmente útil para prevenir uma pessoa em particular de abusar de seus recursos de rede. Por exemplo, digamos que você esteja rodando um servidor CVS, e você descobre que um novo desenvolvedor em particular parece ter configurado um script que atualiza suas fontes com o repositório a cada 10 minutos, usando uma quantia enorme de recursos de rede desnecessariamente durante um dia. Entretanto, enquanto você está escrevendo um email para explicar o erro que ele cometeu, você recebe uma mensagem assim:

Olá caras!

Estou realmente excitado de fazer parte do seu projeto de desenvolvimento. Eu
acabei de configurar um script para atualizar minha cópia local do código a
cada 10 minuts. Estou para sair em um cruzeiro de duas semanas, mas quando eu
voltar, meus fontes estarão totalmente atualizados e estarei pronto para
começar a ajudar! Estou saindo agora mesmo... vejo vocês em duas semanas!

Sinceramente,

Mr. Newbie

Para situações como esta, um simples comando host-tcplimit irá resolver o problema:

# host-tcplimit 1.1.1.1 2401 1 day on

Agora, Mr. Newbie (endereço IP 1.1.1.1) está limitada a uma conexão CVS (porta 2401) por dia, economizando muita banda de rede.

user-outblock

O último e possivelmente o mais intrigante de todos meus scripts de firewall dinâmico é o user-outblock. Este script fornece uma forma idela de permitir que um determinado usuário faça telnet ou ssh para seu sistema, mas não permite que este usuário estabeleça nenhuma outra conexão saindo da sua rede, via linha de comando. Vejamos um exemplo de uma situação em que o user-outblock é útil. Digamos que uma família em particular tem uma conta em meu ISP. Mãe e Pai usam um cliente de email gráfico para ler suas correspondências e ocasionalmente navegar na Web, mas seu filho quer ser um hacker, e geralmente usa seu acesso shell para fazer coisas ruins aos computadores de outras pessoas.

Um dia, você descobre que ele estabeleceu várias conexões ssh com vários sistemas que parecem pertencer ao exército paquistanês -- ouch! Você decide ajudar a dirigir este jovem a atividades mais benéficas, e faz o seguinte:

Primeiro, faz uma auditoria e certifica-se de remover o bit suid de todos os binários dos aplicativos de rede, como o ssh:

# chmod u-s /usr/bin/ssh

Agora, qualquer processo que ele tentar usar para interagir com a rede terá o seu UID. Neste ponto, você pode usar o user-outblock para bloquear todas as conexões TCP que saem de sua máquina que forem iniciada com o UID dele (que por acaso é 2049):

# user-outblock 2049 on
UID 2049 block on

Agora, ele pode fazer o logon e ler seu email, mas não pode usar nossos servidores para estabelecer conexões ssh ou outras. Agora, ele pode instalar um cliente ssh em seu computador doméstico. Entretanto, não é muito difícil criar um outro script de fierwall dinâmico que limite o acesso de seu PC doméstico para Web, mail, e conexões ssh externas (somente para seus servidores).

Recursos

Sobre o autor

Morando em Albuquerque, Novo México, Daniel Robbins é o Presidente/CEO da Gentoo Technologies, Inc., o criador do Gentoo Linux, um Linux avançado para o PC, e o sistema Portage, um sistema de portas de última geração para o LInux. Ele também tem servido como autor para os livros da Macmillan Caldera OpenLinux Unleashed, SuSE Linux Unleashed, e Samba Unleashed. Daniel está envolvido com computadores de alguma forma desde seu segundo grau, quando ele foi exposto pela primeira vez à linguagem de programação Logo, bem como a uma dose podencialmente perigosa de Pac Man. Isto provavelmente explica por que ele tem trabalhado como Lead Graphig Artist na SONY Electronic Publishing/Psygnosis. Daniel gosta de gastar tempo com sua esposa, Mary, e sua filhinha, Hadassah. Você pode entrar em contato com ele no email drobbins@gentoo.org.

1