Criando Firewalls Dinâmicos com Iptables Recent

Não seria interessante montar um firewall com regras dinâmicas que permitisse por exemplo, bloquear algum IP por ter tentado acessar o serviço de SSH mais de 3 vezes em menos de 1 minuto? Ou ainda criar alguma logística de batidas de porta (é!!! igual ao famoso tãn tãn rãn rãn tãn… tãntãn) e só assim permitir a entrada da conexão ao IP que acertou a combinação de portas! Tudo isso e muito mais é possível com o módulo recent do Netfilter.
Para começarmos, criei uma máquina virtual chamada guardiao.home onde iremos utilizar como cenário para demonstrar a utilização do módulo recent.


[root@guardiao ~]# hostname
guardiao.home
[root@guardiao ~]# ifconfig   eth0 | grep "inet addr" 

inet addr:192.168.122.97  Bcast:192.168.122.255  Mask:255.255.255.0
[root@guardiao ~]#

  • Objetivo: Permitir apenas 1 conexão via SSH no período de 2 minutos por origem
Para iniciarmos, criaremos uma regra que permite tudo que for de entrada para a interface de loopback, uma segunda regra para permitir todas conexões estabelecidas ou originadas pela própria máquina guardiao.home, uma terceira que cria a cadeia chamada SSH para direcionarmos todas as conexões SSH e por fim a própria regra que irá direcionar as conexões SSH para a cadeia recém criada. 
[root@guardiao ~]# iptables -A INPUT -i lo -j ACCEPT
[root@guardiao ~]# iptables -A INPUT -m state –state ESTABLISHED,RELATED -j ACCEPT
[root@guardiao ~]# iptables -N SSH
[root@guardiao ~]# iptables -A INPUT -p tcp –dport 22 -j SSH
[root@guardiao ~]# iptables -L -n
Chain INPUT (policy ACCEPT)
target     prot opt source               destination        
ACCEPT     all  –  0.0.0.0/0            0.0.0.0/0          
ACCEPT     all  –  0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED
SSH         tcp  –  0.0.0.0/0            0.0.0.0/0           tcp dpt:22
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination        

Chain SSH (1 references)
target     prot opt source               destination        
[root@guardiao ~]#
 

Com o setup inicial do firewall pronto, podemos agora chamar o módulo recent para tornar nossas regras dinâmicas. Para facilitar o entendimento das próximas regras que iremos trabalhar, vejamos algumas opcões do recent:
–name: seta o nome da lista que iremos trabalhar. Obs.: quando não informado, é utilizado DEFAULT;
–set: irá adicionar o endereço de origem do pacote na lista. Se o endereço já estiver na lista, então irá atualizar o registro;
–rcheck: verifica se o endereço de origem do pacote está atualmente na lista;
–update: verifica e atualiza o timestamp do campo “last seen” se o endereço de origem do pacote já estiver na lista;
–seconds: especifica se a regra terá validade somente se o endereço de origem do pacote estiver dentro do valor especificado;  
–hitcount: verifica se o endereço de origem do pacote esta na lista e se o número de pacotes recebido desse endereço é igual ou maior do valor estipulado.  
Com essas opções explicadas (para maiores informações man 8 iptables), podemos criar nossa primeira regra para cadastrar todas as tentativas de conexões via SSH para uma tabela que será criada pelo recent chamada de conexoes_ssh
[root@guardiao ~]# iptables -A SSH -m recent –set –name conexoes_ssh
[root@guardiao ~]# iptables -L SSH -n
Chain SSH (1 references)
target     prot opt source               destination        
           all  –  0.0.0.0/0            0.0.0.0/0           recent: SET name: conexoes_ssh side: source 
Com a regra iptables -A SSH -m recent –set –name conexoes_ssh  criamos a tabela conexoes_ssh que será alimentada com todas as conexões direcionadas para a cadeia SSH. O que precisamos fazer agora é permitir apenas 1 conexão no intervalo de 2 minutos por vez em nosso servidor.  Para isso, iremos usar os parâmetros –seconds 120 e –hitcount 2 pois queremos determinar  120 segundos e quando o contador for maior ou igual a 2. 

[root@guardiao ~]# iptables -A SSH -m recent –name conexoes_ssh –update –seconds 120 –hitcount 2 -j REJECT
[root@guardiao ~]# iptables -vnL SSH
Chain SSH (1 references)
 pkts bytes target     prot opt in     out     source               destination        
    0     0            all  –  *   &nbs
p;  *       0.0.0.0/0            0.0.0.0/0           recent: SET name: conexoes_ssh side: source
    0     0 REJECT     all  –  *      *       0.0.0.0/0            0.0.0.0/0           recent: UPDATE seconds: 120 hit_count: 2 name: conexoes_ssh side: source reject-with icmp-port-unreachable
 
Após a execução da regra, nosso firewall já esta monitorando tentativas de conexão via SSH e irá permitir a primeira tentativa de cada IP de origem. Vejamos:

–> Máquina Externa  Primeira Conexão
[marcelo@mmello vms]$ date ; ssh root@guardiao
Wed Dec  9 01:34:11 BRST 2009
root@guardiao’s password:
Last login: Wed Dec  9 01:30:24 2009 from 192.168.122.1
[root@guardiao ~]# 
[root@guardiao ~]# iptables -nvL SSH
Chain SSH (1 references)
 pkts bytes target     prot opt in     out     source               destination        
    1    60            all  –  *      *       0.0.0.0/0            0.0.0.0/0           recent: SET name: conexoes_ssh side: source
    0     0 REJECT     all  –  *      *       0.0.0.0/0            0.0.0.0/0           recent: UPDATE seconds: 120 hit_count: 2 name: conexoes_ssh side: source reject-with icmp-port-unreachable
