Social Icons

^^

quarta-feira, 1 de junho de 2011

Firewall e roteamento avançado no Linux

Introdução

Objetivo

Apresentar as diversas ferramentas e funcionalidades existentes no Linux para implementação de Firewall e roteadores. O uso específico de cada ferramenta não será coberto por este artigo e caberá ao leitor se aprofundar nestes assuntos.

Pré Requisitos

É assumido que o leitor tenha conhecimentos básicos sobre sistemas GNU / Linux e redes TCP/IP (incluindo firewalls e roteamento).

É recomendado que o leitor acompanhe o artigo com o manual de cada ferramenta aberto para consulta.

Firewall: IP Tables

A ferramenta atual para firewall no Linux é o iptables. O iptables é constituído de uma série de aplicativos existentes no userland que interagem com o netfilter, que é a implementação de firewall em nível de kernel.

O iptables se baseia em pares de regras e ações. As regras definem em quais pacotes atuar (eg. pacotes originados de uma rede em específico) e a ação define qual atitude deve ser tomada quando um pacote bater com a regra em questão (eg. recusar pacotes de origens duvidosas). O netfilter irá processar todas as regras sequencialmente, e quando encontrar uma que especifique um dado pacote, ele atuará com a ação par daquela regra. As ações podem ser terminativas ou não. Por exemplo, uma ação que diz ao netfilter para ignorar um pacote é executada e nenhuma outra é executada. Esta é uma ação terminativa. Por outro lado, uma ação especificando para apenas notificar a existência de um pacote, faz seu papel e diz ao netfilter para continuar processando as demais regras (eg. fazer log quando certo pacote passa pela máquina).

A principal ferramenta é o comando iptables, que pode ter seu manual acessado com

# man iptables
As próximas sessões explicam como e quais são as partes que compõe este funcionamento.

Tabelas

O nome iptables vem do fato de internamente o iptables funcionar em cima de tabelas, cada uma especializada num tipo de tratamento de pacotes. As tabelas existentes são (kernel 2.6.8, pode variar para outras versões):

raw: onde são feitas algumas alterações em mais baixo nível nos pacotes
filter: nesta tabela cabem as regras responsáveis pela filtragem de pacotes
nat: mudanças nos cabeçalhos dos pacotes (incluindo NAT e IP Masquerade)
mangle: usada para alterações específicas nos pacotes
Portanto, dependendo do que se deseja fazer com um pacote em específico, existe uma tabela adequada para tal.

Cadeias

No iptables, existem diversas cadeias, a cada uma associado um certo tipo de tráfego. São elas:

PREROUTING: tráfego ingressante na máquina (incluindo tráfego gerado localmente com destino local)
INPUT: tráfego que tem como destino a própria máquina
FORWARD: tráfego passante pela máquina
OUTPUT: tráfego gerado localmente (tanto com destino local como remoto)
POSTROUTING: todo tráfego que "sai" da máquina (incluindo tráfego gerado localmente com destino local)
Também é possível a criação de tabelas personalizadas, tópico que não será abordado.

A cadeia FORWARD tem um tratamento especial na kernel do Linux, e vem com uma trava fora do firewall, que por padrão bloqueia tráfego por ela. Para permitir seu funcionamento, é necessário configurar o seguinte parâmetro da kernel do Linux:

net.ipv4.ip_forward=1
Sua ativação é feita pelo comando sysctl, e pode ser automatizada na maioria das distribuições adicionando esta linha ao arquivo /etc/sysctl.conf. Para ativar manualmente:

# sysctl -w net.ipv4.ip_forward=1
Nota: Nas versões do Debian GNU/Linux anteriores (e incluindo) a Sarge, o arquivo /etc/network/options pode ser usado para configurar este e outros parâmetros. É uma configuração tida como legado e errada e não aparece mais em novas versões. Tenha certeza de que nenhum método (sysctl.conf ou options) está sobrepassando a configuração do outro.

Fluxograma

O fluxo de dados que ocorre internamente na kernel do Linux pode ser descrito pelo fluxograma abaixo. Em cada caixa, está evidenciada a cadeia atuante do iptables, e as tabelas válidas na cadeia em questão. O tráfego passa por cada uma das tabelas em sequência em uma dada cadeia. Por exemplo, na cadeia PREROUTING, existem as tabelas raw, mangle e nat. Tráfego que se enquadra nesta cadeia irá passar sequencialmente pelas três tabelas indicadas.

Tráfego
de Entrada
|
|
V
+----------+
|PREROUTING|
+----------+
| raw | <--------------+
| mangle | |
| nat | |
+----------+ |
| |
| |
Decisão |
+- de -+ |
| Roteamento | |
| | |
V V |
Destino Destino |
Local Remoto |
| | |
| | |
V V |
+--------+ +---------+ |
| INPUT | | FORWARD | |
+--------+ +---------+ |
| mangle | | mangle | |
| filter | | filter | |
+--------+ +---------+ |
| | |
| | |
V | |
Máquina | |
Local | |
| | |
| | |
V | |
Decisão | |
de | |
Roteamento | |
| | |
| | |
V | |
+--------+ | |
| OUTPUT | | |
+--------+ | |
| raw | | |
| mangle | | |
| nat | | |
| filter | | |
+--------+ | |
| | |
| +-------------+ |
| | POSTROUTING | Tráfego
+----> +-------------+ --> Local
| mangle |
| nat |
+-------------+
|
|
V
Tráfego
de Saída
Tomemos como exemplo um acesso da máquina para ela mesma. O tráfego irá passar sequencialmente por estas cadeias / tabelas:

Pacote gerado por um processo / kernel local
Decisão de roteamento
OUTPUT: raw
OUTPUT: mangle
OUTPUT: nat
OUTPUT: filter
POSTROUTING: mangle
POSTROUTING: nat
PREROUTING: raw
PREROUTING: mangle
PREROUTING: nat
Decisão de roteamento
INPUT: mangle
INPUT: filter
Pacote entregue a um processo / kernel local
Portanto, se desejamos permitir tal tráfego, devemos garantir que não exista nenhuma ação em nenhum dos casos acima que bloqueie os pacotes deste acesso.

Regras

Dada uma cadeia / tabela em específico, é necessário o uso de regras para selecionar em quais pacotes uma dita ação irá atuar. Nem todas as regras se aplicam a todas as cadeias (por exemplo, uma regra que especifica a interface de saída de pacote não se aplica a cadeia PREROUTING, uma vez que a decisão de roteamento ainda não foi tomada).

Existem regras gerais (ou padrão) e existem extensões (referidas como match extensions no manual). A existência e variedade das regras extras dependem da versão da kernel em uso e da versão do iptables em uso (lembre-se que o iptables é simplesmente uma interface para conversar com a kernel). É possível o caso em que exista determinada regra extra no iptables, mas não a correspondente implementação na kernel. Neste caso, a tentativa de uso desta regra resultará em erro.

As regras gerais são:

-p PROTOCOLO: especifica um protocolo (por exemplo tcp ou udp)
-s ENDEREÇO: especifica um endereço de origem
-d ENDEREÇO: especifica um endereço de destino
-i INTERFACE: especifica a interface de rede na qual o pacote ingressou
-o INTERFACE: especifica a interface de rede na qual o pacote irá sair da máquina
As regras extras serão tratadas mais adiante.

Lembre-se que nem todas as regras se aplicam a todas as cadeias.

Alvo

Especifica a ação a ser tomada quando um pacote casar com uma dada regra de seleção. Esta ação pode ser uma das ações padrão ou uma extenção (similares ao caso das regras acima). As ações padrão são:

ACCEPT: aceita o pacote, e diz ao netfilter para continuar o processamento do pacote na próxima cadeia/tabela
DROP: diz ao netfilter para ignorar completamente o pacote
QUEUE: indica que o pacote deve ser passado ao userspace
RETURN: instrui o netfilter para parar de processar a cadeia em questão e continuar na próxima regra na cadeia anterior
Na prática, a grandíssima maioria das vezes será usado apenas ACCEPT ou DROP.

Escrevendo regras

Segundo o manual do utilitário iptables, existem diversas maneiras de se acrescentar / remover pares de regras / ação. Todas são operações discretas, o que sugere que criemos um Shell Script contendo todas as chamadas discretas das regras que irão compor o firewall completo.

Para adicionar discretamente uma regra (ao final das pré existentes), a forma geral é:

# iptables -t TABLE -A CADEIA REGRAS -j ALVO
Onde:

TABLE identifica a tabela
CADEIA identifica a cadeia
REGRAS compõe as regras de seleção de pacotes no qual o ALVO deve atuar
ALVO ação a ser executada
Por exemplo, para permitirmos que a rede 192.168.0.0/24 ligada a interface de rede eth0 possa enviar tráfego a rede 192.168.1.0/24 conectada a interface de rede eth1, devemos escrever a seguinte regra:

# iptables -t filter -A FORWARD -s 192.168.0.0/24 -d 192.168.1.0/24 -i eth0 -o eth1 -j ACCEPT
Note pelo fluxograma que esta regra não seria suficiente para permitir tal tráfego, uma vez que ele passa por muitas outras cadeias e tabelas.

No caso de não existir alguma regra específica para um determinado tráfego, é possível configurar uma política padrão para cada cadeia / tabela. Esta política aponta um alvo a ser executado, caso nenhuma regra terminativa nesta mesma cadeia / tabela atue no pacote. Para definir uma política padrão:

# iptables -t TABELA -P CADEIA ALVO
Por exemplo:

# iptables -t raw -P OUTPUT ACCEPT
irá permitir tráfego livre por padrão na dada cadeia / tabela.

Até aqui, os recursos são bem limitados. O próximo tópico irá abordar os extras do iptables.

Regras Extras

Como citado anteriormente, existem regras extras que podem ajudar na seleção de pacotes. O uso geral das extenções de regras é:

... -m NOME OPÇÕES
onde

NOME identifica a extenção a ser usada
OPÇÕES opções específicas de cada extenção
No manual do iptables no tópico MATCH EXTENSIONS há uma descrição detalhada das extenções existentes. Adiante, falaremos de algumas de maior relevância.

tcp
Permite a especificação de estado ou portas de uma conexão TCP. Exemplo:

... -m tcp --dport www
especifica a porta de destino como www (80, veja /etc/services) de uma conexão TCP.

udp
Permite a especificação de portas de uma conexão UDP.

Conexões

As regras vistas até agora conseguem identificar apenas casos estáticos. Por exemplo, um servidor web recebe uma conexão em sua porta 80. Porém, a porta de origem do cliente é indeterminada. Existe porém uma funcionalidade de se rastrear conexões. Você identifica somente o destino (eh. servidor web porta 80) e todo o tráfego relacionado, poderá ser identificado facilmente.

Continuando o exemplo anterior, podemos escrever:

# iptables -t filter -A FORWARD -d SERVIDOR_WEB -i INTERFACE_EXTERNA -o INTERFACE_INTERNA -j ACCEPT
# iptables -t filter -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
A primeira regra irá permitir que nosso roteador garanta acesso externo a um servidor web. A segunda regra diz que será permitida a volta deste tráfego (mais especificamente, qualquer tráfego relacionado a uma conexão existente qualquer). A regra extra state permite ter acesso ao estado da conexão. Neste caso, estamos explicitamente dizendo que qualquer tráfego relacionado a uma conexão pré estabelecida é permitido (não necessariamente apenas esta conexão ao servidor web).

