Linux: Escrevendo scripts de firewall, parte 1

Em março, publiquei um tutorial sobre a configuração de firewall no Windows, abordando o Windows firewall e o Comodo. Dessa vez, veremos detalhes sobre a criação e manutenção de scripts de firewall no Linux, abordando diversos cenários de uso, incluindo máquinas desktop, servidores de rede local e servidores de Internet, com um grande volume de dicas de segurança.

Quando falamos em “firewall”, muitas vezes vem à mente um dispositivo dedicado, colocado entre o servidor (ou o switch da rede, no caso de um rede local) e a Internet. Embora firewalls dedicados sejam também bastante comuns e sejam os preferidos em grandes redes, onde o investimento é justificável, você pode obter um nível de segurança similar simplesmente usando os recursos nativos do sistema, configurando o Iptables. O firewall trabalha como um fiscal de alfandega, que analisa todos os pacotes que chegam, decidindo o que deve passar e o que deve ficar retido, através de um conjunto de regras definidas.

Um servidor destinado a compartilhar a conexão com a Internet e proteger os micros da rede local, por exemplo, usaria as regras de compartilhamento da conexão via NAT, combinadas com uma regra para permitir o acesso dos micros da rede local e regras para bloquear o tráfego proveniente da Internet. Você poderia incluir regras para abrir a porta do SSH, ou redirecionar alguma porta específica para um micro dentro da rede local rodando algum software que precisasse de portas de entrada, mas todas as demais portas ficariam fechadas.

Em um servidor de Internet, por sua vez, você não pode simplesmente bloquear todas as portas, já que, por definição, ele deve receber requisições dos clientes e disponibilizar as páginas hospedadas, entre outros recursos. Você faria então uma configuração mais cuidadosa, listando os serviços que devem ficar disponíveis, abrindo as portas referentes a eles e fechando todas as demais. As portas básicas para um servidor web, por exemplo, seriam as portas 22 (do SSH), a porta 53 (do servidor DNS) e as portas 80 e 443, usadas pelo servidor web.

Existem muitos firewalls gráficos for Linux, como o Shorewall e o Firestarter. Eles variam em nível de facilidade e recursos, oferecendo uma interface amigável e gerando as regras do Iptables de acordo com a configuração feita. Você pode escolher entre usar o programa que melhor atenda suas necessidades ou configurar diretamente o Iptables com as regras desejadas, como aprenderemos ao longo desta série de tutoriais. Nesse caso, você pode formular as regras diretamente, definindo condições onde os pacotes serão aceitos ou recusados, como em:

# iptables -A INPUT -s 192.168.1.0/255.255.255.0 -j ACCEPT

Estes comandos seguem uma sintaxe comum: tudo começa com o comando “iptables“, que é o utilitário responsável por ler os parâmetros e atualizar a configuração do firewall. Em seguida, vem uma condição, indicada pela opção “-A”. Neste exemplo usei “INPUT -s 192.168.1.0/255.255.255.0”, que se aplica a qualquer pacote de entrada (INPUT), proveniente dos micros da rede local (192.168.1.0/255.255.255.0). Como não especifiquei o protocolo, a regra permitirá o uso dos três protocolos, ou seja, TCP, UDP e ICMP, sem restrições.

Note que aqui estou especificando uma faixa de endereços e a máscara de sub-rede (usada na rede local), fazendo com que a regra se aplique a todos os pacotes provenientes dela. No final, é preciso dizer o que fazer com os pacotes que se enquadrarem nesta condição, indicando uma ação. O “-j ACCEPT” diz que estes pacotes devem ser aceitos.

Se, por outro lado, quisesse apenas bloquear um endereço específico, usaria o parâmetro “-j REJECT”, como em:

# iptables -A INPUT -s 192.168.1.23 -j REJECT

À primeira vista, configurar o firewall via linha de comando parece bastante complicado, assim como o arquivo de configuração original do Squid, com suas 3.000 e tantas linhas, mas, as coisas ficam bem mais simples se começarmos com um script simples e formos incluindo novas regras aos poucos.

A primeira coisa a ter em mente é que, assim como o Squid, o Iptables processa as regras de forma sequencial, permitindo ou recusando as conexões conforme encontra uma regra que faz referência a ela. Se uma determinada regra diz que os pacotes provenientes de um determinado endereço devem ser aceitos e outra logo depois diz que eles devem ser recusados, como neste exemplo:

# iptables -A INPUT -s 192.168.1.10 -j ACCEPT

# iptables -A INPUT -s 192.168.1.10 -j REJECT