[root@guardiao ~]# cat /proc/net/xt_recent/conexoes_ssh
src=192.168.122.1 ttl: 64 last_seen: 6084651 oldest_pkt: 1 6084651
[root@guardiao ~]#
 
–> Máquina Externa  Segunda Conexão
 [marcelo@mmello vms]$ date ; ssh root@guardiao
Wed Dec  9 01:34:45 BRST 2009
ssh: connect to host guardiao port 22: Connection refused
[marcelo@mmello vms]$   

[root@guardiao ~]# iptables -nvL SSH
Chain SSH (1 references)
 pkts bytes target     prot opt in     out     source               destination       
    2   120            all  –  *      *       0.0.0.0/0            0.0.0.0/0           recent: SET name: conexoes_ssh side: source
    1    60 REJECT     all  –  *      *       0.0.0.0/0            0.0.0.0/0           recent: UPDATE seconds: 120 hit_count: 2 name: conexoes_ssh side: source reject-with icmp-port-unreachable
[root@guardiao ~]# cat /proc/net/xt_recent/conexoes_ssh
src=192.168.122.1 ttl: 64 last_seen: 6119177 oldest_pkt: 3 6084651, 6119177, 6119177
[root@guardiao ~]

 –> Máquina Externa  Terceira Conexão (Depois de 3 minutos)
 [marcelo@mmello vms]$ date ; ssh root@guardiao
Wed Dec  9 01:37:19 BRST 2009
root@guardiao’s password:
Last login: Wed Dec  9 01:34:11 2009 from 192.168.122.1
[root@guardiao ~]#
Como podem perceber, o recent cria um arquivo dentro do diretório /proc/net/xt_recent chamado conexoes_ssh (que é o nome de nossa tabela) para controle das conexões. Se tivéssemos criado outras tabelas, também seriam criados outros arquivos respectivos.  O interessante é que podemos manipular esses arquivos e consequentemente  manipular as tabelas. Perceba também que a cada conexão que fizemos, o campo last seen foi modificado, informando para a regra que houve uma nova tentativa. Para manipularmos as tabelas podemos: 
  • echo +192.168.122.15  > /proc/net/xt_recent/conexoes_ssh para adicionarmos um endereço na tabela
  • echo -192.168.122.1 >  /proc/net/xt_recent/conexoes_ssh  para removermos um endereço da tabela
  • echo / > /proc/net/xt_recent/conexoes_ssh para limparmos a tabela
 [root@guardiao ~]# cat /proc/net/xt_recent/conexoes_ssh
src=192.168.122.1 ttl: 64 last_seen: 6273353 oldest_pkt: 4 6084651, 6119177, 6119177, 6273353
[root@guardiao ~]# echo +192.168.122.15 > /proc/net/xt_recent/conexoes_ssh
[root@guardiao ~]# cat /proc/net/xt_recent/conexoes_ssh
src=192.168.122.1 ttl: 64 last_seen: 6273353 oldest_pkt: 4 6084651, 6119177, 6119177, 6273353
src=192.168.122.15 ttl: 0 last_seen: 6847221 oldest_pkt: 1 6847221
[root@guardiao ~]# echo -192.168.122.1 > /proc/net/xt_recent/conexoes_ssh
[root@guardiao ~]# cat /proc/net/xt_recent/conexoes_ssh
src=192.168.122.15 ttl: 0 last_seen: 6847221 oldest_pkt: 1 6847221
[root@guardiao ~]# echo /> /proc/net/xt_recent/conexoes_ssh
[root@guardiao ~]# cat /proc/net/xt_recent/conexoes_ssh
[root@guardiao ~]#

E finalizando, um ponto importante que precisa ser colocado é que se você for trabalhar com um firewall com grande número de conexões, por padrão o recent irá armazenar apenas 100 endereços nas tabelas criadas. Para alterar esse valor por exemplo para
1024, precisamos passar para o módulo xt_recent algumas flags:
[root@guardiao ~]# modinfo  xt_recent
filename:       /lib/modules/2.6.29.4-167.fc11.i686.PAE/kernel/net/netfilter/xt_recent.ko
alias:          ip6t_recent
alias:          ipt_recent
license:        GPL
description:    Xtables: “recently-seen” host matching for IPv4
author:         Jan Engelhardt
author:         Patrick McHardy
srcversion:     0CA8710587603DFF5C5923B
depends:       
vermagic:       2.6.29.4-167.fc11.i686.PAE SMP mod_unload 686
parm:           ip_list_tot:number of IPs to remember per list (uint)
parm:           ip_pkt_list_tot:number of packets per IP to remember (max. 255) (uint)
parm:           ip_list_hash_size:size of hash table used to look up IPs (uint)
parm:           ip_list_perms:permissions on /proc/net/xt_recent/* files (uint)
parm:           ip_list_uid:owner of /proc/net/xt_recent/* files (uint)
parm:           ip_list_gid:owning group of /proc/net/xt_recent/* files (uint)
[root@guardiao ~]# echo “options xt_recent ip_list_tot=1024″ ; /etc/modprobe.d/xt_recent.conf 
Obs.: Não esqueça de descarregar o módulo e recarregá-lo para honrar as configurações 

Ah!! Para finalizar não esqueça de salvar suas regras e ativar o serviço de firewall persistente ao reboot 
[root@guardiao ~]# service iptables save
iptables: Saving firewall rules to /etc/sysconfig/iptables:[  OK  ]
[root@guardiao ~]# chkconfig  iptables on

Pretendo em outro post, demonstrar como podemos criar algumas regras para que o iptables trabalhe com port knocking, assim permitindo que o  serviço somente seja liberado se a sequência de portas estiver correta.
E agora vivente, prepara o teu chimarrão e a tua mateira e se arranca indío véio configurar o teu firewall tchê!! :)