O sistema de rastreamento do conexões possui diversos módulos, podendo rastrear até mesmo as conexões de transferência de dados de uma conexão FTP, sem a necessidade de um servidor proxy para tal. Para o caso em que não é definida uma conexão (como no caso de UDP ou ICMP) o sistema de rastreamento abre uma janela para o tráfego de retorno, mediante a existência do tráfego de ida, tratando ambos como parte da mesma conexão.

Um bom negócio é manter uma regra como a segunda regra genérica do exemplo acima apenas uma vez, e ao escrever as regras específicas para rastrear conexões de cada tráfego. A existência de tal regra é também recomendada para garantir o tráfego de mensagens ICMP.

Consulte o manual do iptables para encontrar outras possibilidades de rastreamento de conexões e outras formas de utilização da ferramenta.

Alvos Extras

Os alvos extras permitem recursos mais interessantes, tais como NAT ou balanceamento de carga. Ao selecionar um alvo extra, uma nova gama de opções se abre para cada um deles. Por favor consulte sempre o manual para conhece-las.

LOG
Permite que se notifique os pacotes via mensagens da kernel (accessíveis via o comando dmesg). Uma boa dica, é sempre incluir uma regra com alvo LOG exatamente antes de qualquer regra que vá recusar algum pacote. Desta maneira, é possível identificar facilmente se o seu firewall está bloqueando algum tráfego que não deveria. O uso deste alvo, dá acesso a opção –log-prefix, que permite incluir um prefixo na depuração.

MASQUERADE, SNAT, DNAT
São 3 possibilidades da implementação de NAT (RFC1631). Desta maneira, é possível integrar uma rede privada (RFC1918) a internet sem problemas. Por favor, consulte o manual para maiores detalhes de como utilizar estes alvos.

Roteamento Avançado

O Linux possui uma implementação diferenciada de outros UNIX para o sistema de roteamento. Os utilitários ifconfig e route tradicionais de sistema UNIX continuam válidos, porém existe uma outra camada de implementação mas avançada. O utilitário ip (parte do pacote iproute2) permite o acesso a novos sistemas de roteamento. Ele será a ferramenta que estaremos operando agora.

O sistema de roteamento no Linux é implementado em duas partes:

Regras
Tabelas de roteamento
As regras são responsáveis por indicar em qual tabela de roteamento a kernel deve procurar por uma rota. Portanto, é possível a existência de múltiplas tabelas de roteamento.

Regras

Para listar as regras existentes, fazemos:

# ip rule list
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
As regras são processadas em ordem crescente de prioridade (1a coluna). Neste caso, a prioridade 0 instrui a kernel a, para qualquer tráfego, procurar por uma solução de roteamento na tabela de nome local. O conteúdo desta tabela pode ser acessado com:

# ip route list table local
local 192.168.1.1 dev eth3 proto kernel scope host src 192.168.1.1
local 200.170.111.237 dev eth0 proto kernel scope host src 200.170.111.233
broadcast 192.168.1.0 dev eth3 proto kernel scope link src 192.168.1.1
broadcast 192.168.2.255 dev eth1 proto kernel scope link src 192.168.2.1
local 200.170.111.236 dev eth0 proto kernel scope host src 200.170.111.233
broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1
broadcast 200.170.111.239 dev eth0 proto kernel scope link src 200.170.111.233
local 200.170.111.239 dev eth0 proto kernel scope host src 200.170.111.233
local 200.170.111.238 dev eth0 proto kernel scope host src 200.170.111.233
local 200.170.111.233 dev eth0 proto kernel scope host src 200.170.111.233
broadcast 200.170.111.232 dev eth0 proto kernel scope link src 200.170.111.233
local 200.170.111.235 dev eth0 proto kernel scope host src 200.170.111.233
local 201.20.202.11 dev eth0 proto kernel scope host src 201.20.202.11
broadcast 201.6.149.0 dev eth2 proto kernel scope link src 201.6.149.41
local 200.170.111.234 dev eth0 proto kernel scope host src 200.170.111.233
local 192.168.2.1 dev eth1 proto kernel scope host src 192.168.2.1
broadcast 192.168.1.255 dev eth3 proto kernel scope link src 192.168.1.1
broadcast 192.168.2.0 dev eth1 proto kernel scope link src 192.168.2.1
broadcast 201.20.207.255 dev eth0 proto kernel scope link src 201.20.202.11
broadcast 201.20.200.0 dev eth0 proto kernel scope link src 201.20.202.11
broadcast 127.0.0.0 dev lo proto kernel scope link src 127.0.0.1
broadcast 201.6.149.255 dev eth2 proto kernel scope link src 201.6.149.41
local 201.6.149.41 dev eth2 proto kernel scope host src 201.6.149.41
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
A tabela é extensa e é gerenciada pela própria kernel, portanto não se preocupe com ela. A tabela local contém informações de que o tráfego associado aos endereços das interfaces de rede local, devem ser tratados localmente.

