quinta-feira, 13 de março de 2008

PF: Firewall (OpenBSD) para Casa ou Pequeno Escritório

Neste exemplo, PF está rodando numa máquina OpenBSD agindo como firewall e gateway NAT para uma pequena rede em casa ou escritório. O objetivo geral é prover acesso à Internet para a rede interna e acesso limitado ao firewall pela Internet, e ainda disponibilizar para acesso externo um servidor web localizado na rede Interna. Este documento o guiará na criação de um conjunto de regras para esse fim.

A Rede

A rede está configurada da seguinte forma:


[ COMP1 ] [ COMP3 ]
| |
---+------+-----+------- xl0 [ OpenBSD ] fxp0 --- ( Internet )
|
[ COMP2 ]

Existem vários computadores na rede interna; o diagrama mostra apenas três, mas o número real é irrelevante. Estes computadores são estações de trabalho usadas para navegar na Internet, ler email, chat, etc., exceto COMP3 que também roda um pequeno servidor web. A rede interna usa a faixa de ips 192.168.0.0 / 255.255.255.0.

O firewall OpenBSD é um Celeron 300 com duas placas de rede: uma 3com 3c905B (xl0) e uma Intel EtherExpress Pro/100 (fxp0). Ele possui uma conexão Internet a Cabo e faz NAT para compartilhar o acesso com a rede interna. O endereço IP na interface externa é atribuído dinamicamente pelo Provedor de Acesso Internet.

Objetivo

Os Objetivos são:
  • Prover acesso irrestrito à Internet para a rede interna.
  • Usar "negar por padrão" como política padrão de regras.
  • Permitir o seguinte tráfego vindo da Internet para o firewall:
    • SSH (porta TCP 22): será utilizado para manutenção remota do firewall.
    • Auth/Ident (porta TCP 113): utilizado por alguns serviços como SMTP e IRC.
    • ICMP Echo Requests: o tipo de pacote ICMP usado pelo comando ping.
  • Redirecionar tentativas de conexão na porta 80 TCP (que são tentativas de conexão a um servidor web) para o computador COMP3. Também permitir trafego TCP porta 80 destinado a COMP3 através do firewall.
  • Logar estatísticas de filtragem na interface externa.
  • Por padrão, responder com um TCP RST ou ICMP Unreachable para pacotes bloqueados.
  • Tornar as regras o mais simples possível, para facilitar a manutenção.

Preparação

Este documento assume que o host OpenBSD tenha sido corretamente configurado para funcionar como roteador, incluindo configuração de endereços IP, conectividade com a Internet e definição de variaveis net.inet.ip.forwarding e/ou net.inet6.ip6.forwarding para "1". Também é necessário que você tenha ativado o PF usando pfctl ou definindo a variável apropriada em /etc/rc.conf.local.

As regras

A seguir um passo a passo através das regras que realizarão os objetivos acima descritos.

Macros

As seguintes macros são definidas para facilitar a leitura e manutençao das regras:
ext_if="fxp0"
int_if="xl0"

tcp_services = "{ 22, 113 }"
icmp_types = "echoreq"

comp3="192.168.0.3"

As primeiras duas linhas definem a interface de rede onde a filtragem acontecerá. Definindo elas aqui, se precisarmos mover este sistema para outra máquina com hardware diferente, nós trocamos apenas estas duas linhas e o resto das regras continuará a mesma. A terceira e quarta linhas listam os números de portas TCP dos serviços que serão abertos para a internet (SSH e ident/auth) e os tipos de pacotes ICMP que terão permissão de alcançar a máquina do firewall. Finalmente, a última linha define o endereço IP de COMP3.

Nota: Caso a conexão com a Internet necessite PPPoE, então, filtragem e NAT devem acontecer na interface tun0 não em fxp0.

Opções

As duas opções seguintes definem a resposta padrão block para regras de filtragem, e fazem com que sejam logadas estatísticas de filtragem (statistics logging "on") para a interface externa:
set block-policy return
set loginterface $ext_if

Todo sistema Unix possui uma interface "loopback". É uma interface de rede virtual que é utilizada por aplicações para conversarem entre si dentro do sistema. No OpenBSD, a interface loopback é lo. É recomendado desabilitar qualquer filtragem nas interfaces loopback. Usando set skip para isto.

set skip on lo
Note que nós estamos usando skip no grupo de interface lo inteiro, desta maneira nós podemos, mais tarde, adicionar interfaces loopback adicionais e não precisamos nos preocupar em alterar esta parte do nosso arquivo de regras.

Scrub

Não há razão para não utilizar scrubbing, recomendado para todo tráfego entrante, portanto aplicamos a regra com uma simples linha:
scrub in

Tradução do Endereço de Rede (NAT)

Para fazer NAT para toda a rede interna a seguinte regra nat será usada:
nat on $ext_if from !($ext_if) to any -> ($ext_if)

O "!($ext_if)" pode ser substituído por "$int_if" neste caso, mas caso você tenha várias interfaces precisará adicionar mais regras NAT, ao passo que, com esta estrutura o NAT será feito em todas as interfaces protegidas.

