A Aldeia Numaboa ancestral ainda está disponível para visitação. É a versão mais antiga da Aldeia que eu não quis simplesmente descartar depois de mais de 10 milhões de pageviews. Como diz a Sirley, nossa cozinheira e filósofa de plantão: "Misericórdia, ai que dó!"

Se você tiver curiosidade, o endereço é numaboa.net.br.

Leia mais...

Informática Numaboa - Linux

Filtragem de pacotes no Linux 2.4

Qui

26

Abr

2007


17:47

(3 votos, média 3.67 de 5) 


Nível intermediário Um filtro de pacotes é uma peça de software que examina os cabeçalhos dos pacotes para decidir o que fazer com eles.

O filtro pode decidir sumir com o pacote ('DROP' é descartar o pacote como se nunca o tivesse recebido), aceitar o pacote ('ACCEPT' é deixar o pacote passar) ou fazer alguma coisa um pouco mais complicada.

No Linux, a filtragem de pacotes fica embutida no kernel (como um módulo ou fazendo parte do próprio kernel). Existem coisas um pouco mais elaboradas que podem ser feitas com os pacotes, mas a função básica de verificar os cabeçalhos para decidir o que deve acontecer com os pacotes nunca muda.

Para que servem os filtros de pacotes?

A filtragem de pacotes possui três funções principais: controle, segurança e supervisão de tráfego.

Controle

Se o Linux estiver sendo usado para conectar uma rede interna com outra externa (a Internet, por exemplo), então existe a possibilidade de liberar ou bloquear determinados tipos de tráfego. Por exemplo: como o cabeçalho dos pacotes contém o endereço de destino, é possível impedir que um pacote vá para certos lugares na rede externa. Outro exemplo seria bloquear sistematicamente a propaganda vinda de determinado domínio (do doubleclick.net, por exemplo) para não perder tempo com este tipo de bobagem (nem você, nem o seu navegador). Se o filtro de pacotes for informado de que deve bloquear qualquer pacote recebido ou enviado para o endereço, o problema estará resolvido (existe uma forma mais eficiente de atingir o mesmo objetivo... dê uma olhada no Junkbuster).

Segurança

Se o Linux for a única barreira entre o caos da Internet e sua rede configurada no capricho, é muito bom saber que se pode restringir o que costuma atropelar a porta. É possível, por exemplo, liberar tudo o que sai da sua rede e, ao mesmo tempo, dar um tombo no conhecido 'Ping da Morte' que costuma ser enviado por 'estrangeiros' mal intencionados. Outro exemplo é impedir que seu Linux seja 'telnetado'. Talvez você também queira (como a maioria das pessoas) ser apenas um observador da Internet e não um servidor da mesma, sem seu conhecimento ou autorização. Neste caso, basta usar um filtro de pacotes para rejeitar pacotes que chegam para iniciar e ativar conexões - com isto você impede que qualquer mané se conecte.

Supervisão

Máquinas da rede local mal configuradas podem começar a esparramar pacotes por todos os lados. Neste caso, é uma mão na roda se o filtro de pacotes avisar quando alguma coisa anormal estiver ocorrendo para que possamos dar um jeito na máquina mal comportada. Além do mais, nos dá a chance de matar a curiosidade smile

Filtros de pacotes no Linux

Os kernels do Linux possuem filtros de pacotes desde a série 1.1. A primeira geração, baseada no ipfw do BSD, foi adapatada por Alan Cox no final de 1994. Esta foi melhorada por Jos Vos e colaboradores para o Linux 2.0, onde a ferramenta do espaço do usuário 'ipfwadm' controlava as regras de filtragem do kernel. Em meados de 1998, para o Linux 2.2, Rusty Russel, com a ajuda de Michael Neuling, remodelou o kernel e disponibilizou a ferramenta 'ipchains'. Finalmente, em meados de 1999, o kernel foi aperfeiçoado e apareceu uma ferramenta de quarta geração para o Linux 2.4: a 'iptables'. Este tutorial foca especificamente esta ferramenta.

Você vai precisar de um kernel que tenha a infraestrutura do netfilter. O netfilter (filtro de rede) é um framework genérico embutido no kernel que abre as portas para outras coisas - o módulo iptables, por exemplo. Isto significa que você precisa do kernel 2.3.15 (ou posterior) e que responda 'Y' (sim) para o CONFIG_NETFILTER quando configurar o kernel.

A ferramenta iptables fala com o kernel informando quais pacotes devem ser filtrados. A não ser que você seja um programador ou absurdamente curioso e fizer as coisas por conta própria, esta será a ferramenta para controlar a filtragem de pacotes.

iptables

A ferramenta iptables insere e elimina regras das tabelas de filtragem de pacotes do kernel. Seja lá o que for que você inventar de colocar nas tabelas precisa ser fixado, senão será perdido no próximo reboot.

iptables é um substituto do ipfwadm e do ipchains.

Tornando as regras permanentes

A configuração do firewall é armazenada no kernel e é perdida em cada reboot. Pode-se usar o script iptables-save para guardar a configuração num arquivo e o iptables-restore para restaurar a configuração a partir de um arquivo.

Uma outra forma é colocar os comandos necessários para montar as regras num script de inicialização. Se algum comando falhar, você vai ter que se virar - geralmente o 'exec /sbin/sulogin' quebra o galho.

Guia realmente rápido para filtragem de pacotes

Esta é a sugestão do Rusty Russel, criador do ipchains:

## Inserir os módulos de rastreamento de conexão (não é necessário se já estiverem no kernel).
# insmod ip_conntrack
# insmod ip_conntrack_ftp

## Criar uma cadeia que bloqueia conexões novas, exceto se vierem de dentro.
# iptables -N block
# iptables -A block -m state --state ESTABLISHED,RELATED -j ACCEPT
# iptables -A block -m state --state NEW -i ! ppp0 -j ACCEPT
# iptables -A block -j DROP