Quando ocorre de a tabela local não satisfazer um determinado destino (eg. tráfego destinado a outra máquina) a kernel procura a regra seguinte de menor prioridade (maior número) para achar uma solução de roteamento. No caso, a regra de prioridade 32766 diz para procurar na tabela de nome main.

# ip route list table main
200.170.111.232/29 dev eth0 proto kernel scope link src 200.170.111.233
192.168.2.0/24 dev eth1 proto kernel scope link src 192.168.2.1
192.168.1.0/24 dev eth3 proto kernel scope link src 192.168.1.1
201.6.149.0/24 dev eth2 proto kernel scope link src 201.6.149.41
201.20.200.0/21 dev eth0 proto kernel scope link src 201.20.202.11
default via 201.6.149.1 dev eth2
Esta tabela contém as regras de roteamento encontradas pelo comando route:

# route -n
Tabela de Roteamento IP do Kernel
Destino Roteador MáscaraGen. Opções Métrica Ref Uso Iface
200.170.111.232 0.0.0.0 255.255.255.248 U 0 0 0 eth0
192.168.2.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1
192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth3
201.6.149.0 0.0.0.0 255.255.255.0 U 0 0 0 eth2
201.20.200.0 0.0.0.0 255.255.248.0 U 0 0 0 eth0
0.0.0.0 201.6.149.1 0.0.0.0 UG 0 0 0 eth2
Esta tabela portanto, contém o sistema de roteamento tradicional.

A última tabela (default) vem vazia.

As tabelas mostradas até aqui compõe o sistema de roteamento tradicional. Porém é possível incluir novas regras de acordo com a necessidade, apontando para tabelas de roteamento diferentes.

Regras personalizadas

Podemos por exemplo, inlcuir uma regra especificando que tráfego de determinada origem deve ser resolvido com uma tabela de roteamento especial:

# ip rule add type unicast from 192.168.4.0/24 priority 55 table 55
Isso dirá a kernel para quando receber tráfego com origem na rede 192.168.4.0/24, procurar roteamento na tabela de número 55. Esta tabela deverá ser criada pelo administrador, e conter as regras de roteamento aplicáveis somente a rede em questão.

Neste caso:

# ip rule list
0: from all lookup local
55: from 192.168.4.0/24 lookup 55
32766: from all lookup main
32767: from all lookup default
Note que tanto a prioridade 55 e o número 55 para a tabela, foram absolutamente arbitrários, e não há necessidade alguma de ambos serem iguais.

Por favor, consulte o manual do utilitário 'ip' para obter maiores informações de como adicionar e remover regras.

Tabelas

As tabelas de roteamento são identificadas por números de 0 a 255. As tabelas local e main tem os números 255 e 254 associados a elas respectivamente. Existe um arquivo de configuração em que se pode associar nome a tabelas (como no caso da loca e main, que já vem de fábrica assim) que não será coberto neste artigo.

