Linux: Escrevendo scripts de firewall, parte 2

Clique aqui para ler a primeira 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, permitindo apenas que o servidor fosse acessado remotamente via SSH. Vamos agora aprimorar a configuração, gerando um script de firewall mais elaborado, voltado para um servidor de rede local, configurado como gateway da rede.

Na verdade, as regras para compartilhamento da conexão e para a ativação do proxy transparente também são regras de firewall, apenas usadas para um propósito diferente. Em vez de criar dois scripts separados, um para compartilhar a conexão e outro para o firewall, você pode simplesmente adicionar as regras de restrição de acesso no seu script de compartilhamento da conexão, gerando um script unificado. Nesse caso, tome o cuidado de sempre colocar as regras que compartilham a conexão e ativam o proxy transparente antes das regras que bloqueiam conexões.

Este é um exemplo de script de firewall que inclui as regras para compartilhar a conexão e ativar o proxy transparente. Ao usá-lo, comente as linhas que não se aplicam à sua instalação e substitua o “eth1” e o “eth0” pelas interfaces da Internet e da rede local, caso diferente:

#!/bin/bash

iniciar(){

# Compartilha a conexão:

modprobe iptable_nat

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

iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE

echo “Compartilhamento ativado”

# Proxy transparente:

iptables -t nat -A PREROUTING -i eth0 -p tcp –dport 80 -j REDIRECT –to-port 3128

echo “Proxy transparente ativado”

# Permite conexões na interface de rede local e na porta 22:

iptables -A INPUT -i eth0 -j ACCEPT

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

# Regras básicas de firewall:

iptables -A INPUT -i lo -j ACCEPT

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

echo 1 > /proc/sys/net/ipv4/conf/default/rp_filter

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

# Bloqueia as portas UDP de 0 a 1023:

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

echo “Regras de firewall e compartilhamento ativados”

}

parar(){

iptables -F

iptables -t nat -F

iptables -P INPUT ACCEPT

iptables -P OUTPUT ACCEPT

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

echo “Regras de firewall e compartilhamento desativados”

}

case “$1” in

“start”) iniciar ;;

“stop”) parar ;;

“restart”) parar; iniciar ;;

*) echo “Use os parâmetros start ou stop”

esac

Veja que agora a função “parar” do script inclui também os comandos “iptables -t nat -F” e “echo 0 > /proc/sys/net/ipv4/ip_forward”, destinados a limpar as regras de roteamento e desativar o encaminhamento de pacotes quando o script é desativado, parando também o compartilhamento da conexão.

Outra novidade é a regra que bloqueia as portas UDP de 0 a 1023, adicionada no final do script. Ao contrário do TCP, o protocolo UDP não é baseado na abertura de conexões, os dados são simplesmente enviados diretamente, sem serem precedidos por um pacote SYN ou qualquer aviso. Por isso, não é possível utilizar uma regra que bloqueie a abertura de novas conexões UDP mas permita respostas a conexões iniciadas por você, como no caso da “iptables -A INPUT -p tcp –syn -j DROP”. No caso das portas UDP é tudo ou nada, ou seja, ou você mantém a porta aberta para qualquer tipo de pacote, ou a fecha completamente.

Não podemos simplesmente bloquear todas as portas UDP, pois elas são usadas para o recebimento de requisições DNS. Você pode fazer o teste limpando as regras de firewall e usando a regra “iptables -A INPUT -p tcp –syn -j DROP”, que fecha todas as portas UDP; tente navegar e você verá que a resolução de nomes parará de funcionar até que você remova a regra usando o “iptables -F”. Entretanto, podemos bloquear as portas UDP privilegiadas, que são as usadas pelos serviços conhecidos. Este acaba sendo um bom meio-termo.

Forwarding de portas: Ao compartilhar uma conexão via NAT, apenas o servidor recebe conexões vindas da Internet. Os micros da rede local acessam através do servidor e recebem apenas pacotes de resposta. Na maioria dos casos, é justamente isso que você deseja, já que apenas o servidor fica exposto a ataques diretos, enquanto as estações ficam protegidas dentro da rede local. Entretanto, isso nos leva a outro problema, que são os casos em que você realmente deseja que algum dos hosts fique diretamente acessível.

Imagine que você queira que um servidor web, escutando na porta 80 do micro 192.168.1.3 da rede local, fique disponível para a Internet. Como o servidor é o único com um IP válido na Internet, a única forma de fazer com que o 192.168.1.3 fique acessível é fazer com que o servidor “passe a bola” para ele ao receber conexões na porta 80. É justamente isso que fazemos ao configurar o forwarding de portas. Uma vez feita a configuração, sempre que o servidor receber uma conexão qualquer na porta 80 (ou qualquer outra definida por você), ele a repassará para o 192.168.1.3. Isso é feito de forma completamente transparente, de forma que o emissor nem percebe que quem respondeu à solicitação foi outro host.

Essa opção pode ser usada também para permitir que os micros da rede local fiquem com as portas do bittorrent abertas (de forma a baixar arquivos com um melhor desempenho), rodem servidores de games online ou qualquer outra tarefa onde seja necessário manter determinadas portas TCP ou UDP abertas. A limitação é que continua existindo uma única porta 80, uma única porta 22, etc., de forma que apenas um micro da rede interna pode receber cada porta de cada vez.

Veja um exemplo de como redirecionar as portas 6881 a 6889 usadas pelo Bittorrent para o host 192.168.1.10 da rede local:

# Redireciona uma faixa de portas para um micro da rede local:

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

iptables -t nat -A PREROUTING -p tcp -i eth1 –dport 6881:6889 -j DNAT

–to 192.168.1.10

iptables -t nat -A POSTROUTING -d 192.168.1.10 -j SNAT –to 192.168.1.1

Esta regra é um pouco mais complexa, pois trabalha em duas fases. A primeira faz com que o servidor encaminhe todas as conexões que receber na interface e porta especificada para o micro da rede local e a segunda faz com que os pacotes de resposta enviados por ele posam ser encaminhados de volta. Para que ambas funcionem, é necessário usar o comando “echo 1 > /proc/sys/net/ipv4/ip_forward”, que ativa o forwarding de portas. É o mesmo comando que usamos ao compartilhar a conexão.

Nos parâmetros que coloquei em negrito, a “eth1” é a placa de Internet, onde chegam os pacotes, a “6881:6889” é a faixa de portas que está sendo redirecionada e o “192.168.1.10” é o IP do micro dentro da rede local que passa a receber as conexões. Na segunda regra, temos repetido o IP do micro na rede local e, em seguida, o “192.168.1.1” que indica o IP do servidor, dentro da rede local.

Para redirecionar uma única porta, ao invés de uma faixa, basta citar a porta, sem usar os “:”, como em:

# Redireciona uma única porta para um micro da rede local.

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

iptables -t nat -A PREROUTING -p tcp -i eth1 –dport 22 -j DNAT –to 192.168.1.10

iptables -t nat -A POSTROUTING -d 192.168.1.10 -j SNAT –to 192.168.1.1

É possível ainda indicar uma lista de portas (usando a opção -m multiport), como em:

# Redireciona um conjunto de portas

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

iptables -t nat -A PREROUTING -p tcp -i eth1 -m multiport –dport 21,22,80 -j DNAT

–to-dest 192.168.1.10

iptables -t nat -A POSTROUTING -d 192.168.1.10 -j SNAT –to 192.168.1.1

Note que, nos três exemplos, usei o parâmetro “-p tcp”. Embora necessário, ele faz com que a regra se aplique apenas a portas TCP. Caso você precise fazer forwarding de portas UDP, deve alterar o protocolo dentro da regra, como em:

# Redireciona uma porta UDP

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

iptables -t nat -A PREROUTING -p udp -i eth1 –dport 53 -j DNAT –to 192.168.1.10

iptables -t nat -A POSTROUTING -d 192.168.1.10 -j SNAT –to 192.168.1.1

Bloqueando portas de saída: Mais um uso importante para o firewall é bloquear portas de saída, ou seja, bloquear portas no sentido rede local > Internet. Isso permite bloquear o uso de determinados programas que utilizem estas portas.

O MSN, por exemplo, utiliza originalmente a porta 1863. Nas versões recentes ele é capaz de se conectar também através da porta 80 (ou através de sites como o meebo.com, que permitem acessar o MSN diretamente através do navegador). Por isso, ao bloquear a porta 1863, os clientes podem continuar conseguindo se conectar, porém, você obriga o tráfego a passar pela porta 80, onde tem a chance de fazê-lo passar por um servidor Squid, configurado como proxy transparente. Isso permite logar os acessos ou sabotar o sistema de autenticação do MSN, bloqueando os domínios “messenger.hotmail.com” e “webmessenger.msn.com”, além de outros sites que ofereçam clientes via web.

Hoje em dia, cada vez mais programas são capazes de acessar a Web através da porta 80, 443 ou via proxy, o que torna difícil bloqueá-los. Em muitos casos, é preciso usar uma combinação de portas fechadas no firewall, bloqueio a endereços IPs específicos e bloqueio de determinados domínios no Squid.

Ao criar as regras do Iptables, existem duas opções. Bloqueando a porta para “FORWARD”, você impede o acesso a partir dos micros da rede local, que acessam através da conexão compartilhada pelo servidor. Bloqueando para “OUTPUT”, a porta é bloqueada no próprio micro onde o firewall está ativo. Você pode bloquear as duas situações, duplicando a regra:

iptables -A OUTPUT -p tcp –dport 1863 -j REJECT

iptables -A FORWARD -p tcp –dport 1863 -j REJECT

Você pode ainda bloquear intervalos de portas, separando-as por “:”, como em:

iptables -A FORWARD -p tcp –dport 1025:65536 -j REJECT

Como estamos criando regras para os micros da rede local e não para possíveis invasores provenientes da Internet, é aconselhável usar a regra “REJECT” ao invés de “DROP”. Caso contrário, os programas nos clientes sempre ficarão muito tempo parados ao tentar acessar portas bloqueadas, o que vai gerar reclamações e um certo overhead de suporte.

Você pode descobrir facilmente quais portas de saída são utilizados por cada programa fazendo buscas no Google, mas tentar bloquear um a um todos os programas indesejados acaba sendo tedioso. Ao invés disso, você pode experimentar um solução mais radical: inverter a lógica da regra, bloqueando todas as portas de saída e abrindo apenas algumas portas “permitidas”.

O mínimo que você precisa abrir neste caso são as portas 80 e 53 (DNS). A partir daí, você pode abrir mais portas, como a 25 (SMTP), 110 (POP3), 443 (HTTPS) e assim por diante. Um exemplo de configuração neste caso seria:

iptables -A FORWARD -p udp -i eth0 –dport 53 -j ACCEPT

iptables -A FORWARD -p tcp -i eth0 –dport 80 -j ACCEPT

iptables -A FORWARD -p tcp -i eth0 –dport 443 -j ACCEPT

iptables -A FORWARD -p tcp -i eth0 -j LOG

iptables -A FORWARD -p tcp -i eth0 -j REJECT

Veja que todas as regras especificam a interface da rede local (eth0 no exemplo), de onde serão recebidas as conexões dos clientes. Note que não incluí nenhum bloqueio para forwarding de pacotes provenientes da interface eth1 (da Internet), pois a idéia é bloquear diretamente as requisições dos clientes, e não as respostas. Em uma conexão TCP típica, o cliente envia a requisição na porta TCP usada pelo serviço, mas recebe a resposta em uma porta aleatória. Este é um exemplo de entrada no log do Iptables que mostra a resposta a uma conexão HTTP normal. Veja que ela está endereçada à porta 45159 do cliente:

IN=eth1 OUT=eth0 SRC=64.233.169.99 DST=192.168.0.10 LEN=40 TOS=0x00 PREC=0x00 TTL=239 ID=36813 PROTO=TCP SPT=80 DPT=45159 WINDOW=8190 RES=0x00 ACK FIN URGP=0

No caso da porta 53 (DNS), estou especificando o protocolo UDP ao invés de TCP, pois as requisições são feitas usando portas UDP para ganhar tempo. Embora os servidores DNS escutem tanto na porta 53 TCP, quanto UDP, a porta 53 TCP é utilizada apenas para transferência de zonas e não para resolução de nomes, já que ao usar UDP o tempo de resposta é menor. No UDP a requisição é simplesmente respondida da forma mais rápida possível, enquanto que no TCP é necessário abrir e encerrar a conexão.

A regra “iptables -A FORWARD -j LOG” é uma boa opção durante a fase de testes, pois ela faz com que o Iptables logue todos os pacotes que forem encaminhados (tanto envio, quanto resposta), permitindo que você verifique o que está ocorrendo quando algo não estiver funcionando. Você pode acompanhar o log usando o comando “dmesg”.

Colocado nesta posição (depois das regras que autorizam as conexões nas portas 53 e 80), ele vai mostrar apenas as requisições bloqueadas pelo firewall, dando-lhe a chance de acompanhar os acessos dos clientes e permitir portas adicionais sempre que necessário.

Por exemplo, esta estrada (no log) mostra uma tentativa de conexão de um cliente MSN rodando no micro “192.168.1.10” que foi bloqueada pelo firewall:

IN=eth0 OUT=eth1 SRC=192.168.1.10 DST=207.46.28.77 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=21328 DF PROTO=TCP SPT=38119 DPT=1863 WINDOW=5840 RES=0x00 SYN URGP=0

A opção “DTP” indica a porta usada. Se quisesse autorizar o uso do programa, você adicionaria a regra “iptables -A FORWARD -p tcp -i eth1 –dport 1863 -j ACCEPT” em seu script.

Outra opção, para não precisar abrir tantas portas e ter um melhor controle sobre o tráfego de saída é usar um servidor Squid configurado como proxy transparente (interceptando o tráfego da porta 80) e rodar servidores locais para DNS e e-mail (você pode configurar um servidor Postfix como sistema satélite, de forma que ele envie os e-mails dos usuários da rede usando o SMTP do provedor), de forma que qualquer acesso precise necessariamente passar por algum dos serviços ativos no servidor, sujeito a log e aos bloqueios que configurar.

Neste caso, desabilite o compartilhamento da conexão (ou bloqueie o forward de todas as portas) e configure os clientes para utilizarem o IP do servidor como DNS, servidor SMTP, POP e outros serviços que tenha ativado. Mesmo ao ser configurado como proxy transparente, o Squid continua funcionando como um proxy tradicional, através da porta 3128. Você pode configurar clientes de FTP e outros programas com suporte a proxy para acessarem através dele. A vantagem sobre o acesso direto é que ao passar pelo proxy, tudo fica registrado e todo acesso precisa passar pelos filtros de domínios, formatos de arquivos, limitação de banda, etc. definidos por você.

Complementando o bloqueio de portas, você pode também bloquear o acesso de determinados endereços IP, como em:

# Bloqueia o acesso à web a partir de um determinado IP

iptables -A FORWARD -p tcp -s 192.168.1.67 -j REJECT

Esta regra deve ir logo no início do script, antes das regras que abrem as portas de saída, caso contrário não surtirá efeito. Lembre-se de que o Iptables processa as regras seqüencialmente: se uma compartilha a conexão com todos os micros da rede, não adianta tentar bloquear para determinados endereços depois. As regras com as exceções devem sempre vir antes da regra mais geral.

Bloqueando domínios: É possível ainda bloquear ou permitir com base no domínio, tanto para entrada quanto saída. Isso permite bloquear sites e programas diretamente a partir do firewall, sem precisar instalar um servidor Squid e configurá-lo como proxy transparente. Nesse caso, usamos o parâmetro “-d” (destiny) do Iptables, seguido do domínio desejado.

Para bloquear os acessos ao Orkut, por exemplo, você usaria as regras:

iptables -A OUTPUT -d www.orkut.com -j REJECT

iptables -A FORWARD -d www.orkut.com -j REJECT

A primeira linha bloqueia pacotes de saída destinados ao domínio, ou seja, impede que ele seja acessado a partir da própria máquina local. A segunda linha bloqueia o forward de pacotes destinados a ele (domínio), ou seja, impede que outras máquinas da rede local, que acessam através de uma conexão compartilhada, acessem o domínio.

Se for paranóico, você pode usar também a regra:

iptables -A INPUT -s www.orkut.com -j DROP

Esta regra impede também que qualquer pacote proveniente do orkut.com chegue até a sua máquina. Como disse, é apenas para paranóicos. 😉

Originalmente, o Iptables sabia trabalhar apenas com endereços IP. A possibilidade de criar regras baseadas em domínios é um recurso um pouco mais recente, onde o firewall faz um lookup do domínio, de forma a descobrir qual é o IP atual e assim poder bloqueá-lo. Você pode verificar o IP usado pelo servidor de um determinado domínio usando o comando “dig” (que no Debian faz parte do pacote “dnsutiuls”), como em:

$ dig orkut.com

A vantagem de criar as regras do firewall baseadas em domínios é que elas são automaticamente atualizadas caso o servidor do site mude de endereço.

Ao bloquear o “orkut.com” no Iptables, você automaticamente bloqueia o “www.orkut.com” ou qualquer outra variante do domínio que leve ao mesmo servidor. A principal limitação é que a regra não se aplica a subdomínios hospedados em diferentes servidores. Por exemplo, você pode bloquear o domínio “uol.com.br”, mas isso não bloqueará o “tvuol.uol.com.br”, que é hospedado em um servidor separado. Em casos como este, a única solução é bloquear ambos.

Assim como no caso do Squid, ao decidir bloquear o acesso a sites ou endereços diversos utilizando o Iptables, você vai logo acabar com uma lista relativamente grande de endereços a bloquear. Diferente do Squid, o Iptables não oferece uma opção para carregar a lista dos endereços que devem ser bloqueados a partir de um arquivo de texto, mas isso pode ser resolvido com um script simples, que leia o conteúdo do arquivo e gere as regras correspondentes.

Comece criando um arquivo de texto contendo os domínios ou endereços IP que deseja bloquear, um por linha, como em:

www.orkut.com

www.myspace.com

www.facebook.com

Em seguida, adicione esta função no início do seu script de firewall, especificando o arquivo de texto com os endereços:

for end in `cat /etc/bloqueados`

do

iptables -A OUTPUT -d $end -j REJECT

iptables -A FORWARD -d $end -j REJECT

done

Esta função usa o comando cat para ler o conteúdo do arquivo, gerando um par de regras do Iptables para cada um deles. O laço continua até gerar regras para todos os endereços incluídos no arquivo.

Confira a terceira parte em: https://www.hardware.com.br/tutoriais/linux-escrevendo-scripts-firewall3/

Sobre o Autor

Redes Sociais:

Deixe seu comentário

X