## Saltar para esta cadeia vindo das cadeias INPUT e FORWARD.
# iptables -A INPUT -j block
# iptables -A FORWARD -j block

Como os pacotes atravessam os filtros

O kernel começa com três listas de regras na tabela 'filtro'. Estas listas são chamadas de cadeias do firewall ou simplesmente cadeias. As três cadeias são chamadas de INPUT (entrada ou ingresso), OUTPUT (saída ou egressão) e FORWARD (redespacho).

                            _____
Ingresso                   /     \         Saída
       -->[Roteamento]--->|FORWARD|------->
          [Decisão]        \_____/         ^
               |                           |
               v                         ____
              ___                       /    \
             /   \                     |OUTPUT|
            |INPUT|                     \____/
             \___/                         ^
               |                           |
                ----> Processo Local ------

Os três círculos representam as três cadeias mencionadas acima. Quando um pacote alcança um destes círculos, a cadeia é examinada para decidir o destino do pacote. Se a cadeia disser que o pacote deve ser descartado (DROP), ele é eliminado ali, mas se a cadeia disser que o pacote dever ser aceito (ACCEPT), ele continua seu caminho.

Uma cadeia é uma checklist de regras. Cada regra diz 'se o cabeçalho do pacote for este, então faça o seguinte'. Se a regra não puder ser aplicada ao pacote, então a próxima é consultada. Finalmente, se não houver mais regras para consultar, então o kernel verifica a política da cadeia para decidir o que deve fazer. Num sistema onde há preocupação com a segurança, esta política costuma dizer ao kernel para eliminar (DROP) o pacote.

  1. Quando um pacote entra (digamos, através de uma placa Ethernet), a primeira coisa que o kernel olha é o destino do pacote. Isto é chamado de 'roteamento'.
  2. Se o destino for esta máquina, o pacote continua pelo caminho até a cadeia INPUT mostrada no diagrama. Se ele passar por esta cadeia, qualquer processo que o estiver aguardando irá recebê-lo.
  3. Caso contrário, se o kernel não estiver com o redespacho habilitado ou se não souber como redespachar o pacote, o pacote será eliminado. Se o redespacho estiver habilitado e o pacote é destinado para uma outra interface de rede, então ele vai para a cadeia FORWARD. Se for aceito, o pacote será redespachado.
  4. Finalmente, um programa rodando nesta máquina pode enviar pacotes de rede. Estes pacotes passam através da cadeia OUTPUT imediatamente: se ele disser ACCEPT, então o pacote continua na direção da interface para a qual ele foi destinado.

Usando iptables

O manual do iptables (man iptables) é bem completo e deve ser consultado caso você precise de maiores detalhes.

Existem várias coisas diferentes que podem ser feitas com o iptables. Sempre começamos com as três cadeias embutidas que não podem ser eliminadas: INPUT, OUTPUT e FORWARD. Vamos dar uma olhada nas operações que gerenciam cadeias inteiras:

  1. Criar uma nova cadeia (-N de Nova)
  2. Eliminar uma cadeia (-X de eXcluir)
  3. Alterar a política de uma cadeia embutida (-P de Política)
  4. Listar as regras de uma cadeia (-L de Listar)
  5. Depurar ou limpar as regras de uma cadeia (-F de Faxina)
  6. Zerar os contadores de pacotes e de bytes em todas as regras de uma cadeia (-Z de Zerar)

Existem várias maneiras de manipular regras pertencentes a uma cadeia:

  1. Anexar uma nova regra a uma cadeia (-A de Anexar)
  2. Inserir uma nova regra numa determinada posição dentro de uma cadeia (-I de Inserir)
  3. Substituir uma regra numa determinada posição (-R de tRocar)
  4. Eliminar uma regra numa determinada posição ou a primeira que coincidir (-D de Deletar)

O que se vê quando o computador inicia

O iptables pode ser um módulo, chamado 'iptable_filter.o', que deve ser carregado automaticamente quando o iptables for rodado a primeira vez. Também pode ser embutido no kernel permanentemente.

Se nenhum comando iptables tiver sido rodado, as cadeias embutidas INPUT, FORWARD e OUTPUT não possuem nenhuma regra e todas têm a política ACCEPT. Você pode alterar a política default da cadeia FORWARD fornecendo a opção 'forward=0' ao módulo iptable_filter.

atencao Cuidado: algumas distribuições rodam o iptables nos seus scripts de inicialização!

Operações numa só regra

O feijão com arroz da filtragem de pacotes é a manipulação de regras. Os comandos que serão mais usados são, provavelmente, 'append' (-A de Anexar) e 'delete' (-D de Deletar). Os outros, -I para Inserir e -R para tRocar, são simples extensões destes conceitos.

Cada regra especifica um conjunto de condições que o pacote precisa preencher e o que fazer quanto isto acontece (um 'alvo'). Por exemplo, você pode querer eliminar todos os pacotes ICMP que venham do endereço IP 127.0.0.1. Neste caso, as condições são que o protocolo precisa ser ICMP e que o endereço de origem seja 127.0.0.1. O alvo é 'DROP' (eliminar).

127.0.0.1 é a interface 'loopback', a qual existe mesmo quando nenhuma conexão de rede estiver ativa. Podemos usar o programa 'ping' para gerar pacotes deste tipo: o ping envia um ICMP tipo 8 (requisição de eco) ao qual todos os hosts cooperativos precisam obrigatoriamente responder com um pacote ICMP tipo 0 (resposta de eco). Isto torna o programa muito útil para fazer testes.

    # ping -c 1 127.0.0.1
    PING 127.0.0.1 (127.0.0.1): 56 data bytes
    64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.2 ms

    --- 127.0.0.1 ping statistics ---
    1 packets transmitted, 1 packets received, 0% packet loss
    round-trip min/avg/max = 0.2/0.2/0.2 ms
    # iptables -A INPUT -s 127.0.0.1 -p icmp -j DROP
    # ping -c 1 127.0.0.1
    PING 127.0.0.1 (127.0.0.1): 56 data bytes

    --- 127.0.0.1 ping statistics ---
    1 packets transmitted, 0 packets received, 100% packet loss
    #

Aqui é possível ver que o primeiro ping funcionou - o '-c 1' diz para o ping enviar apenas um pacote. Depois foi anexada uma regra (-A) à cadeia INPUT especificando que, se o pacote vier de 127.0.0.1 (fonte = source, '-s 127.0.0.1') com o protocolo ICMP ('-p icmp'), deve ocorrer um salto (jump) para DROP ('-j DROP').

A seguir a regra foi testada usando um segundo ping. O programa demora um pouco até desistir de esperar por uma resposta (que, na verdade não vai vir) e indica que seu pacote foi perdido porque não recebeu o pacote com a resposta.

Esta regra pode ser eliminada de duas maneiras. Primeiro, como sabemos que é a única regra existente na cadeia INPUT, podemos usar uma eliminação numerada para deletar a regra número 1:

            # iptables -D INPUT 1
            #

A segunda maneira é espelhar o comando -A, substituindo -A por -D. Este modo é útil quando a cadeia possui muitas regras e você não tem paciência para ficar contando todas elas para finalmente descobrir que se trata da regra número 45 ou coisa que o valha. Neste caso você pode usar:

            # iptables -D INPUT -s 127.0.0.1 -p icmp -j DROP
            #
A sintaxe de -D precisa ter exatamente as mesmas opções que tinha o comando -A (ou -I, ou -R). Se houver várias regras idênticas na mesma cadeia, apenas a primeira será deletada.


Especificações de filtragem

Vimos o uso de '-p' para especificar o protocolo e de '-s' para especificar o endereço de origem (source). Existem outras opções que podem ser utilizadas para especificar características dos pacotes. Güenta que vem aí um caminhão delas:

Endereços de Origem e de Destino

Endereços IP de origem ('-s', '--source' ou '--src') e destino ('-d', '--destination' ou '--dst') podem ser especificados de quatro modos diferentes. O mais comum é usar o nome completo, como 'localhost' ou 'www.numaboa.com'. O segundo modo é especificar o endereço IP pelo seu quarteto de números, como por exemplo '127.0.0.1'.

O terceiro e quarto modos permitem a especificação de um grupo de endereços IP, do tipo '199.95.207.0/24' ou '199.95.207.0/255.255.255.0'. Ambos especificam endereços IP que vão de 199.95.207.0 a 199.95.207.255 inclusive; os dígitos após a barra '/' indicam as partes significantes do endereço IP. '/32' ou '/255.255.255.255' é o default (não há faixa de endereços, apenas um IP específico). Se nenhum endereço IP deve ser considerado, pode-se usar '/0' como em:

            [ NOTA: `-s 0/0' é redundante. ]
            # iptables -A INPUT -s 0/0 -j DROP
            #

Isto raramente é usado porque o efeito mostrado acima é o mesmo que não usar a opção '-s'.

Inversão

Muitas flags, incluindo-se aí as '-s' (ou '--source') e '-d' ('--destination'), podem ter seus argumentos precedidos por '!' (significando NÃO e pronunciado 'NOT') para caçar endereços que NÃO sejam iguais aos indicados. Por exemplo, '-s ! localhost' identificam qualquer pacote que não tenha vindo do localhost.

Protocolo

O protocolo pode ser especificado com a flag '-p' (ou '--protocol'). O protocolo pode ser um número (se você conhecer seu número de identificação) ou um nome nos casos especiais de 'TCP, 'UDP' e 'ICMP'. A caixa não importa, de modo que 'tcp' funciona tão bem quanto 'TCP'.

O nome do protocolo pode receber o prefixo '!' para inverter a especificação. Por exemplo, '-p ! TCP' especifica pacotes que não sejam TCP.

Interfaces

As opções '-i' (ou '--in-interface') e '-o' (ou '--out-interface') especificam o nome da interface. Uma interface é o dispositivo físico pelo qual o pacote entrou ('-i') ou pelo qual deve sair ('-o'). Pode-se usar o comando ifconfig para listar as interfaces que estão 'no ar', isto é, as que estão trabalhando no momento.

Pacotes que atravessam a cadeia INPUT não possuem uma interface de saída (output) de modo que qualquer regra nesta cadeia que use '-o' encontrará alguma coisa. Da mesma forma, pacotes atravessando a cadeia OUTPUT não possuem uma interface de entrada (input) e qualquer regra que use '-i' não terá qualquer efeito.

Apenas os pacotes que atravessam a cadeia FORWARD possuem tanto uma interface de entrada quanto uma de saída.

É absolutamente lícito especificar uma interface que não exista - a regra não vai caçar nada enquanto esta interface não estiver operante. Isto é extremamente útil para links discados PPP (geralmente, interface ppp0) e outros semelhantes.

Como caso especial, um nome de interface que termina com um '+' coincide com todas as interfaces (se estiverem ativas ou não) que comecem com esta string. Por exemplo, para especificar uma regra que identifique todas as interfaces PPP, pode-se usar a opção '-i ppp+'.

O nome da interface precedido por ' ! ' (observe os espaços) seleciona pacotes que não casam com a(s) interface(s) especificada(s). Por exemplo, '-i ! ppp+'.

Fragmentos

Algumas vezes um pacote é muito grande para caber num fio. Quando isto acontece, o pacote é dividido em fragmentos e enviado como múltiplos pacotes. O outro lado dá um jeito de se virar com estes fragmentos e de recompor o pacote.

O problema é que o fragmento inicial possui todos os campos do cabeçalho (IP + TCP, UDP e ICMP), mas os subsequentes só possuem um subconjunto do cabeçalho (IP sem os campos de protocolo adicionais). Por este motivo, examinando os demais fragmentos para procurar os cabeçalhos de protocolo (como fazem as extensões TCP, UDP e ICMP) não é possível.

Se você estiver usando 'connection tracking' ou NAT, então todos os fragmentos serão reagrupados antes de alcançarem o código da filtragem de pacotes e você não precisa se preocupar com eles.

(atencao Saiba também: a cadeia INPUT da tabela de filtros (ou qualquer outra tabela atrelada ao gancho NF_IP_LOCAL_IN) só é atravessada depois do reagrupamento dos fragmentos da pilha IP central (core))

Necas de 'connection tracking' ou NAT? Então é importante entender como os fragmentos são tratados pelas regras de filtragem. Qualquer regra que solicitar informações que não temos, deixará de atuar. Isto significa que o primeiro fragmento será tratado como qualquer outro pacote, mas os fragmentos subsequentes não. Uma regra como '-p TCP --sport www' (especificando a porta de origem como 'www') nunca poderá coincidir com um fragmento que não seja o primeiro. A regra oposta, '-p TCP --spor ! www', terá a mesma limitação.

Entretanto, é possível criar uma regra específica para o segundo e demais fragmentos usando a flag '-f' (ou '--fragment'). Também é permitido especificar que determinada regra não se aplica para o segundo e demais fragmentos com '-f' precedido por ' ! '.

Já que o filtro pode controlar o primeiro fragmento, normalmente é considerado seguro deixar passar o segundo e os demais porque, se o primeiro for 'dropado', os demais não poderão ser reagrupados. Só tem um probleminha: sabe-se que existem alguns bugs nesta área que fazem com que as máquinas congelem só porque recebem fragmentos sem o fragmento líder...

atencao Pacotes malformados (pacotes TCP, UDP e ICMP muito pequenos que não permitem que o código do firewall leia as portas ou o código e tipo ICMP) são eliminados quando este tipo de verificação é tentado. O mesmo acontece com fragmentos que começam na posição 8.

Como exemplo, a regra a seguir eliminará qualquer fragmento que esteja indo na direção de 192.168.1.1:

    # iptables -A OUTPUT -f -d 192.168.1.1 -j DROP
    #

Extensões do iptables

O iptables aceita extensões, o que significa que podemos adicionar novas capacidades para ampliar o poder de fogo tanto do kernel quanto da ferramenta iptables.

Algumas destas extensões são padrão, outras são mais exóticas. As extensões podem ser criadas por qualquer pessoa e distribuídas para usuários interessados.

As extensões do kernel normalmente são colocadas num subdiretório dos módulos do kernel (/lib/modules/2.4.0-test10/kernel/net/ipv4/netfilter, por exemplo). São carregadas por demanda se o kernel tiver sido compilado com CONFIG_KMOD ou então inseridas manualmente.

As extensões do programa iptables são bibliotecas compartilhadas que normalmente estão em /usr/local/lib/iptables/, se bem que algumas distribuições as colocam em /lib/iptables ou /usr/lib/iptables.

As extensões podem ser de dois tipos: novos alvos e novos controles (matches) - falaremos sobre novos alvos logo adiante. Alguns protocolos oferecem automaticamente novos testes. Os disponíveis atualmente são TCP, UDP e ICMP como mostrado abaixo. Para estes, você poderá especificar os novos testes na linha de comando logo após a opção '-p', o que carregará a extensão. Para testes explícitos, use a opção '-m' para carregar a extensão para disponibilizar as opções adicionadas.

Para obter ajuda, use a opção '-p', '-j' ou '-m' seguida de '-h' ou '--help':

    # iptables -p tcp --help
    #
Extensões TCP

As extensões TCP são carregadas automaticamente se '-p tcp' for especificado. Elas fornecem as seguintes opções (nenhuma das quais atua sobre fragmentos).

  • --tcp-flags: Seguida por um '!' opcional, então duas strings de flags, permite filtrar flags TCP específicas. A primeira string de flags é a máscara: uma lista de flags que você quer examinar. A segunda string de flags indica qual ou quais devem ser ativadas. Por exemplo,
            # iptables -A INPUT --protocol tcp --tcp-flags ALL SYN,ACK -j DROP
    
    indica que todas as flags devem ser examinadas ('ALL' é sinônimo de 'SYN,ACK,FIN,RST,URG,PSH'), mas apenas SYN e ACK devem ser ativadas. Também existe um argumento `NONE', que significa nenhuma flag.
  • --syn: opcionalmente precedida por um '!', é a forma resumida de `--tcp-flags SYN,RST,ACK SYN'.
  • --source-port: seguida por um '!' opcional e por uma única porta TCP ou uma sequência de portas. As portas podem ser citadas por nome (como listado em /etc/services) ou por números. A sequência pode ser dois nomes de portas separados por ':', uma porta seguida de ':' (para especificar maior ou igual à porta citada) ou ':' seguido pela porta (para especificar menor ou igual à porta citada).
  • --sport: sinônimo de '--source-port' (porta de origem).
  • --destination-port e --dport: o mesmo que acima, só que especificam a porta de destino ao invés de porta de origem.
  • --tcp-option: seguida por um '!' opcional e um número, identifica um pacote com a opção TCP igual ao número indicado. Pacotes que não têm cabeçalho TCP completo são eliminados automaticamente assim que houver uma tentativa de examinar suas opções TCP.
Explicação das flags TCP

Em algumas ocasiões é interessante permitir conexões TCP apenas num sentido. Por exemplo, você pode querer permitir conexões com um servidor WWW externo, mas não conexões vindas deste servidor.

A abordagem simplista seria bloquear todos os pacotes TCP vindos do servidor. Infelizmente as conexões TCP, para funcionarem, exigem pacotes transitando nos dois sentidos.

A solução está em bloquear apenas os pacotes que solicitam uma conexão. Estes pacotes são chamados pacotes SYN. Este é o modo abreviado de denominá-los porque, tecnicamente falando, são pacotes com a flag SYN ativada e as flags RST e ACK zeradas. Bloqueando apenas este tipo de pacote pode-se impedir tentativas de conexão.

A flag '--syn' é usada para fazer este tipo de bloqueio. Ela só é válida para regras que especificam TCP como protocolo. Por exemplo, para especificar tentativas de conexão feitas por 192.168.1.1:

    -p TCP -s 192.168.1.1 --syn

Esta flag pode ser invertida precedendo-a com '!', o que significa todos os pacotes com exceção dos que iniciam uma conexão.

Extensões UDP

Estas extensões são carregadas automaticamente se '-p udp' for especificado. Elas oferecem as opções '--source-port', '--sport', '--destination-port' e '--dport' da mesma forma que as do TCP.

Extensão ICMP

Esta extensão é carregada automaticamente se '-p icmp' for especificado. Ela oferece apenas uma opção:

  • --icmp-type: seguida por um '!' opcional, um nome do tipo de icmp (por exemplo, 'host-unreachable'), um tipo numérico (por exemplo, '3') ou um tipo numérico e um código separados por '/' (por exemplo, '3/3'). Uma lista dos nomes dos tipos pode ser obtida com '-p icmp --help'.

Outras extensões

As outras extensões do pacote do netfilter são apenas para demonstração. Se estiverem instaladas podem ser chamadas com a opção '-m'.

  • mac: este módulo precisa ser explicitamente especificado com '-m mac' ou '--match mac'. É usado para controlar o endereço (MAC) da Ethernet de origem no ingresso dos pacotes. É útil para analisar pacotes atravessando as cadeias PREROUTING e INPUT. Possui apenas uma opção:
    --mac-source: precedida por '!' opcional, depois um endereço Ethernet na notação hexbytes separados por dois pontos. Por exemplo, '--mac-source 00:60:08:91:CC:B7'.
  • limit: este módulo precisa ser explicitamente especificado com '-m limit' ou '--match limit'. É usado para restringir a taxa de acertos para, por exemplo, diminuir a quantidade de mensagens no log. Ele só vai atuar um determinado número de vezes por segundo (o default é 3 acertos por hora, com rajadas de 5). Aceita dois argumentos opcionais:
    --limit: seguido por um número que especifica a quantidade média máxima de acertos permitidos por segundo. O número pode especificar unidades (ou parte delas) como '/second', '/minute', '/hour' ou '/day' ('5/second é o mesmo que '5/s').
    --limit-burst: seguido por um número, indica a rajada máxima antes que o limite acima entre em ação.

    Este filtro geralmente é usado com o alvo LOG para criar arquivos de log com taxa limitada. Para entender como funciona veja a seguinte regra, a qual loga pacotes com o parâmetro limit default:
            # iptables -A FORWARD -m limit -j LOG
    
    Na primeira vez em que esta regra se aplicar, o pacote entrará no log. Na verdade, como a rajada (burst) default é 5, os primeiros cinco pacotes serão logados. Depois disto, independentemente de quantos pacotes atenderem a regra, decorrerão 20 minutos antes que o próximo pacote seja logado. Se nenhum pacote atender a regra em 20 minutos, um dos pacotes da rajada será recuperado; se nenhum pacote ficar sujeito à regra por 100 minutos, a rajada será repetida (recarga) e o processo volta para o estado inicial.

    atencao Nota: não é possível criar uma regra com um tempo de recarregamento maior do que cerca de 59 horas. Por este motivo, se for definida uma taxa média de um por dia, a taxa da rajada precisa ser menor do que 3.

    Este módulo também pode ser usado para evitar vários ataques de negação de serviço (DoS) com uma taxa maior para aumentar a resposta.
    Proteção para o syn-flood:
            # iptables -A FORWARD -p tcp --syn -m limit --limit 1/s -j ACCEPT
    
    Rastreamento velado de portas:
            # iptables -A FORWARD -p tcp --tcp-flags SYN,ACK,FIN,RST RST -m limit --limit 1/s -j ACCEPT
    
    Ping da morte (ping of death):
            # iptables -A FORWARD -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT
    
    Este módulo trabalha como um "porta de histerese" (histerese significa atraso), como mostrado no gráfico abaixo.
    
                   taxa (pct/s)  
                         ^        .---.
                         |       / DoS \
                         |      /       \
           Borda do DoS -|.....:.........\.......................
             = (limit *  |    /:          \
            limit-burst) |   / :           \         .-.
                         |  /  :            \       /   \
                         | /   :             \     /     \
           Borda do DoS  -|/....:..............:.../.......\..../.
             = limit     |     :              :`-'         `--'
            -------------+-----+--------------+------------------> time (s)
               LOGIC =>  Casou |  Não Casou   |    Casou
    
    Digamos que filtramos um pacote por segundo com uma rajada de 5 pacotes, mas os pacotes começam a chegar num ritmo de quatro por segundo, durante três segundos, e depois a inundação começa novamente por mais três segundos.
                    <--Flood 1-->           <---Flood 2--->
    
            Total  ^                   Line  __--      YNNN
            Pacotes|               Rate  __--      YNNN
                   |            mum  __--      YNNN
                10 |        Maxi __--         Y
                   |         __--            Y
                   |     __--               Y
                   | __--    YNNN           
                   |-    YNNN
                 5 |    Y    
                   |   Y                          Chave:  Y -> Regra aplicada
                   |  Y                                   N -> Regra não aplicada
                   | Y
                   |Y 
                 0 +-------------------------------------------------->  Tempo (segundos)
                    0   1   2   3   4   5   6   7   8   9  10  11  12
    
    Pode-se ver que os primeiros cinco pacotes tiveram permissão para exceder um pacote por segundo, logo depois o limite entrou em ação. Se houver uma pausa, outra rajada é permitida, mas não acima da taxa máxima especificada pela regra (1 pacote por segundo depois que a rajada é usada).
  • owner: este módulo tenta casar várias características do criador do pacote para pacotes gerados localmente. Só atua na cadeia OUTPUT, mas alguns pacotes sem dono (como respostas ping ICMP) nunca serão retidos.
    --uid-owner userid: a regra se aplica se o pacote foi criado por um processo com a userid (numérica) indicada.
    --gid-owner groupid: a regra se aplica se o pacote foi criado por um processo com a groupid (numérica) indicada.
    --pid-owner processid: a regra se aplica se o pacote foi criado por um processo com a pid indicada.
    --sid-owner sessionid: a regra se aplica se o pacote foi criado por um processo no grupo de sessões sessionid indicado.
  • unclean: este módulo experimental precisa ser explicitamente especificado com '-m unclean' ou '--match unclean'. Ele faz várias checagens randômicas de integridade dos pacotes. Este módulo não foi auditado e não deve ser usado como um dispositivo de segurança (provavelmente ele torna as coisa ainda piores porque eventualmente tenha alguns bugs). Não possui opções.
Estado

O critério de seleção mais útil é fornecido pela extensão 'state'. Ele interpreta a análise do rastreamento de conexão (connection-tracking) do módulo 'ip_conntrack'. Esta extensão é altamente recomendada.

O '-m state' permite uma opção adicional '--state', que é uma lista de estados separados por vírgulas. A flag '!' indica quais estados não devem ser levados em consideração. Os estados são:

  • NEW: um pacote que cria uma nova conexão.
  • ESTABLISHED: um pacote que pertence a uma conexão existente (isto é, um pacote de resposta ou um pacote que esteja saindo por uma conexão que já recebeu respostas).
  • RELATED: um pacote relacionado a uma conexão existente, mas que não faça parte dela, como é o caso de um erro ICMP. Também pode ser um pacote estabelecendo uma conexão de dados ftp (se o módulo FTP estiver inserido).
  • INVALID: um pacote que, por alguma razão, não pode ser identificado. Isto inclui falta de memória e erros ICMP sem uma conexão correspondente. Geralmente estes pacotes devem ser eliminados.

Um exemplo do poder desta extensão seria:

    # iptables -A FORWARD -i ppp0 -m state ! --state NEW -j DROP

Especificações do alvo

Agora que sabemos como examinar pacotes, precisamos de um método para dizer o que fazer com os pacotes localizados pelos testes. Isto é chamado de alvo da regra.

Existem dois alvos internos muito simples: DROP e ACCEPT. Já tivemos o prazer de conhecê-los. Se a regra flagrar um pacote e seu alvo é um destes dois, nenhuma outra regra será consultada: o destino do pacote está selado smile

Existem outros dois tipos de alvo diferentes dos internos: extensões e cadeias definidas pelo usuário.

Cadeias definidas pelo usuário

Uma das características mais poderosas que o iptables herdou do ipchains é a possibilidade do usuário criar cadeias para ampliar as cadeias básicas INPUT, FORWARD e OUTPUT. Convencionou-se que as cadeias definidas pelo usuário recebam nomes em letras minúsculas para deferenciá-las (a criação de novas cadeias definidas pelo usuário será descrita em 'Operações em Toda uma Cadeia').

Quando um pacote atende uma regra cujo alvo é uma cadeia definida pelo usuário, este pacote começa a atravessar as regras da cadeia definida pelo usuário. Se esta cadeia não decidir o que deve ser feito com ele, quando a ela tiver sido percorrida, a travessia continua na próxima regra da cadeia que a acionou.

Mais um pouquinho de arte ASCII. Considere duas cadeias (bobinhas): INPUT e teste (uma cadeia definida pelo usuário):

         `INPUT'                         `teste'
        ----------------------------    ----------------------------
        | Regra1: -p ICMP -j DROP   |    | Regra1: -s 192.168.1.1    |
        |---------------------------|    |---------------------------|
        | Regra2: -p TCP -j teste   |    | Regra2: -d 192.168.1.1    |
        |---------------------------|    ----------------------------
        | Regra3: -p UDP -j DROP    |
        ----------------------------

Imagine um pacote TCP chegando de 192.168.1.1, indo para 1.2.3.4. Ele entra na cadeia INPUT e é testado pela Regra1 - passa. A Regra2 se aplica e seu alvo é 'teste', portanto a próxima regra aplicada é o início de 'teste'. A Regra1 em teste se aplica, mas não indica o alvo - como não há um alvo especificado, a próxima regra é aplicada. A Regra2 de teste não se aplica e o pacote chega no fim da cadeia. Retorna então à cadeia INPUT, onde a Regra2 acabou de atuar. Segue então para a Regra3, que também não se aplica.

O caminho do pacote foi:

                                v    __________________________
         `INPUT'                |   /    `teste'               v
        ----------------------|--/    -----------------------|----
        | Regra1                | /|    | Regra1               |   |
        |-----------------------|/-|    |----------------------|---|
        | Regra2                /  |    | Regra2               |   |
        |--------------------------|    -----------------------v----
        | Regra3                /--+___________________________/
        ------------------------|---
                                v

Cadeias definidas pelo usuário podem saltar para outras cadeias definidas pelo usuário (mas não fazem loops: o pacote será descartado se for encontrado num loop).

Extensões do iptables: novos Alvos

Um outro tipo de extensão é um alvo. Uma extensão de alvo é um módulo do kernel e uma extensão opcional do iptables que fornece novas opcções de linha de comando. Existem várias extensões na distribuição default do netfilter:

  • LOG: este módulo possibilita que o kernel faça log de pacotes selecionados. Ele fornece as seguintes opções adicionais:
    --log-level: seguida por um nível numérico ou um nome. Nomes válidos são (em maiúsculo ou minúsculo) 'debug', 'info', 'notice', 'warning', 'err', 'crit', 'alert' e 'emerg', correspondendo aos números de 7 até 0. Veja a página man syslog.conf para explicações sobre estes níveis. O default é 'warning'.
    --log-prefix: seguida por uma string de até 29 caracteres, esta mensagem é enviada no início da mensagem de log para que o log possa ser identificado.
    Este módulo tem maior utilidade depois que um limite é atingido - serve para impedir que os arquivos de log inchem demais.
  • REJECT: este módulo atua da mesma forma que o DROP, só que o remetente recebe uma mensagem de erro de porta não alcançável (port unreachable). Saiba que a mensagem de erro ICMP não é enviada se (veja a RFC 1122):
    • O pacote filtrado era uma mensagem de erro ICMP ou de um tipo ICMP desconhecido.
    • O pacote filtrado era um fragmento sem cabeçalho.
    • Mensagens de erro ICMP foram enviadas em número excessivo para este destino recentemente (veja /proc/sys/net/ipv4/icmp_ratelimit).
    REJECT também pode ter o argumento opcional `--reject-with', o qual altera o pacote de resposta usado (veja a página do manual).
Alvos internos especiais

Existem dois alvos internos especiais: RETURN e QUEUE.

RETURN tem o mesmo efeito que chegar ao fim de uma cadeia: para uma regra de uma cadeia interna a política da cadeia é executada. Para uma regra de uma cadeia definida pelo usuário, a travessia continua pela cadeia anterior logo depois da regra que gerou o salto para esta cadeia.

QUEUE é um alvo especial que coloca o pacote numa fila para processamento no espaço do usuário. Para que seja útil, dois componentes adicionais são requeridos:

  • um manipulador de fila (queue handler) que trata da mecânica da passagem de pacotes entre o kernel e o espaço do usuário e
  • um aplicativo no espaço do usuário para receber, possivelmente manipular e determinar o veredito dos pacotes.

O manipulador de fila padrão para o iptables IPv4 é o módulo ip_queue, distribuído juntamente com o kernel e marcado como experimental.

O seguinte é um exemplo rápido de como usar iptables para colocar pacotes na fila para processamento no espaço do usuário:

    # modprobe iptable_filter
    # modprobe ip_queue
    # iptables -A OUTPUT -p icmp -j QUEUE

Com esta regra, os pacotes ICMP de saída gerados localmente (como os criados, digamos, pelo ping) são passados pelo módulo ip_queue que tenta entregá-los para o espaço do usuário. Se não houver um aplicativo esperando por eles, os pacotes são descartados.

Para criar um aplicativo para o espaço do usuário, use a API libipq. Ela é distribuída com o iptables. Exemplos de código podem ser encontrados nas ferramentas testsuite (por exemplo, redirect.c) no CVS.

O status do ip_queue pode ser checado através de

    /proc/net/ip_queue

O comprimento máximo da fila (isto é, o número de pacotes entregues ao espaço do usuário sem que seja dado um veredito) pode ser controlado através de

    /proc/sys/net/ipv4/ip_queue_maxlen

O valor default do comprimento máximo da fila é 1024. Quando este limite é alcançado, novos pacotes serão descartados até que a fila fique novamente abaixo do limite. Protocolos simpáticos, como é o caso do TCP, interpretam pacotes descartados como congestionamento e certamente farão uma contenção quando a fila estiver entupida. Entretanto, determinar o comprimento máximo de uma fila numa determinada situação quando o valor default for muito pequeno requer algumas tentativas.


Operações em Toda uma Cadeia

Uma característica muito útil do iptables é permitir o agrupamento de regras relacionadas em cadeias. As cadeias podem receber um nome qualquer, mas é aconselhável usar letras minúsculas para evitar confusões com as cadeias internas e alvos. Nomes de cadeias podem ter até 31 caracteres.

Criando uma nova cadeia

Para criar uma nova cadeia existem as opções '-N' ou '--new-chain'. Para criar a cadeia 'teste' basta:

    # iptables -N teste
    #

Isto é tudo. A cadeia teste está pronta para receber regras.

Eliminando uma cadeia

Eliminar uma cadeia também é muito simples. Basta usar as opções '-X' ou '--delete-chain':

    # iptables -X test
    #

Existe algumas restrições para deletar cadeias: elas precisam estar vazias (veja Limpando uma cadeia logo a seguir) e não podem ser o alvo de qualquer regra. Também não é possível eliminar as cadeias internas.

Se nenhuma cadeia for especificada, então todas as cadeias definidas pelo usuário, possíveis de serem eliminadas, serão eliminadas.

Limpando uma cadeia

Os comandos '-F' ou '--flush' esvaziam uma cadeia eliminando todas as suas regras:

    # iptables -F FORWARD
    #

Se nenhuma cadeia for especificada, então todas as cadeias serão esvaziadas.

Listando uma cadeia

Os comandos '-L' ou '--list' servem para listar todas as regras de uma cadeia.

O 'refcnt' mostrado para cada cadeia definida pelo usuário é o número de regras que possuem uma cadeia como alvo. Este número precisa ser zero (e a cadeia precisa estar vazia) antes que esta cadeia possa ser eliminada.

Se o nome da cadeia for omitido, todas as cadeias serão listadas, inclusive as vazias.

Existem três opções que podem acompanhar '-L'. A opção '-n' (numérica) é muito útil para evitar que o iptables tente verificar os endereços IP, o que causa grandes retardos se o seu DNS não estiver adequadamente configurado ou você tenha bloqueado solicitações de DNS. Esta opção também faz com que as portas TCP e UDP sejam mostradas por seus números e não pelos seus nomes.

A opção '-v' mostra todos os detalhes das regras, como os contadores de pacotes e de bytes, comparações TOS e as interfaces. Se não for usada, estes valores são omitidos.

atencao Os contadores de pacotes e de bytes possuem os sufixos 'K', 'M' ou 'G' para indicar 1.000, 1.000.000 e 1.000.000.000. Usando a flag '-x' (eXpandir números) faz com que os valores sejam mostrados completos, não importando o tamanho dos mesmos.

Zerando os contadores

É muito útil poder zerar os contadores. Isto pode ser feito com as opções '-z' ou '--zero'.

    # iptables -L FORWARD
    # iptables -Z FORWARD
    # 

No exemplo acima, alguns pacotes ter passado no intervalo de tempo decorrido entre os comandos '-L' e '-Z'. Por este motivo é permitido usar '-L' e '-Z' junto para zerar os contadores enquanto são lidos.

Estabelecendo a política

Já demos uma olhada no que acontece quando um pacote chega no fim de uma cadeia interna quando foi discutido como os pacotes atravessam as cadeias. Neste caso, a política da cadeia determina a sentença do pacote. Apenas as cadeias internas (INPUT, OUTPUT e FORWARD) possuem políticas porque, se um pacote chegar no fim de uma cadeia definida pelo usuário, a travessia continua na cadeia anterior.

A política pode ser ACCEPT ou DROP. Por exemplo:

    # iptables -P FORWARD DROP
    #

NAT e Filtros de Pacotes

É comum fazer NAT (Network Address Translation - Tradução de Endereços de Rede) junto com filtragem de pacotes. A boa notícia é que eles se dão muito bem! Para maiores detalhes sobre a NAT, leia o tutorial NAT HOWTO.

Prepare seus filtros de pacotes e deixe de lado qualquer NAT que estiver fazendo. As origens e destinos vistos pelo filtro de pacotes serão origens e destinos 'reais'. Por exemplo, se você estiver usando DNAT para enviar qualquer conexão para a porta 80 de 1.2.3.4 e daí para a porta 8080 de 10.1.1.1, o filtro de pacotes veria apenas pacotes indo para a porta 8080 de 10.1.1.1 (o destino real) e não a porta 80 de 1.2.3.4. Da mesma forma pode-se ignorar as máscaras: vai parecer que os pacotes chegam dos seus endereços IP internos reais (digamos 10.1.1.1) e que as respostas voltam para lá.

Pode-se usar a extensão 'state' sem que o filtro de pacotes fique sobrecarregado porque a NAT exige o rastreamento de conexões (connection tracking) de qualquer forma. Para impedir que novas conexões cheguem através da interface ppp0, faça o seguinte:

    # Masquerade out ppp0
    iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE

    # Disallow NEW and INVALID incoming or forwarded packets from ppp0.
    iptables -A INPUT -i ppp0 -m state --state NEW,INVALID -j DROP
    iptables -A FORWARD -i ppp0 -m state --state NEW,INVALID -j DROP

    # Turn on IP forwarding
    echo 1 > /proc/sys/net/ipv4/ip_forward

Sugestões para o planejamento de filtros de pacotes

No campo de batalha da segurança de computadores, o princípio mais sábio é bloquear tudo - depois pode-se abrir as passagens necessárias. Para isto existe uma frase:

atencao Tudo que não for explicitamente permitido está proibido. atencao

Esta abordagem é altamente recomendada se a sua preocupação maior for com a segurança.

atencao Não ponha no ar serviços que não são usados, mesmo quando você achar que o acesso a eles tenha sido bloqueado - serviço morto não dá bandeira, não recebe e não fornece informações smile

Se você está criando um firewall dedicado, comece com tudo fora do ar e com todos os pacotes bloqueados. Depois, adicione os serviços desejados e deixe passar os pacotes necessários.

atencao Recomendo segurança máxima: combine tcp-wrappers (para conexões com o próprio filtro de pacotes), proxies (para conexões que passam pelo filtro de pacotes), verificação de rotas e filtragem de pacotes. É na verificação de rota que um pacote vindo de uma interface inesperada é descartado. Por exemplo, se o endereço da sua rede interna é 10.1.1.0/24 e um pacote com este endereço de origem aparecer na sua interface externa, ele será descartado. Isto pode ser configurado para uma interface (ppp0) da seguinte maneira:

    # echo 1 > /proc/sys/net/ipv4/conf/ppp0/rp_filter
    #

Ou, para todas as interfaces existentes e futuras, deste modo:

    # for f in /proc/sys/net/ipv4/conf/*/rp_filter; do
    #     echo 1 > $f
    # done
    # 

O Debian, onde é possível, faz isto por default. Se você tem roteamento assimétrico (isto é, espera que os pacotes entrem vindos de direções estranhas), pode querer desabilitar esta filtragem nestas interfaces.

atencao Fazer arquivos de log durante a preparação de um firewall é muito útil para esclarecer alguns pontos quando a coisa não está funcionando como se quer. Num firewall em plena atividade (firewall de produção), combine sempre o log com o 'limit' para prevenir que alguém inunde seus logs.

atencao O rastreamento de conexões (connection tracking) é altamente recomendado para sistemas seguros: ele acarreta alguma sobrecarga, porque todas as conexões são rastreadas, mas é muito útil para controlar os acessos feitos nas suas redes. Será preciso carregar o módulo 'ip_conntrack.o' se seu kernel não carrega módulos automaticamente e o módulo não estiver no kernel. Se você quiser rastrear com precisão protocolos complexos, também será preciso carregar o módulo de ajuda apropriado (por exemplo, o 'ip_conntrack_ftp.o').

    # iptables -N no-conns-from-ppp0
    # iptables -A no-conns-from-ppp0 -m state --state ESTABLISHED,RELATED -j ACCEPT
    # iptables -A no-conns-from-ppp0 -m state --state NEW -i ! ppp0 -j ACCEPT
    # iptables -A no-conns-from-ppp0 -i ppp0 -m limit -j LOG --log-prefix "Bad packet from ppp0:"
    # iptables -A no-conns-from-ppp0 -i ! ppp0 -m limit -j LOG --log-prefix "Bad packet not from ppp0:"
    # iptables -A no-conns-from-ppp0 -j DROP

    # iptables -A INPUT -j no-conns-from-ppp0
    # iptables -A FORWARD -j no-conns-from-ppp0

Criar um bom firewall foge do escopo deste tutorial, mas recomendo ser 'sempre minimalista'.

Referências

Texto de Rusty Russel sob licença GNU GPL com tradução livre da vovó vovo

mfx capitalникас кухняотзывы полигон ценакупить быстровозводимыйvsemsmart лобановский александр

Informações adicionais