A existência de uma tabela, contendo suas próprias regras não implica em que ela esteja em uso. Uma tabela apenas estará em uso se existir uma regra apontando para ela.

Consulte o manual para aprender como adicionar e remover regras de roteamento nas tabelas. Não deve haver dificuldade quanto a isso, uma vez que a operação é similar ao sistema de roteamento clássico (comando route).

Nota

A kernel do Linux possui uma proteção "anti-spoff" separadamente do firewall. Esta proteção serve para proteger uma rede interna contra ataques externos. Dependendo da situação de roteamento desejada (especialmente com diversos links de internet) esta opção tem que ser desabilitada.

Esta opção é accessível pelo sysctl

net.ipv4.conf.all.rp_filter
que muda a configuração para todas as interfaces de rede. Também existe a possibilidade de habilitar ou desabilitar por itnerface (basta substituir .all. por .eth0. para mudar apenas a eth0).

Colocando a linha

net.ipv4.conf.all.rp_filter=0
no arquivo /etc/sysctl.conf, deverá desligar esta proteção automaticamente durante a inicialização.

Integrando Firewall e Roteamento

Uma das possibilidades de escrita de regra de roteamento é a opçãp fwmark. Esta opção faz com que pacotes que tenham uma marca feita pelo firewall caiam na tabela de roteamento desejada.

# ip rule list
0: from all lookup local
33: from all fwmark 0x1 lookup 33
32766: from all lookup main
32767: from all lookup default
Neste caso, pacotes que contenham uma marca de firewall 1 (0x1 é o mesmo número, porém em hexa-decimal) devem cair na tabela de roteamento 33. A escolha deste número é absolutamente arbitrária.

É importante saber que esta marca é local, e não altera de forma alguma qualquer conteúdo do pacote.

Marcando pacotes

Para marcar pacotes com o iptables usa-se o alvo MARK:

# iptables -t mangle -A PREROUTING -i eth0 -j MARK --set-mark 1
Nesta regra de exemplo, todo tráfego entrante pela interface eth0 será marcado como 1 (a escolha do número 1 é absolutamente arbitrária). Feito isso, é possível incluir uma regra de roteamento como no tópico anterior que irá se aplicar somente aos pacotes que entrarem pela interface eth0.

Note pelo fluxograma do iptables exposto anteriormente que a marca está sendo feita antes da decisão de roteamento. Neste caso, como o tráfego é passante pela máquina, ele deve acontecer no PREROUTING. Caso se desejasse marcar tráfego gerado localmente, deve-se utilizar a cadeia OUTPUT.

Conexões

É possível também marcar todos os pacotes relativos a uma conexão. O alvo CONNMARK permite a marcação de uma conexão (referida como connection mark, interna ao firewall), que não é a mesma marcação utilizada pelo roteamento (referida como netfilter mark, acessível pelo sistema de roteamento), não confunda as duas. Portanto, pode-se fazer o seguinte para marcar todos os pacotes de uma conexão:

# iptables -t mangle -A OUTPUT -d SERVIDOR -j CONNMARK --set-mark 1
# iptables -t mangle -A OUTPUT -m connmark --mark 1 -j MARK --set-mark 1
A primeira regra marca todas as conexões desejadas. A segunda regra usufrui da extensão -m connmark para identificar tais conexões e marcar seus pacotes com o alvo MARK (marca que pode ser utilizada pelo sistema de roteamento).

Nota: A kernel 2.6.8 distribuida com o Debian GNU/Linux Sarge não possui tal funcionalidade (CONNMARK). Esta funcionalidade apareceu apenas em kernels posteriores e é necessária uma atualização de kernel para usar este recurso.2

Nenhum comentário:

Postar um comentário

Popular Posts

- Arquivo -

 

Seguidores

Hora exata:

Total de visualizações de página