Já que o endereço IP da interface externa é atribuído dinamicamente, parênteses devem ser colocados em volta da interface, assim o PF notará qualquer alteração no endereço.

Como queremos que o proxy FTP funcione, adicionaremos uma âncora NAT também:

nat-anchor "ftp-proxy/*"

Redirecionamento

As primeiras regras de redirecionamento necessárias são para o ftp-proxy assim, clientes FTP na rede interna local, poderão se conectar a servidores FTP na Internet.
rdr-anchor "ftp-proxy/*"
rdr on $int_if proto tcp from any to any port 21 -> 127.0.0.1 port 8021

Note que esta regra funcionará apenas com conexões destinadas à porta 21. Caso usuários se conectem a servidores FTP em outras portas, então uma lista deve ser utilizada para especificar as portas de destino, por exemplo: from any to any port { 21, 2121 }.

As últimas regras de redirecionamento pegam qualquer tentativa de conexão vinda da Internet em direção à porta 80 TCP no firewall. Tentativas de conexões legítimas nesta porta serão de usuários tentando acessar o servidor web da rede. Estas conexões devem ser redirecionadas para COMP3:

rdr on $ext_if proto tcp from any to any port 80 -> $comp3

Regras de Filtragem

Agora as regras de filtragem. Começando com negar por padrão:
block in

Neste ponto todo tráfego tentando entrar em uma interface será bloqueado, mesmo estes da interface de rede interna. As regras seguintes abrem o firewall de acordo com os objetivos acima descritos, bem como quaisquer interfaces virtuais que se façam necessárias.

Tenha em mente que o pf pode bloquear tráfego entrando e saindo de uma interface. Para simplificar sua vida você pode escolher fazer a filtragem de tráfego em apenas uma direção ao invés de bloquear entrada e saída. Em nosso caso optamos por filtrar tráfego entrando na interface, uma vez filtrada a entrada, não tentaremos obstruir sua saída, portanto faremos o seguinte:

pass out keep state

Precisamos ter uma âncora para o ftp-proxy:

anchor "ftp-proxy/*"

É bom usar também a proteção antispoof:

antispoof quick for { lo $int_if }

Agora abrimos as portas usadas pelos serviços que estarão disponíveis para a Internet. Primeiro, o tráfego que é destinado ao firewall em si:

pass in on $ext_if inet proto tcp from any to ($ext_if) \
port $tcp_services flags S/SA keep state

Especificando as portas na macro $tcp_services simplifica a abertura de serviços adicionais para a Internet através da simples edição da macro e recarregamento das regras. Serviços UDP também podem ser abertos com a criação de uma macro $udp_services e adição de uma regra de filtragem similar à anterior, que especifique proto udp.

Além de ter uma regra rdr que redireciona o tráfego do servidor web para COMP3, ainda PRECISAMOS permitir a passagem do tráfego através do firewall:

pass in on $ext_if inet proto tcp from any to $comp3 port 80 \
flags S/SA synproxy state

Para aumentar um pouco a segurança, faremos uso de TCP SYN Proxy com intuito de proteger o servidor web.

Tráfego ICMP precisa ser permitido:

pass in inet proto icmp all icmp-type $icmp_types keep state

Similar à macro $tcp_services, a macro $icmp_types pode ser facilmente editada para alterar os tipos de pacotes ICMP que terão permissão de passar pelo firewall. Note que esta regra se aplica a todas interfaces de rede.

Agora o tráfego deve passar "de" e "para" a interface interna. Assumiremos que os usuários da rede interna sabem o que estão fazendo e não nos causarão problemas. Esta não é necessariamente uma suposição válida; um conjunto de regras muito mais restritivo pode ser apropriado para muitos ambientes.

pass in quick on $int_if

Tráfego TCP, UDP e ICMP pode sair do firewall com destino à Internet de acordo com a regra "pass out keep state" anterior. A informação de estado das conexões é mantida de forma que pacotes que retornam passarão pelo firewall.

O Conjunto Completo de Regras

# macros
ext_if="fxp0"
int_if="xl0"

tcp_services="{ 22, 113 }"
icmp_types="echoreq"

comp3="192.168.0.3"

# opções
set block-policy return
set loginterface $ext_if

set skip on lo

# scrub
scrub in

# nat/rdr
nat on $ext_if from !($ext_if) -> ($ext_if:0)
nat-anchor "ftp-proxy/*"
rdr-anchor "ftp-proxy/*"

rdr pass on $int_if proto tcp to port ftp -> 127.0.0.1 port 8021
rdr on $ext_if proto tcp from any to any port 80 -> $comp3

# regras de filtragem
block in

pass out keep state

anchor "ftp-proxy/*"
antispoof quick for { lo $int_if }

pass in on $ext_if inet proto tcp from any to ($ext_if) \
port $tcp_services flags S/SA keep state

pass in on $ext_if inet proto tcp from any to $comp3 port 80 \
flags S/SA synproxy state

pass in inet proto icmp all icmp-type $icmp_types keep state

pass quick on $int_if

Nenhum comentário: