O meu filtro de pacotes FreeBSD definitivo (pf.conf)

13 min

language: ja bn en es hi pt ru zh-cn zh-tw

Olá, eu sou o Munou.

Desta vez, gostaria de apresentar as configurações de filtro de pacotes com as quais andei mexendo recentemente.

Configuração Atual

Está assim.
No caso do Packet Filter (PF):
Regras de conversão como NAT, rdr, etc., são aplicadas na primeira correspondência (first-match).
Regras de filtragem como pass, block, etc., são aplicadas na última correspondência (last-match).
Essa é a especificação.

Neste caso, por uma questão de legibilidade, configurei todas as regras de filtragem como quick para que a regra correspondente naquele momento seja aplicada imediatamente.
No entanto, quanto às regras de block do fail2ban que desejo registrar em log de forma abrangente, decidi colocá-las no final para garantir o registro sem omissões.
Vou deixar comentários onde for possível.

set skip on lo
set block-policy drop
set optimization conservative
set state-policy if-bound
set ruleset-optimization basic
wanint="vtnet0"
# Configuração do WireGuard
wg_ifs  = "{ wg0, wg1 }"
wg_net = "10.1.0.0/24"
wg_ports="{51820}"
table <wg_clients> const { 10.1.0.2, 10.1.0.4, 10.1.0.22 }
#### -- Regras de Scrub -- ####
scrub in on $wanint all random-id max-mss 1360
scrub out on $wanint all random-id max-mss 1360
scrub in all
scrub out all
#### -- Regras de NAT -- ####
# NAT para Clientes WireGuard
nat on $wanint inet from <wg_clients> to any -> ($wanint)
block all
#### -- Regras do Fail2Ban -- ####
anchor "f2b/*"
pass out quick keep state
#### -- Regras UDP -- ####
pass in quick on $wg_ifs from $wg_net to any keep state
pass in quick on $wanint proto udp from any to ($wanint) port $wg_ports keep state
# Protocolo HTTP/3
pass in quick on $wanint proto udp to ($wanint) port 443 keep state
#### -- Regras TCP -- ####
# Protocolos de E-mail
pass in quick proto tcp from any to ($wanint) port {25, 465, 993, 995} synproxy state
# HTTP
pass in quick on $wanint proto tcp to ($wanint) port {80, 443} modulate state

set block-policy drop

Todos os pacotes que não corresponderem a nenhuma regra serão descartados (drop).
No caso de return, se for TCP, ele retorna um RST para notificar a outra parte e fechar a conexão.
No caso do UDP, como obviamente não existe uma conexão TCP, ele apenas retorna um ICMP port unreachable.

set optimization conservative

Esta é uma regra de otimização que, na documentação, é considerada uma configuração conservadora.
No caso de aggressive, as conexões expiram prioritariamente, mas em ambientes como o WireGuard, onde se deseja manter um estado de conexão constante, a conexão seria cortada apenas por não haver novos pacotes chegando.

Além disso, existe até algo como satellite, o que é bem interessante.

       high-latency
             A high-latency environment (such  as  a  satellite  connec-
             tion).
       satellite
             Alias for high-latency.

set state-policy if-bound

Mantém o state (estado) e o memoriza para permitir a passagem de pacotes, evitando assim inconsistências.
O resultado de pfctl -ss é o state. Por exemplo, na seguinte configuração:

Cliente ←→ wg ←→ Servidor

Como a informação de "em qual interface foi criado" está vinculada ao state, a inconsistência é evitada ao validar apenas a comunicação que passa pela mesma interface.

No caso do floating normal:
Entrada: wg0
Saída: vtnet0
Retorno: OK tanto por vtnet0 quanto por wg0

No entanto, no caso de state-policy if-bound:
Entrada: wg0
Saída: vtnet0
Retorno: Deve ser obrigatoriamente wg0, caso contrário, falha (NG)

set ruleset-optimization basic

Realiza a otimização das regras de forma adequada.
Da documentação oficial:

  1. remove duplicate rules

  2. remove rules that are a subset of another rule

  3. combine multiple rules into a table when advanta-geous

  4. re-order the rules to improve evaluation perfor-mance

Em português:

  1. Remover regras duplicadas

  2. Remover regras que são um subconjunto de outras regras

  3. Combinar múltiplas regras em uma tabela quando vantajoso

  4. Reordenar as regras para melhorar o desempenho da avaliação

Em outras palavras, ele organiza de forma limpa até mesmo regras de pf escritas de qualquer jeito. É uma funcionalidade fantástica que organiza as regras adequadamente, permitindo que você as escreva de uma forma que seja fácil de ler para você.

Definição de Variáveis

Especificação do nome da interface WAN

wanint="vtnet0"

Definido como o endereço IPv4 atribuído e um IP de saída explícito

exsrv1 = "163.44.113.145"

Definição de variáveis e tabelas no WireGuard

wg_ifs  = "{ wg0, wg1 }"
wg_net = "10.1.0.0/24"
wg_ports="{51820}"
table <wg_clients> const { 10.1.0.2, 10.1.0.4, 10.1.0.22 }

Regras de Scrub

Neste caso, o valor MSS é especificado tanto para pacotes vindos da WAN quanto para os que saem.

Quanto ao all ramdom-id, parece ser para evitar que o próprio SO seja identificado a partir dos pacotes, mas, pelo que verifiquei com sudo tcpdump -n -v -i $interface, no caso do GNU/Linux, parece estar ativado por padrão.

04:15:18.538048 IP (tos 0x0, ttl 52, id 61686, offset 0, flags [DF], proto TCP (6), length 52)
04:15:18.538049 IP (tos 0x0, ttl 52, id 61687, offset 0, flags [DF], proto TCP (6), length 131)
04:15:18.538179 IP (tos 0x0, ttl 64, id 65430, offset 0, flags [DF], proto TCP (6), length 52)
04:15:18.538223 IP (tos 0x0, ttl 52, id 61688, offset 0, flags [DF], proto TCP (6), length 131)

Além disso, configurei in all e out all para que o scrub também seja feito em outras interfaces além da WAN.

scrub in on $wanint all random-id max-mss 1360
scrub out on $wanint all random-id max-mss 1360
scrub in all
scrub out all

Regras de NAT

Certos clientes do WireGuard saem através do NAT da interface WAN do lado do servidor WireGuard.
No entanto, neste caso, a saída é apenas por IPv4. Como o lado do servidor WireGuard não está distribuindo endereços IPv6, utilizei apenas inet from.

nat on $wanint inet from <wg_clients> to any -> ($wanint)

Regras do Fail2Ban

Regras para fazer o Fail2Ban adicionar o IP de origem do ataque a uma tabela do pf e bloqueá-lo. Coloquei o anchor "f2b/*" no final para direcionar os pacotes de IPs adicionados à tabela do pf que não corresponderam a nenhuma regra para as regras do Fail2Ban.
Coloquei-as acima porque precisam corresponder antes das regras de filtragem que usam quick.
Como a regra padrão block all está logo antes disso, todos os pacotes que não corresponderem aqui serão bloqueados (block).

/usr/local/etc/fail2ban/action.d/pf.conf:

# Opção: block
#
# A ação que você deseja que o pf execute.
# Provavelmente, você vai querer "block quick", mas ajuste conforme necessário.
# Se desejar registrar todos os bloqueios, use "blog log quick"
block = block quick

Como as regras do Fail2ban com block quick são adicionadas a partir desta configuração, eu as coloquei em uma posição superior.

Ou seja, primeiro direciono os pacotes de IPs adicionados à tabela do pf para as regras do Fail2Ban via anchor "f2b/*" e, em seguida:

anchor "f2b/*"

Como regra de filtragem, defini primeiro a regra que permite a saída de pacotes.

pass out quick keep state

Regras UDP

Regras de permissão para o servidor WireGuard.
Permite a porta 51820/udp e todas as interfaces virtuais (NICs) do WireGuard em $wg_ifs.
Talvez devesse ser mais rigoroso aqui para o caso de algum imprevisto?

pass in quick on $wg_ifs from $wg_net to any keep state
pass in quick on $wanint proto udp from any to ($wanint) port $wg_ports keep state

Neste caso, como o estado (state) já está sendo mantido, todos os pacotes que vêm pelo túnel WireGuard serão permitidos. Se esta ordem fosse invertida, a regra pass in quick on $wg_ifs from $wg_net to any keep state não funcionaria, pois ela pressupõe a existência do túnel WireGuard.

Permite 443/udp para habilitar o HTTP/3.

# Protocolo HTTP/3
pass in quick on $wanint proto udp to ($wanint) port 443 keep state

Regras TCP

Como quem já usou ferramentas como o nmap deve saber, os escaneamentos de porta em TCP são realizados de forma extremamente rápida, então apenas mudar a porta não impedirá que um atacante a encontre rapidamente. No caso de portas UDP, o escaneamento do nmap leva muito tempo, então, para tornar o sistema mais seguro, configurei o acesso ao SSH apenas através do túnel WireGuard. Basicamente, acredito que a configuração mais segura seja, na prática, encapsular o TCP em UDP.
Como implementei o Fail2ban, se houver um ataque ao SSH, o IP de origem será adicionado à tabela do pf e bloqueado. Isso funciona praticamente como um honeypot.

Para os protocolos de e-mail, ativei o synproxy para prevenir ataques de SYN Flood.
No caso do HTTP, especificar o IP de destino com ($wanint) serve para corresponder dinamicamente ao endereço IP atribuído à interface WAN. Não especifiquei explicitamente inet para permitir tanto IPv4 quanto IPv6.

Para pacotes que chegam via IPv6, como o IP de destino é especificado por ($wanint), eles corresponderão dinamicamente ao endereço IPv6 atribuído à interface WAN.

# Protocolos de E-mail
pass in quick proto tcp from any to ($wanint) port {25, 465, 993, 995} synproxy state
# HTTP
pass in quick on $wanint proto tcp to ($wanint) port {80, 443} modulate state

Enfim, ficou longo...

Related Posts