… vale a primeira, já que ao serem autorizados por ela, os pacotes são imediatamente aceitos, sem passarem pela segunda. Ao formular as regras para seu script de firewall, você deve colocar as regras mais específicas primeiro, deixando as regras mais gerais por último. Se você quer permitir o acesso de todos os endereços provenientes da rede local, bloqueando apenas um endereço específico, você usaria:

# iptables -A INPUT -s 192.168.1.10 -j REJECT

# iptables -A INPUT -s 192.168.1.0/255.255.255.0 -j ACCEPT

Além de serem diretamente digitadas no terminal, as regras podem ser incluídas dentro de scripts, de forma que você possa ativar o firewall rapidamente e/ou configurar o sistema para carregá-lo automaticamente durante o boot. O script de firewall nada mais é do que um shell script comum, contendo os comandos que devem ser executados, um por linha. Você pode, por exemplo, criar o arquivo “/etc/init.d/firewall“. Outra opção seria simplesmente adicionar os comandos no final do arquivo “/etc/rc.local”, de forma que eles sejam executados durante o boot.

Este é um exemplo de mini-script de firewall que pode ser usado em um desktop que simplesmente acessa a Internet como cliente, sem rodar nenhum servidor nem compartilhar a conexão com outros micros:

#!/bin/sh

iptables -A INPUT -i lo -j ACCEPT

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

A idéia aqui é que o micro possa acessar a Internet sem ficar vulnerável a acessos externos. Estes dois comandos fazem isso da forma mais simples possível.

A primeira linha orienta o firewall a deixar passar os pacotes enviados através da interface de loopback (-i lo -j ACCEPT). É importante que esta linha (ou outra com o mesmo efeito) sempre seja usada em qualquer script de firewall que termine bloqueando todas as conexões, pois, no Linux, a interface de loopback é usada para comunicação entre diversos programas. Para ter uma idéia, todos os programas gráficos a utilizam para se comunicarem com o servidor X, os programas do KDE a utilizam para trocar mensagens entre si e assim por diante. Sem esta regra, muita coisa deixa de funcionar corretamente.

Depois de abrir o firewall para as mensagens locais, usamos a segunda regra para bloquear todas as novas conexões vindas de fora. O “–syn” faz com que o firewall aplique a regra apenas para tentativas de abrir novas conexões (alguém tentando acessar o servidor SSH que você esqueceu aberto, por exemplo), sem entretanto impedir que servidores remotos respondam a conexões iniciadas por você. Isso permite que você continue navegando e acessando compartilhamentos em outros micros da rede local, com poucas limitações.

Estas duas regras podem ser usadas como base para criar um firewall de bloqueio, onde você diz as portas que gostaria de abrir e ele fecha todas as demais. Ou seja, o firewall fecha por padrão todas as portas, com exceção das que você disser explicitamente que deseja manter abertas. Isso garante uma configuração de firewall bastante segura com um mínimo de dor de cabeça.

Para testar, você pode executar o script no seu micro, ou em qualquer outro PC da rede e tentar acessá-lo via SSH (ou qualquer outro serviço ativo). Como usamos o parâmetro “DROP” na segunda regra, o PC simplesmente ignorará o chamado, fazendo com que o cliente fique um longo tempo tentando abrir a conexão, para depois exibir um erro de timeout, como em:

$ ssh 192.168.1.21
ssh: connect to host 192.168.1.21 port 22: Connection timed out

Para desativar o firewall e voltar a aceitar conexões, use o comando “iptables -F”, que limpa as regras do Iptables:

# iptables -F

A partir deste script básico, você pode adicionar novas regras, abrindo portas, direcionando faixas de portas para micros da rede interna, fechando portas de saída, de forma a bloquear o uso de determinados programas e assim por diante.

Imagine que você está configurando o firewall do servidor da rede. Ele tem duas placas de rede, uma para a rede local e outra para a Internet. Você precisa que ele fique acessível sem limitações dentro da rede local, mas quer manter tudo fechado para quem vem da Internet.

Nesse caso, você poderia usar a regra que mostrei há pouco no seu script de firewall:

# Abre para uma faixa de endereços:

iptables -A INPUT -s 192.168.1.0/255.255.255.0 -j ACCEPT

O “192.168.1.0” indica a faixa de endereços da rede local. A máscara “255.255.255.0” indica que a última parte do endereço muda, ou seja, os micros da rede local usam endereços entre 192.168.1.1 e 192.168.1.254. Tudo o que vier deles é aceito.

Note que esta faixa de endereços não é roteável, ou seja, ela simplesmente não existe na Internet. Não existe a possibilidade de algum engraçadinho de outro estado tentar configurar seu micro para usar esta faixa de endereços e enganar a regra do firewall.

Como uma proteção adicional, as versões recentes do Iptables são capazes de ignorar pacotes aparentemente destinados a uma interface quando eles chegam em outra. Com duas placas, onde uma está ligada à rede local (usando a faixa 192.168.1.x) e outra à Internet, o firewall não aceitará que um pacote falseado, proveniente da Internet, com endereço de emissor “192.168.1.3” (por exemplo), seja encaminhado a um micro da rede local, pois ele sabe que pacotes com este endereço de emissor devem chegar apenas pela placa ligada à rede local.

Se o servidor possuir duas placas de rede, você poderia tornar a regra mais à prova de falhas especificando a interface de origem em vez da faixa de endereços, usando o parâmetro “-i”. Com isso, o firewall é instruído a aceitar pacotes recebidos na interface de rede local, independentemente da faixa de endereços usada. Embora funcione de forma diferente, ela tem a mesma função da regra anterior. Ao usá-la, não esqueça de substituir o “eth0” pela interface de rede local, caso diferente:

# Aceita tudo na interface de rede local:

iptables -A INPUT -i eth0 -j ACCEPT

Se você é um administrador paranóico, pode também combinar as duas condições, de forma que o firewall verifique tanto a interface de origem quanto a faixa de endereços, aceitando a conexão apenas se as duas condições forem satisfeitas. Nesse caso, a regra seria:

# Verifica tanto a interface quanto a faixa de endereços de origem:

iptables -A INPUT -s 192.168.1.0/255.255.255.0 -i eth0 -j ACCEPT

O parâmetro “-s” (source), usado na regra anterior, pode também ser usado para permitir endereços ou faixas de endereços da Internet. Imagine que você precise dar acesso aos hosts de uma das filiais da empresa, onde usam um link dedicado, com uma faixa inteira de endereços, que vai do “200.220.234.1” até o “200.220.234.254”. Você poderia abrir o firewall para conexões a partir da faixa “200.220.234.0” (como em: iptables -A INPUT -s 200.220.234.0/255.255.255.0 -j ACCEPT), de forma que o firewall permitisse acessos vindos de lá, mas continuar bloqueando o restante. Você pode abrir o firewall para várias faixas de endereços distintas, basta repetir a linha adicionando cada uma das faixas desejadas.

Imagine agora que este servidor foi instalado na sede de uma empresa para a qual você presta serviços. Você precisa acessá-lo de vez em quando para corrigir problemas, mas, naturalmente, vai querer fazer isso via Internet, sem precisar se deslocar até lá. Você pode configurar o firewall para abrir a porta 22, usada pelo SSH, adicionando a regra:

# Abre uma porta (inclusive para a Internet):

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

Note que esta regra abre a porta 22 para todo mundo. Lembre-se de que todo servidor disponível para a Internet é um risco potencial de segurança, por isso, só abra as portas para os servidores que você realmente for utilizar. Do ponto de vista da segurança, o ideal seria usar um par de chaves, protegidas por uma passphrase, para acessar o servidor SSH e configurá-lo para não aceitar logins com senha (apenas com chaves), como você pode ver nesta dica: https://www.hardware.com.br/dicas/ssh-login-automatico.html

Ao abrir várias portas, você pode utilizar o parâmetro “-m multiport” para especificar todas de uma vez, separadas por vírgula, sem precisar colocar uma em cada linha. Para abrir as portas 22, 80 e 443, por exemplo, você usaria a regra abaixo:

# Abre um conjunto de portas:

iptables -A INPUT -m multiport -p tcp –dport 22,80,443 -j ACCEPT

Se você presta suporte a partir de uma conexão com IP fixo, pode tornar a regra mais específica, permitindo o acesso apenas a partir deste endereço IP específico, como em:

# Abre uma porta para um IP específico:

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

Em um micro doméstico, você pode abrir também as portas usadas pelo bittorrent (6881 a 6889) ou portas usadas por jogos multiplayer, por exemplo. Para abrir um intervalo de portas, indique a primeira e a última porta, separadas por “:”, como em:

# Abre um intervalo de portas:

iptables -A INPUT -p tcp –dport 6881:6889 -j ACCEPT

Além de trabalhar com endereços IP, é possível criar regras baseadas também em endereços MAC. Isso permite adicionar uma camada extra de proteção ao criar regras para a rede local. Para isso, usamos o parâmetro “-m mac –mac-source”, seguido pelo endereço MAC da placa do host desejado. Para permitir que o host “192.168.1.100” tenha acesso ao servidor, mas apenas depois de verificado também o endereço MAC da interface de rede, você usaria uma regra como:

# Verifica tanto o endereço IP quanto o MAC antes de autorizar a conexão:

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

Note que agora, além do IP, especificamos o endereço MAC da placa. As duas regras são usadas em conjunto, de forma que o acesso é permitido apenas caso as duas informações estejam corretas. Isso dificulta as coisas para alguém que queira acessar o servidor trocando o IP da máquina. Você pode descobrir o MAC das máquinas da rede usando o próprio ifconfig ou o comando “arp -a”.

Note que limitar o acesso com base no endereço MAC adiciona uma camada extra de proteção, mas não é infalível. O endereço MAC pode ser trocado de forma quase tão simples quanto o endereço IP e, sniffando a rede, é possível descobrir os endereços IP e MAC dos micros com uma certa facilidade.

No Linux, você pode trocar o endereço MAC da placa de rede usando os comandos:

# ifconfig eth0 down

# ifconfig eth0 hw ether 00:11:D8:76:59:2E

# ifconfig eth0 up

Como vê, basta especificar o endereço desejado. O Iptables não é capaz de diferenciar máquinas com os endereços MAC falseados das reais, de forma que se alguém desconectasse o micro “192.168.1.100” da rede (desconectando o cabo no switch, por exemplo) e configurasse outro para usar o mesmo endereço IP e o mesmo MAC, poderia acessar o servidor bipassando a regra de firewall. A única forma de ter uma segurança completa seria utilizar o SSH ou outro protocolo que utilize um algoritmo robusto de encriptação para o login e a transmissão dos dados.

Lembre-se de que o firewall é uma primeira barreira de proteção, mas não é uma garantia por si só. É preciso combiná-lo com outras camadas de segurança para ter um servidor completamente seguro.

Outra limitação é que as regras baseadas em endereços MAC podem ser usadas apenas dentro da rede local. Os roteadores descartam os endereços MAC dos pacotes antes de transmiti-los para a Internet, enviando apenas o endereço IP. Ao acessar através de uma conexão compartilhada, todos os pacotes provenientes da Internet chegam com o endereço MAC do gateway da rede.

Este é um exemplo de script completo, incluindo algumas regras adicionais para evitar ataques comuns. Ele inclui as funções para aceitar os comandos “start”, “stop” e “restart”, de forma a se comportar como se fosse um serviço de sistema:

#!/bin/bash

iniciar(){

# Abre para a faixa de endereços da rede local:

iptables -A INPUT -s 192.168.1.0/255.255.255.0 -j ACCEPT

# Faz a mesma coisa, só que especificando a interface. Pode ser

# usada em substituição à regra anterior:

# iptables -A INPUT -i eth0 -j ACCEPT

# Abre uma porta (inclusive para a Internet):

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

# Ignora pings:

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

# Protege contra IP spoofing:

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

# Descarta pacotes malformados, protegendo contra ataques diversos:

iptables -A INPUT -m state –state INVALID -j DROP

# Abre para a interface de loopback. Esta regra é essencial para que

# o KDE e outros programas gráficos funcionem adequadamente:

iptables -A INPUT -i lo -j ACCEPT

# Impede a abertura de novas conexões, efetivamente bloqueando o acesso

# externo ao seu servidor, com exceção das portas e faixas de endereços

# especificadas anteriormente:

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

echo “Regras de firewall ativadas”

}

parar(){

iptables -F

iptables -P INPUT ACCEPT

iptables -P OUTPUT ACCEPT

echo “Regras de firewall desativadas”

}

case “$1” in

“start”) iniciar ;;

“stop”) parar ;;

“restart”) parar; iniciar ;;

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

esac

A receber qualquer conexão, vinda de qualquer endereço, o firewall primeiro verifica todas estas regras, seqüencialmente, para decidir se o pacote passa ou não. Usando este script de exemplo, teríamos o seguinte:

– Se o pacote vier da rede local, ele é aceito.

– Se o pacote for para porta 22 (do SSH), ele é aceito.

– Se for um ping, ele é ignorado (de forma a dificultar um pouco para outros descobrirem que você está online).

– Pacotes inválidos, que não façam parte de uma conexão aberta anteriormente e que não correspondam à abertura de uma nova conexão são ignorados, protegendo contra pacotes forjados e tentativas de ataque diversas.

– Se o pacote vier da sua própria máquina (um programa tentando mostrar alguma coisa na tela, por exemplo), ele é aceito.

– Se o pacote for uma resposta a uma conexão que você iniciou, como, por exemplo, o servidor do guiadohardware.net enviando a página do site que você está acessando, ele é aceito.

– Tentativas de conexão (toda conexão TCP é iniciada por um pacote syn) fora das condições especificadas acima são descartadas pelo firewall. A conexão nem sequer chega a ser estabelecida e o emissor não recebe qualquer resposta (DROP).

Para usar o script, transforme-o em um arquivo executável, usando o comando:

# chmod +x /etc/init.d/compartilhar

A partir daí, você pode iniciar e parar o compartilhamento usando os comandos:

# /etc/init.d/compartilhar start

# /etc/init.d/compartilhar stop

Para que o script seja executado durante o boot, você pode adicionar o comando “/etc/init.d/compartilhar start” no arquivo “/etc/rc.local”, em vez de colocar os comandos diretamente.

Outra opção (mais elegante) é criar um link para ele dentro da pasta “/etc/rc5.d”, o que também faz com que ele seja executado durante o boot:

# cd /etc/rc5.d

# ln -s ../init.d/compartilhar S21compartilhar

O “S” indica que o script deve ser executado com o parâmetro “start” e o “21” indica a ordem em que deve ser executado durante o boot, em relação aos outros serviços. A ordem de execução dos serviços muda de distribuição para distribuição, mas nessa posição garantimos que ele será executado depois da ativação das interfaces de rede e de outros serviços essenciais.

Se você é um administrador paranóico, verifique a configuração dos serviços na distribuição em uso e altere a ordem de inicialização do script de firewall, de forma que ele seja carregado antes de qualquer outro serviço que aceite conexões, como o Samba ou o Apache. Com isso você garante que o firewall será carregado primeiro e não fica com seu servidor vulnerável nem mesmo por um curto espaço de tempo durante o boot.

Isso nos leva a uma terceira opção para salvar a configuração, utilizando os próprios arquivos de inicialização do sistema. Ela é a mais “correta” do ponto de vista técnico, embora menos flexível. Nesse caso, a configuração seria feita em três passos:

A primeira parada seria o arquivo “/etc/modules”, onde são listados os módulos carregados pelo sistema durante o boot. Nele seria adicionada a linha “iptables_nat”, ativando o carregamento do módulo. Em seguida, você adicionaria (ou descomentaria) a linha “net.ipv4.conf.default.forwarding=1” no arquivo “/etc/sysctl.conf”, para ativar o roteamento de pacotes, tornando permanente o comando “echo 1 > /proc/sys/net/ipv4/ip_forward”.

Finalmente, depois de executar o comando “iptables -t nat -A POSTROUTING -o $placa -j MASQUERADE”, que efetivamente ativa o compartilhamento, você usaria o comando “/etc/init.d/iptables save active” (nas derivadas do Debian) ou “service iptables save” (nas derivadas do Red Hat) para salvar regra e fazer com que ela passe a ser reativada durante o boot.

Continuando, outra dica importante são os comandos usados para limpar as regras do Iptables. É necessário executá-los sempre que você fizer alterações no seu script de firewall e quiser executá-lo novamente para que as novas regras entrem em vigor. Você pode notar que incluí o comando “iptables -F” como parte da função “stop” do script, de forma que ao desativar e reativar o script, as regras antigas seriam eliminadas e as novas regras carregadas. Além dele temos também o “iptables -t nat -F” e o “iptables -L”:

iptables -F: Limpa a tabela principal do iptables, onde vão os comandos para abrir e fechar portas, que vimos até aqui.

iptables -t nat -F: Limpa a tabela nat, que é usada por regras que compartilham a conexão e fazem forwarding de portas. Todas as regras do Iptables que levam “-t nat” são armazenadas nesta segunda tabela, que precisa ser zerada separadamente. A idéia é que você pode limpar as regras principais do firewall sem desabilitar o compartilhamento da conexão e vice-versa.

iptables -L: Este comando lista a configuração atual, sem alterar nada. É interessante executá-lo depois de fazer alterações na configuração do firewall, para ter certeza que as regras surtiram o efeito esperado. Para ver as regras de forwarding e compartilhamento, use também o “iptables -t nat -L“.

Este é um tutorial de três partes. Na segunda parte, iremos aprofundar esta configuração, gerando um script de firewall mais elaborado, voltado para um servidor de rede local, configurado como gateway da rede e, na terceira parte, estudaremos sobre a criação de scripts destinados a serem usados em um servidor de Internet.

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

Sobre o Autor

Redes Sociais:

Deixe seu comentário

X